Tuesday, February 27, 2007 

DIY Sirius FM Direct Adapter Installation

I've had Sirius satellite radio for a few months, and it's been a great addition to the daily commute and my in-office work environment (via the web). I own the cheapest possible Sirius "plug and play" radio, the Sirius One (I think it was $20 from Circuit City), and it uses a built-in FM transmitter to send the signal to the radio wirelessly. However, in my truck (a 1996 Ford Ranger), I've always found it difficult to find an "unused" station that does not have some type of interference at some time of the day. The antenna is on the passenger side of the vehicle for one thing, and a preset that was clear in the morning always seems to become horrible at night (and vice-versa).

Sirius offers a product to solve this problem: the FM Direct Adapter. Essentially, you pull your radio out, unplug the antenna, plug the antenna into the adapter, then plug the adapter into the radio instead of the antenna. Then you plug the other end of the adapter into your Sirius unit. Done. When the Sirius One is on, the radio only receives what the Sirius One is sending via the adapter. When the Sirius One is off, the radio uses the external car antenna as usual. I picked one up on eBay for less than $20 shipped. Here are a few pics of how I installed it.


In this picture, I've inserted the "DIN tools" into the front of the radio, applied some pressure on the tools toward the doors, and slipped the radio partially out. You can get DIN tools or whatever is required to remove the radio in your vehicle from eBay for under $5 shipped. That's what I did.



Here, the radio is completely out, and you can see the antenna input into the radio on the right side of the radio.


Now I've unplugged the antenna, plugged the adapter into the radio's antenna input, and plugged the antenna into the adapter. At this point, you can test that the adapter works by plugging the adapter cable into your Sirius radio, turning on your Sirius radio, and tuning the car radio to the correct preset. Make note of that support bar in the crevice that the radio slides into for reference in the next picture.


In order to run the adapter cable down to the floor (where I've run the cigarette adapter that powers the Sirius One unit, I found this nice hole just to the left of the support bar in the previous picture. The adapter cable headed to the Sirius One is just to the right of the two red wires. I pulled it down through the hole.


Near the gas pedal underneath the dash, here comes the cable out of the other end of the hole. The adapter cable is the one that runs over the top of the coiled cable at the bottom of the picture (a CB handset cable).


Now that everything is connected and working, I just need to mount the adapter and slide the radio back in. Luckily there is plenty of clearance on top of the radio since the factory radio's face is twice the size of the actual internal unit. I used a piece of the double-sided adhesive strip originally provided with the Sirius One to attach the antenna to the roof (since I never used it for its intended purpose) - the red strip in this picture.


Now I'm ready to slide the radio back in and remove the DIN tools. Total installation time: 5 minutes. Total cost: $25 shipped to my door. No static: priceless.

Monday, June 26, 2006 

Diagnosing "Serious Errors" with Windows Memory Diagnostic

We use fast user switching in Windows XP at my house so I don't have to close all of my running apps just to let another family member check their email. One of the greatest annoyances which up until recently had been very rare was to sit down to click my username and not see any indication of how many programs I had running listed underneath it - a sure sign that the computer had rebooted on its own.

Once you log on after an "unanticipated" reboot, XP tells you that the system has recovered from a "serious error" - i.e. a BSOD happened and Windows rebooted as configured. You can happily send the error report off to Microsoft Windows Crash Analyis and they will give you some info on what might be wrong, but in some cases, the cause of these reboots might keep changing. In my case, I would awaken to find several reboots had happened, and the diagnosed causes would range from an IE plugin (I don't even use IE) to an error in an unknown driver. I took this as a sign that there was actually some kind of hardware problem - most likely a memory issue.

I burned some self-booting memory testing ISO images to a few CD's (namely OCZ's modified memtest86 and Microsoft's Windows Memory Diagnostic). I started with WMD, running the extended tests, and saw hundreds of errors happen almost immediately on the first run of one of the tests. So now that I had found these memory errors, what was the next step? Microsoft suggests in their documentation that their tests will try to determine which memory bank caused the errors if possible, and that you should then flip your DIMM's around to see if you get the errors in the same bank. This would indicate a bad bank. If the errors move to the other bank, that would indicate a bad memory module. So where were my errors happenning? WMD was unable to determine that, so I was left on my own.

Luckily, WMD does show you the hexidecimal memory addresses as the errors occur. Since I knew I had two 1GB modules installed, my adressable memory space was something like 2 * 1024(mb in a gb) * 1024(kb in a mb) * 1024(bytes in a kb) = 2147483648 decimal bytes or 0x80000000 hex bytes. Since I was using two DIMM modules, slot 0 would map to adressable memory up to 0x40000000 and anything above would be in slot 1. Armed with this information, I could tell that all of the errors being reported by WMD could be attributed to slot 1 (not sure why WMD itself could not make this determination).

The next step was to flip the modules and see if the errors jumped to slot 0. I tried this and the errors stayed in slot 1. I then moved one of the modules to slot 2 (my mobo has three slots, slot 0 is DIMMA and slots 1 and 2 are designated as DIMMB1 and DIMMB2). Slots 1 and 2 were grouped together, apart from slot 0, so I was not surprised that slot 2 also showed many errors.

Looks like it's time for a new motherboard. For the time being, I am left with two perfectly good 1GB memory modules but only one reliable DIMM slot. Newegg only sells a handful of AMD Socket A mobos these days (the defective board in question is a Biostar M7NCD Pro) since Athlon XP processors are getting up there in age, but I found a cheap replacement in a used Albatron KX400-8X for $20 from a seller on Anandtech's Sale/Trade Forum which should arrive later this week.

Friday, February 24, 2006 

SQL Replication and Renamed Imaged Servers

I've run into various issues when rolling out Microsoft SQL Server 2000 to multiple locations using disk imaging. The roll-out process usually involves creating a disk image with SQL Server already installed, applying that image on multiple server machines at a production center, then distributing and renaming each server once it gets to its final destination (no, I'm not involved with the licensing issues). There are multiple locations, each with one server with a handful of clients.

The problems arise when we need to have SQL Server replication (a pushed one-way merge publication) deployed at each location. I now know the best strategy is to install replication via some scripts after the server and clients are all renamed and deployed. However, I recently ran into a case where replication had been installed before the master image was created, then the image was deployed to a server, then the machine name of the server was changed. Here are the issues and how I dealt with them.

Because all of the names of the machines had changed, I needed to uninstall and re-install the replication setup. The common strategy is to script out the current publication, drop and disable publishing, then reinstall replication using the generated scripts. However, when the machine name was changed and the machine was rebooted, the value of the server property "ServerName" was updated, but the value of @@servername was not changed. If you run the queries "select @@servername" and "select ServerProperty('ServerName')," you will see that they return different values. If you do a select from sysservers, you will also see that server id 0 still has the value of the previous machine name. To correct this, run sp_dropserver with the old machine name, then run sp_addserver with the new machine name (see this MS article for more info).

Before adjusting those values, I did a few other things. First, I right-clicked on my publication and scripted it out. Then I used Enterprise Manager to completely disable publishing at the server (Tools->Replication->Disable Publishing). You may have to register the new server name in EM if you had explicitly referred to the previous server name. When you then disable publishing, you will receive an error about MSX server jobs. This is due to the fact that sysjobs in the msdb database contains jobs that have an originating server value referencing the previous server name. At this point, I adjusted the server name using the dropserver/addserver steps above. Then I restarted the MSSQLSERVER service.
Now that the servername values were in sync, I deleted all rows in sysjobs, sysjobsteps, and sysjobschedules. Then I tried to disable publishing again. This time it completed with no errors.

At this point, I had to clean up the subscriber machines who still thought they were being pushed a subscription from the old server name. I ran sp_removedbreplication on all of the subscribers. Next, I edited the replication scripts I had generated to make sure the correct publisher and subscriber names were in them. Finally, I was then able to execute the scripts to recreate the publication. No errors. YMMV.

Monday, February 06, 2006 

IE7: Flickr tag search in the Search bar

In the latest IE7 beta, it's simple to add a search provider to the new integrated Search bar using OpenSearch description documents and A9's OpenSearch specification. You just link an onClick event to window.external.AddSearchProvider('URL') where URL is the link to the OpenSearch description document (see this Microsoft page for more info). I think the spec and the IE implementation actually expect some kind of formatted XML back.

As far as I know, Flickr doesn't support the OpenSearch spec, but I but I put together a description document that just sticks your search term into the Flickr search results URL. You can add it to your IE7 search bar by clicking here. It's a simple one-term search (if you put two terms in, Flickr just concats them). The XML for the OpenSearch description doc looks like this:

<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
  <ShortName>Flickr</ShortName>
  <Description>Search for pictures on Flickr</Description>
  <Url type="text/html"
  template="http://www.flickr.com/photos/search/tags:{searchTerms}/tagmode:all/"/>
</OpenSearchDescription>

Tuesday, January 10, 2006 

Embedding a Google Map

Took a shot at the quick tutorial on how to embed a Google map into a web page in 10 minutes by Joshua Siler. Simple enough that it can be done just using the sample code provided by Google when you get your map key (though Joshua does provide a tweak and a couple of additional tips). Don't know why I never bothered to check out the API. Perhaps I just never needed an embedded map. Certainly the one I've provided below is completely pointless.

Sunday, January 01, 2006 

New Application/User Settings Interface in VS2005

While thinking about how to keep track of various configurable application settings in the X10 application, I came across an article in MSDN Magazine from October about a match-making game. The author referenced a previous article where he talked about user and application settings and how to store and retrieve them. Following his links, I came across an article from Emad Ibrahim that explained the easiest way to keep track of settings in Visual Studio 2005 - it's built in!

First, here's my current list of simple properties:


So in the project properties, I have defined these settings under the appropriately-named Settings tab:


As you can see, you just specify the name of the property, the type, whether it's user- or application-level, and the default value. Simple. Then, you can even tie controls on the form to specific settings! Under the properties for a control, such as a checkbox, you see this:


You just click on the box next to Property Binding to bring up a box allowing you to bind control properties to application settings:


I want to bind the checked property of this checkbox control to an application setting. Clicking the dropdown on the checked property shows the various application settings I've defined:


The checked property is automatically set during startup based on the application setting, and the settings file is automatically updated when the checked property is changed by the user. It's like magic.

Thursday, December 15, 2005 

Using HttpListener Asynchronously

My previous entry about creating a mini-web server application to take X10 device requests used HttpListener to wait for and respond to HTTP requests synchronously. However, as I develop a user interface for my X10Listener service, I'll need to be able to respond to those requests in the background while the user configures other areas of the service. So I've switched to an asynchronous use of HttpListener. First, in my load subroutine, I start the listener as before, but use BeginGetContext instead of GetContext.
Dim listener As New System.Net.HttpListener

listener.Prefixes.Add("http://*:33333/")

Try
    listener.Start()
Catch ex As Exception
    MsgBox(ex.Message)
    Exit Sub
End Try

listener.BeginGetContext(New System.AsyncCallback(AddressOf Me.ProcessRequest), _
listener)
Now I can go on and set up the UI and handle UI events. Note that I pass a reference to the address of another routine called ProcessRequest along with a reference to the listener object. Here is the routine to process the request, which is automatically called when an HTTP request comes in.
Private Sub ProcessRequest(ByVal result As System.IAsyncResult)

    Dim listener As Net.HttpListener
    Dim context As Net.HttpListenerContext

    listener = result.AsyncState
    context = listener.EndGetContext(result)

    ' use context.Request to explore the incoming HTTP request

    listener.BeginGetContext(New System.AsyncCallback(AddressOf _
Me.ProcessRequest), listener) End Sub
Note that I never have to instantiate a new HttpListener except during the initial load. I can pass the single listener object around via the asynchronous handler. At the end of the ProcessRequest routine, I simply set up to handle the next incoming request by calling BeginGetContext again. Next, I'll be building the UI and sending a more complex response that will include a configurable, simple HTML menu of X10 commands.

Tuesday, December 06, 2005 

Using HttpListener For X10 Web Interface

When I created my VB.NET library for the X10 CM17A, I really didn't know what I was going to use it for. It was a learning exercise. I intended my next step to be creating an ASP.NET app that I could access from the network to send X10 commands "remotely" from another machine on the wireless connection in the house. When I started that, I quickly realized that the latest version of IIS that supported the ASP.NET 2.0 applications I would be creating in VS 2005 was not available for Windows XP. Not interested in upgrading to a new operating system, I put the idea aside, noting that I could still do it some day by implementing some kind of client/server messasing protocol in a couple of separate applications.

Well today I stumbled upon the new HttpListener class while browsing through an article on streams in the latest issue of MSDN. HttpListener seemed to be just what I needed - a way to listen to and respond to HTTP requests easily. I could run a small app (kind of a mini webserver) that would listen for incoming requests on a certain port, decode the query string from the URL, and instantiate and manipulate a CM17A device via my existing module.

Keep in mind that this implementation is synchronous, but the HttpListener does allow for asynchronous operation using BeginGetContext. First, create your listener and point him at the right port.
Dim listener As New System.Net.HttpListener
listener.Prefixes.Add("http://*:33333/")
listener.Start()
Then, sit in a loop waiting for incoming requests. Once I receive a request in the proper format, I split up the string to get the house, device, and commands. Then I simply send these to the CM17A Firecracker unit using my class. Note there is not much error checking at all. I'm assuming the incoming URL has all three parameters, etc.
Do
    Dim context As Net.HttpListenerContext = listener.GetContext
    If context.Request.Url.LocalPath = "/X10" Then
        Dim paramslist As String = context.Request.Url.Query.Split("?")(1)
        Dim params() As String = paramslist.Split("&")

        Dim msg As String = DateTime.Now.ToString() & ": "
        Dim nDevice As Integer
        Dim strHouse As String = ""
        Dim strCommand As String = ""
        Dim strVal As String

        For Each stringpair As String In params
            strVal = stringpair.Split("=")(1)
            Select Case stringpair.Split("=")(0).ToUpper
                Case "DEVICE"
                    nDevice = strVal.ToUpper
                Case "HOUSE"
                    strHouse = strVal
                Case "COMMAND"
                    strCommand = strVal.ToUpper
                Case Else
            End Select
        Next

        Dim FC As New X10.CM17A(1)
        If FC.SendCommand(strHouse, nDevice, strCommand) Then
            msg += "Command sent."
        Else
            msg += "Error sending command."
        End If

        Dim b() As Byte = System.Text.Encoding.ASCII.GetBytes(msg)
        context.Response.OutputStream.Write(b, 0, b.Length)
        context.Response.Close()
   End If
Loop While True
Now, sending an OFF command to device 1 in house A is as simple as opening up a web browser and pointing it to "http://192.168.1.2:33333/X10?device=1&house=A&command=off" (or whatever IP address your listener is running on). You get back a simple success or failure message. It's amazing to use a little Windows Mobile 5.0 device to control X10 devices all over the house just by browsing an internal web "server." It would be simple to extend this idea by building a little client app that sends these HTTP requests based on button clicks.

In fact, you could just write up a little HTML page that had links to URL's that would turn your various devices on or off. This HTML could live on the listener and be returned as the response to /X10?init or something similar. There are lots of UI possibilities here.

Friday, December 02, 2005 

Might We Suggest the Enemy?

There was a deal posted over at SlickDeals.net for a collegiate hoodie + t-shirt combo for about $20, so I clicked-through to check it out. There were lots of schools available, but I found the "might we also suggest" items to be questionable. The example to the left (click for larger version) shows a suggestion of UNC slippers for someone purchasing an NC State hoodie. For those of you living outside of North Carolina that might only know about the UNC-Duke rivalry, all three of these schools hate each other. Suggesting UNC gear to a Wolfpack fan is about the most offensive suggestion you could possibly offer.

Thursday, November 24, 2005 

Rebooting the Cable Modem Programmatically Using X10

I mentioned that I used my .NET X10 library to reboot my cable modem every week or so when it appears to have locked up, and now here's the simple code. I created a new VB.NET app, put this code in the Load method of a basic form with a single label object, and created a shortcut to it on the desktop for all users. Now everyone can reboot the cable modem directly from the workstation without going down to the interface room or knowing how to pull the correct plug. Plus, this code knows how long to wait before the connection should be back up, and even does a simple web request to test the connection after the reboot.
        Dim FC As New X10.CM17A(1)

        Label1.Text = "Sending Off Command ..."
        Me.Show()
        Me.Refresh()

        FC.SendCommand(X10.CM17A.HouseA, 1, X10.CM17A.CommandOff)
        Label1.Text = "Sent Off Command, Waiting 3 Seconds ..."
        Me.Refresh()
        System.Threading.Thread.Sleep(3000)
        Label1.Text = "Sending On Command ..."
        Me.Refresh()
        FC.SendCommand(X10.CM17A.HouseA, 1, X10.CM17A.CommandOn)
        Label1.Text = "Sent On Command, Waiting 30 Seconds ..."
        Me.Refresh()
        System.Threading.Thread.Sleep(30000)
        Label1.Text = "Testing Connection ..."
        Me.Refresh()

        Dim objHTTP As New System.Net.WebClient
        Dim objIn As IO.Stream

        Try
            objIn = objHTTP.OpenRead("http://www.microsoft.com")
            Label1.Text = "Success! Exiting ..."
        Catch ex As Exception
            MsgBox("Error Testing Connection! Click OK and Try Again.")
        End Try

        Me.Refresh()
        System.Threading.Thread.Sleep(3000)
        Me.Close()

    Add to Google

    This page is powered by Blogger.



Photostream

    www.flickr.com
    justinhenry's photos More of justinhenry's photos