<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/atom.xsl" media="screen"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US"><title type="html">Michael Hopcroft</title><subtitle type="html" /><id>http://blogs.msdn.com/mhop/atom.xml</id><link rel="alternate" type="text/html" href="http://blogs.msdn.com/mhop/default.aspx" /><link rel="self" type="application/atom+xml" href="http://blogs.msdn.com/mhop/atom.xml" /><generator uri="http://communityserver.org" version="2.1.61025.2">Community Server</generator><updated>2006-11-27T18:01:50Z</updated><entry><title>Home Automation Part 3 - Using Regular Expressions to Parse Sensor Data</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/mhop/archive/2007/01/07/home-automation-part-3-using-regular-expressions-to-parse-sensor-data.aspx" /><link rel="enclosure" type="application/x-zip-compressed" length="31185" href="http://blogs.msdn.com/mhop/attachment/1433109.ashx" /><id>http://blogs.msdn.com/mhop/archive/2007/01/07/home-automation-part-3-using-regular-expressions-to-parse-sensor-data.aspx</id><published>2007-01-08T10:02:00Z</published><updated>2007-01-08T10:02:00Z</updated><content type="html">&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;In the &lt;A title="Home Automation Part 2 – Making Basic Measurements" href="http://blogs.msdn.com/mhop/archive/2006/12/06/home-automation-part-2-making-basic-measurements.aspx" mce_href="http://blogs.msdn.com/mhop/archive/2006/12/06/home-automation-part-2-making-basic-measurements.aspx"&gt;previous installment&lt;/A&gt; I managed to set up basic communications with the sensor network and try out some &lt;A href="http://www.ibuttonlink.com/Link_Details.htm" mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;Link45&lt;/A&gt;&amp;nbsp;commands using my &lt;A title="Home Automation Part 2 – Making Basic Measurements" href="http://blogs.msdn.com/mhop/archive/2006/12/06/home-automation-part-2-making-basic-measurements.aspx" mce_href="http://blogs.msdn.com/mhop/archive/2006/12/06/home-automation-part-2-making-basic-measurements.aspx"&gt;Super-Simple Serial Console&lt;/A&gt;. In order to actually do anything with the sensor data, I would need a function that could crack the&amp;nbsp;&lt;A href="http://www.ibuttonlink.com/Link_Details.htm" mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;Link45&lt;/A&gt; responses into sensor IDs and temperature and humidity values. &lt;/P&gt;
&lt;P&gt;This seemed like a job for &lt;A title=System.Text.RegularExpressions href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.aspx"&gt;System.Text.RegularExpressions&lt;/A&gt;. My only problem was that no matter how carefully I constructed my regular expression, there would always be a bug. The edit-compile-test cycle was getting tedious so I decided to take a detour and code up a simple interactive regular expression&amp;nbsp;tester using &lt;A title="Windows Forms Applications" href="http://msdn2.microsoft.com/en-us/library/ms644558.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/ms644558.aspx"&gt;WinForms&lt;/A&gt;. This post describes the regular expression tester and then explains how I used it to develop the regular expression necessary to extract the sensor ID, temperature, and humidity values from the&amp;nbsp;&lt;A href="http://www.ibuttonlink.com/Link_Details.htm" mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;Link45&lt;/A&gt; response string.&lt;/P&gt;
&lt;H3&gt;&lt;STRONG&gt;An Interactive Regular Expression Tester&lt;/STRONG&gt;&lt;/H3&gt;
&lt;P&gt;My goal was to create a simple application that could show the results of a &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt; in real time as I was editing the expression. I also wanted to prototype the &lt;A title="Visual C#" href="http://msdn2.microsoft.com/en-us/library/kx37x362(VS.80).aspx" mce_href="http://msdn2.microsoft.com/en-us/library/kx37x362(VS.80).aspx"&gt;C#&lt;/A&gt; code that I would need&amp;nbsp;to navigate the &lt;A title="System.Text.RegularExpressions.Regex class" href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.regex.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.regex.aspx"&gt;Regex's&lt;/A&gt;&amp;nbsp;resulting hierarchy of &lt;A title=System.Text.RegularExpressions.Match href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.match.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.match.aspx"&gt;Matches&lt;/A&gt;, &lt;A title=System.Text.RegularExpressions.Group href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx"&gt;Groups&lt;/A&gt;, and &lt;A title=System.Text.RegularExpressions.Capture href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.capture.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.capture.aspx"&gt;Captures&lt;/A&gt; to pull out the values matched by my &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt;. If you develop a lot of &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expressions&lt;/A&gt; you might&amp;nbsp;consider using a more sophisticated and full featured application such as &lt;A title="Chris Sells" href="http://www.sellsbrothers.com/" mce_href="http://www.sellsbrothers.com/"&gt;Chris Sells'&lt;/A&gt; excellent &lt;A title=RegexDesigner.NET href="http://www.sellsbrothers.com/tools/#regexd" mce_href="http://www.sellsbrothers.com/tools/#regexd"&gt;RegexDesigner.NET&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;My simple application provides two text boxes, one for entering sample data and one for the regular expression (see Figure 1). Edits to either the sample data or the &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt;&amp;nbsp;trigger an application of the &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt;, the results of which are shown in the bottom pane in real time. &lt;/P&gt;
&lt;P&gt;&lt;A href="http://hopcroft.members.winisp.net/images/HomeAutomationPart3UsingRegularExpressio_BA4B/image01.png" atomicselection="true" mce_href="http://hopcroft.members.winisp.net/images/HomeAutomationPart3UsingRegularExpressio_BA4B/image01.png"&gt;&lt;IMG height=449 src="http://hopcroft.members.winisp.net/images/HomeAutomationPart3UsingRegularExpressio_BA4B/image0_thumb1.png" width=523 mce_src="http://hopcroft.members.winisp.net/images/HomeAutomationPart3UsingRegularExpressio_BA4B/image0_thumb1.png"&gt;&lt;/A&gt; &lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Figure 1. A very simple interactive regular expression evaluator.&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;The application is built in &lt;A title="Windows Forms Applications" href="http://msdn2.microsoft.com/en-us/library/ms644558.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/ms644558.aspx"&gt;WinForms&lt;/A&gt;&amp;nbsp;using &lt;A title="Visual C#" href="http://msdn2.microsoft.com/en-us/library/kx37x362(VS.80).aspx" mce_href="http://msdn2.microsoft.com/en-us/library/kx37x362(VS.80).aspx"&gt;C#&lt;/A&gt;. You can find&amp;nbsp;the complete source code and executable&amp;nbsp;in an attachment at&amp;nbsp;the end of this post. Here's a rough outline of the steps you would need to take to write&amp;nbsp;a similar&amp;nbsp;application:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;From &lt;A href="http://msdn.microsoft.com/vstudio/vshome.aspx" mce_href="http://msdn.microsoft.com/vstudio/vshome.aspx"&gt;Visual Studio&lt;/A&gt;, use File -&amp;gt; New Project to create a Visual C# Windows Application. 
&lt;LI&gt;In Form1.cs [design] 
&lt;OL&gt;
&lt;LI&gt;Set the window title to "Interactive Regular Expression Evaluator" 
&lt;LI&gt;Add a &lt;A title=System.Windows.Forms.Label href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemwindowsformslabelclasstopic.asp" mce_href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemwindowsformslabelclasstopic.asp"&gt;Label&lt;/A&gt; control&amp;nbsp;with Text = "Sample Data" 
&lt;LI&gt;Add a &lt;A title=System.Windows.Forms.TextBox href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemWindowsFormsTextBoxClassTopic.asp" mce_href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemWindowsFormsTextBoxClassTopic.asp"&gt;TextBox&lt;/A&gt; control&amp;nbsp;named &lt;EM&gt;&lt;STRONG&gt;SampleData&lt;/STRONG&gt;&lt;/EM&gt; to hold the sample input string. 
&lt;LI&gt;Add another Label with Text = "Regular Expression" 
&lt;LI&gt;Add a &lt;A title=System.Windows.Forms.TextBox href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemWindowsFormsTextBoxClassTopic.asp" mce_href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemWindowsFormsTextBoxClassTopic.asp"&gt;TextBox&lt;/A&gt;&amp;nbsp;named &lt;STRONG&gt;&lt;EM&gt;RegularExpression&lt;/EM&gt;&lt;/STRONG&gt; to hold the text of the &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt;. 
&lt;LI&gt;Add another &lt;A title=System.Windows.Forms.Label href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemwindowsformslabelclasstopic.asp" mce_href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemwindowsformslabelclasstopic.asp"&gt;Label&lt;/A&gt;&amp;nbsp;with Text = "Results" 
&lt;LI&gt;Add a read-only &lt;A title=System.Windows.Forms.TextBox href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemWindowsFormsTextBoxClassTopic.asp" mce_href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemWindowsFormsTextBoxClassTopic.asp"&gt;TextBox&lt;/A&gt;&amp;nbsp;named &lt;STRONG&gt;&lt;EM&gt;Results&lt;/EM&gt;&lt;/STRONG&gt; to display the results of the expression application. 
&lt;LI&gt;Set all &lt;A title=System.Windows.Forms.TextBox href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemWindowsFormsTextBoxClassTopic.asp" mce_href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemWindowsFormsTextBoxClassTopic.asp"&gt;TextBox&lt;/A&gt;&amp;nbsp;anchors to include &lt;STRONG&gt;&lt;A title="System.Windows.Forms.AnchorStyles Enumeration" href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemwindowsformsanchorstylesclasstopic.asp" mce_href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemwindowsformsanchorstylesclasstopic.asp"&gt;Top&lt;/A&gt;&lt;/STRONG&gt;, &lt;STRONG&gt;&lt;STRONG&gt;&lt;A title="System.Windows.Forms.AnchorStyles Enumeration" href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemwindowsformsanchorstylesclasstopic.asp" mce_href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemwindowsformsanchorstylesclasstopic.asp"&gt;Left&lt;/A&gt;&lt;/STRONG&gt;&lt;/STRONG&gt;, and &lt;STRONG&gt;&lt;STRONG&gt;&lt;A title="System.Windows.Forms.AnchorStyles Enumeration" href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemwindowsformsanchorstylesclasstopic.asp" mce_href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemwindowsformsanchorstylesclasstopic.asp"&gt;Right&lt;/A&gt;&lt;/STRONG&gt;&lt;/STRONG&gt;. Set the anchor for the &lt;STRONG&gt;&lt;EM&gt;Results&lt;/EM&gt;&lt;/STRONG&gt; &lt;A title=System.Windows.Forms.TextBox href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemWindowsFormsTextBoxClassTopic.asp" mce_href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemWindowsFormsTextBoxClassTopic.asp"&gt;TextBox&lt;/A&gt;&amp;nbsp;to include &lt;STRONG&gt;&lt;STRONG&gt;&lt;A title="System.Windows.Forms.AnchorStyles Enumeration" href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemwindowsformsanchorstylesclasstopic.asp" mce_href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemwindowsformsanchorstylesclasstopic.asp"&gt;Bottom&lt;/A&gt;&lt;/STRONG&gt;&lt;/STRONG&gt;.&lt;/LI&gt;&lt;/OL&gt;
&lt;LI&gt;Wire up the &lt;A title=System.Windows.Forms.Control.TextChangedEvent href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemwindowsformscontrolclasstextchangedtopic.asp" mce_href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemwindowsformscontrolclasstextchangedtopic.asp"&gt;TextChanged&lt;/A&gt; event handlers for &lt;STRONG&gt;&lt;EM&gt;SampleData&lt;/EM&gt;&lt;/STRONG&gt; and &lt;STRONG&gt;&lt;EM&gt;RegularExpression&lt;/EM&gt;&lt;/STRONG&gt; to invoke&amp;nbsp;a new&amp;nbsp;method called &lt;STRONG&gt;&lt;EM&gt;ApplyRegularExpression()&lt;/EM&gt;&lt;/STRONG&gt;. 
&lt;LI&gt;Modify the constructor of &lt;STRONG&gt;&lt;EM&gt;Form1&lt;/EM&gt;&lt;/STRONG&gt; to call &lt;STRONG&gt;&lt;EM&gt;ApplyRegularExpression()&lt;/EM&gt;&lt;/STRONG&gt; method on application startup. 
&lt;LI&gt;Write the &lt;STRONG&gt;&lt;EM&gt;ApplyRegularExpression()&lt;/EM&gt;&lt;/STRONG&gt; method.&lt;/LI&gt;&lt;/OL&gt;
&lt;P&gt;The heart of the application is the &lt;STRONG&gt;&lt;EM&gt;&lt;STRONG&gt;&lt;EM&gt;ApplyRegularExpression&lt;/EM&gt;&lt;/STRONG&gt;()&lt;/EM&gt;&lt;/STRONG&gt; method which is shown in Listing 1. Whenever the user changes either the sample data or the &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt;, the &lt;EM&gt;&lt;STRONG&gt;&lt;STRONG&gt;&lt;EM&gt;ApplyRegularExpression&lt;/EM&gt;&lt;/STRONG&gt;()&lt;/STRONG&gt;&lt;/EM&gt; method is invoked to update the results panel. Let's walk through the code. &lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Listing 1. The &lt;STRONG&gt;&lt;EM&gt;ApplyRegularExpression&lt;/EM&gt;&lt;/STRONG&gt;() method updates the results panel whenever the sample data or regular expression changes.&lt;/STRONG&gt;&lt;/P&gt;
&lt;DIV class=csharpcode&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   1:  &lt;/SPAN&gt;&lt;SPAN class=kwrd&gt;void&lt;/SPAN&gt; ApplyRegularExpression()&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   2:  &lt;/SPAN&gt;{&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   3:  &lt;/SPAN&gt;    &lt;SPAN class=kwrd&gt;try&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   4:  &lt;/SPAN&gt;    {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   5:  &lt;/SPAN&gt;        Results.Text = &lt;SPAN class=str&gt;""&lt;/SPAN&gt;;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   6:  &lt;/SPAN&gt;        &lt;SPAN class=kwrd&gt;string&lt;/SPAN&gt; results = &lt;SPAN class=str&gt;""&lt;/SPAN&gt;;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   7:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   8:  &lt;/SPAN&gt;        Regex r = &lt;SPAN class=kwrd&gt;new&lt;/SPAN&gt; Regex(RegularExpression.Text);&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   9:  &lt;/SPAN&gt;        MatchCollection matches = r.Matches(SampleData.Text);&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  10:  &lt;/SPAN&gt;        &lt;SPAN class=kwrd&gt;for&lt;/SPAN&gt; (&lt;SPAN class=kwrd&gt;int&lt;/SPAN&gt; i=0; i &amp;lt; matches.Count; ++i)&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  11:  &lt;/SPAN&gt;        {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  12:  &lt;/SPAN&gt;            Match m = matches[i];&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  13:  &lt;/SPAN&gt;            results += &lt;SPAN class=str&gt;"Matches["&lt;/SPAN&gt; + i.ToString() + &lt;SPAN class=str&gt;"]"&lt;/SPAN&gt;;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  14:  &lt;/SPAN&gt;            results += &lt;SPAN class=str&gt;"\n\r\n\r"&lt;/SPAN&gt;;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  15:  &lt;/SPAN&gt;            &lt;SPAN class=kwrd&gt;for&lt;/SPAN&gt; (&lt;SPAN class=kwrd&gt;int&lt;/SPAN&gt; j = 0; j &amp;lt; m.Groups.Count; ++j)&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  16:  &lt;/SPAN&gt;            {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  17:  &lt;/SPAN&gt;                Group g = m.Groups[j];&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  18:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  19:  &lt;/SPAN&gt;                results += &lt;SPAN class=str&gt;"    .Groups["&lt;/SPAN&gt;;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  20:  &lt;/SPAN&gt;                &lt;SPAN class=kwrd&gt;string&lt;/SPAN&gt; name = r.GroupNameFromNumber(j);&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  21:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  22:  &lt;/SPAN&gt;                &lt;SPAN class=kwrd&gt;if&lt;/SPAN&gt; (name == j.ToString())&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  23:  &lt;/SPAN&gt;                    results += name;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  24:  &lt;/SPAN&gt;                &lt;SPAN class=kwrd&gt;else&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  25:  &lt;/SPAN&gt;                    results += &lt;SPAN class=str&gt;"\""&lt;/SPAN&gt; + name + &lt;SPAN class=str&gt;"\""&lt;/SPAN&gt;;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  26:  &lt;/SPAN&gt;                results += &lt;SPAN class=str&gt;"]\n\r\n\r"&lt;/SPAN&gt;;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  27:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  28:  &lt;/SPAN&gt;                &lt;SPAN class=kwrd&gt;for&lt;/SPAN&gt; (&lt;SPAN class=kwrd&gt;int&lt;/SPAN&gt; k=0; k &amp;lt; g.Captures.Count; ++k) &lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  29:  &lt;/SPAN&gt;                {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  30:  &lt;/SPAN&gt;                    Capture c = g.Captures[k];&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  31:  &lt;/SPAN&gt;                    results += &lt;SPAN class=str&gt;"        .Captures["&lt;/SPAN&gt;+k+&lt;SPAN class=str&gt;"]=\""&lt;/SPAN&gt; + c.Value + &lt;SPAN class=str&gt;"\"\n\r\n\r"&lt;/SPAN&gt;;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  32:  &lt;/SPAN&gt;                }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  33:  &lt;/SPAN&gt;                &lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  34:  &lt;/SPAN&gt;            }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  35:  &lt;/SPAN&gt;            &lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  36:  &lt;/SPAN&gt;        }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  37:  &lt;/SPAN&gt;        Results.Text = results;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  38:  &lt;/SPAN&gt;        RegularExpression.ForeColor = Color.Black;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  39:  &lt;/SPAN&gt;    }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  40:  &lt;/SPAN&gt;    &lt;SPAN class=kwrd&gt;catch&lt;/SPAN&gt; (System.Exception e)&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  41:  &lt;/SPAN&gt;    {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  42:  &lt;/SPAN&gt;        Results.Text = e.Message;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  43:  &lt;/SPAN&gt;        RegularExpression.ForeColor = Color.Red;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  44:  &lt;/SPAN&gt;    }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  45:  &lt;/SPAN&gt;}&lt;/PRE&gt;&lt;/DIV&gt;
&lt;STYLE type=text/css&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/STYLE&gt;

&lt;P&gt;&lt;STRONG&gt;&lt;/STRONG&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;The try-catch block (lines 3-44) is used to catch errors that occur during the application of the &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt;. If an error is detected, the error message is shown in the results pane (lines 40-44) and the &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt;&amp;nbsp;text is colored red.&lt;/P&gt;
&lt;P&gt;Normally the code enters the try block at line 3. The string called &lt;STRONG&gt;&lt;EM&gt;results&lt;/EM&gt;&lt;/STRONG&gt; (line 6) is used to accumulate the text that will go into the results panel. Accumulating the results into a string seems to be more performant than accumulating them directly into &lt;STRONG&gt;&lt;EM&gt;Results.Text&lt;/EM&gt;&lt;/STRONG&gt;, probably because each change to &lt;STRONG&gt;&lt;EM&gt;Results.Text&lt;/EM&gt;&lt;/STRONG&gt; forces the &lt;A title=System.Windows.Forms.TextBox href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemWindowsFormsTextBoxClassTopic.asp" mce_href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemWindowsFormsTextBoxClassTopic.asp"&gt;TextBox&lt;/A&gt;&amp;nbsp;to redo its text layout computations. In this code, all the changes are accumulated into &lt;STRONG&gt;&lt;EM&gt;results&lt;/EM&gt;&lt;/STRONG&gt; and only at the end is the string copied into &lt;EM&gt;&lt;STRONG&gt;Results.Text&lt;/STRONG&gt;&lt;/EM&gt;.&lt;/P&gt;
&lt;P&gt;The &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt; is applied to the sample text in line 8 and a &lt;A title=System.Text.RegularExpressions.MatchCollection href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.matchcollection.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.matchcollection.aspx"&gt;MatchCollection&lt;/A&gt; returns the set of &lt;A title=System.Text.RegularExpressions.Match href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.match.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.match.aspx"&gt;Matches&lt;/A&gt; in line 9. The nested for-loops in lines 10-36, 15-34, 28-32&amp;nbsp;walk through the &lt;A title=System.Text.RegularExpressions.Match href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.match.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.match.aspx"&gt;Match&lt;/A&gt;/&lt;A title=System.Text.RegularExpressions.Group href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx"&gt;Group&lt;/A&gt;/&lt;A title=System.Text.RegularExpressions.Capture href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.capture.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.capture.aspx"&gt;Capture&lt;/A&gt; hierarchy, converting it into human readable text which is accumulated in the &lt;STRONG&gt;&lt;EM&gt;results&lt;/EM&gt;&lt;/STRONG&gt; string.&amp;nbsp;The &lt;STRONG&gt;&lt;EM&gt;results&lt;/EM&gt;&lt;/STRONG&gt; string is used to set the Text property of the results region on line 37. The only part that is not&amp;nbsp;completely straight forward is the&amp;nbsp;if statement&amp;nbsp;on lines 22-25 which checks to see if the &lt;A title=System.Text.RegularExpressions.Group href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx"&gt;Group&lt;/A&gt;&amp;nbsp;is named&amp;nbsp;in order to determine whether to output a numerical or string indexer.&lt;/P&gt;
&lt;P&gt;To understand how to actually extract matched values from an input string, it helps to understand the hierarchical &lt;A title=System.Text.RegularExpressions.Match href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.match.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.match.aspx"&gt;Match&lt;/A&gt;/&lt;A title=System.Text.RegularExpressions.Group href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx"&gt;Group&lt;/A&gt;/&lt;A title=System.Text.RegularExpressions.Capture href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.capture.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.capture.aspx"&gt;Capture&lt;/A&gt; data structure&amp;nbsp;created by the &lt;A title="System.Text.RegularExpressions.Regex class" href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.regex.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.regex.aspx"&gt;Regex&lt;/A&gt;&amp;nbsp;in line 9.&amp;nbsp;The root of the hierarchy is&amp;nbsp;a &lt;A title=System.Text.RegularExpressions.MatchCollection href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.matchcollection.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.matchcollection.aspx"&gt;MatchCollection&lt;/A&gt;&amp;nbsp;with one &lt;A title=System.Text.RegularExpressions.Match href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.match.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.match.aspx"&gt;Match&lt;/A&gt; for each portion of the input string that matches the entire &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt; string (line 9). Each &lt;A title=System.Text.RegularExpressions.Match href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.match.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.match.aspx"&gt;Match&lt;/A&gt; contains one or more &lt;A title=System.Text.RegularExpressions.Group href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx"&gt;Groups&lt;/A&gt; (line 15). The first &lt;A title=System.Text.RegularExpressions.Group href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx"&gt;Group&lt;/A&gt;&amp;nbsp;always corresponds to the &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt; in its entirety. Subsequent &lt;A title=System.Text.RegularExpressions.Group href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx"&gt;Groups&lt;/A&gt; correspond to &lt;A title="Grouping Constructs" href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx"&gt;grouping constructs&lt;/A&gt; within the &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt;. A &lt;A title="Grouping Constructs" href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx"&gt;grouping construct&lt;/A&gt; is just a parenthesized sub-expression of a &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt;. &lt;A title="Grouping Constructs" href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx"&gt;Grouping constructs&lt;/A&gt; are sometimes used to capture the input text matching&amp;nbsp;specific portions of a larger match. As an example, one could use a &lt;A title="Grouping Constructs" href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx"&gt;grouping construct&lt;/A&gt; to capture the area code from a larger expression that matches phone numbers:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;(\d\d\d)-\d\d\d-\d\d\d\d&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;&lt;A title="Grouping Constructs" href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx"&gt;Grouping constructs&lt;/A&gt;&amp;nbsp;can also used when applying &lt;A title=Quantifiers href="http://msdn2.microsoft.com/en-us/library/3206d374.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/3206d374.aspx"&gt;quantifiers&lt;/A&gt; in &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt;. As an example, one could match the&amp;nbsp;string "abcabcabc"&amp;nbsp;using a &lt;A title=Quantifiers href="http://msdn2.microsoft.com/en-us/library/3206d374.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/3206d374.aspx"&gt;quantifier&lt;/A&gt; in combination with an expression that matches "abc":&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;(abc){3}&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Each &lt;A title=System.Text.RegularExpressions.Group href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx"&gt;Group&lt;/A&gt; contains one or more &lt;A title=System.Text.RegularExpressions.Capture href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.capture.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.capture.aspx"&gt;Captures&lt;/A&gt;, each of which corresponds to one of the allowed matches&amp;nbsp;for an expression with a &lt;A title=Quantifiers href="http://msdn2.microsoft.com/en-us/library/3206d374.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/3206d374.aspx"&gt;quantifier&lt;/A&gt;. As an example, when the expression&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;(dog|cat){3}&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;is applied to the string "dogdogcat", the resulting subexpression group will contain three &lt;A title=System.Text.RegularExpressions.Capture href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.capture.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.capture.aspx"&gt;Captures&lt;/A&gt;, one each for "dog", "dog", and "cat" (see Figure 2).&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;A href="http://hopcroft.members.winisp.net/images/HomeAutomationPart3UsingRegularExpressio_BA4B/image03.png" atomicselection="true" mce_href="http://hopcroft.members.winisp.net/images/HomeAutomationPart3UsingRegularExpressio_BA4B/image03.png"&gt;&lt;IMG height=449 src="http://hopcroft.members.winisp.net/images/HomeAutomationPart3UsingRegularExpressio_BA4B/image0_thumb3.png" width=523 mce_src="http://hopcroft.members.winisp.net/images/HomeAutomationPart3UsingRegularExpressio_BA4B/image0_thumb3.png"&gt;&lt;/A&gt; &lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Figure 2. An subexpression with a quantifier maps to a group with multiple captures.&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;As you can see, the Interactive Regular Expression Evaluator makes it easy to quickly examine the &lt;A title=System.Text.RegularExpressions.Match href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.match.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.match.aspx"&gt;Match&lt;/A&gt;/&lt;A title=System.Text.RegularExpressions.Group href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx"&gt;Group&lt;/A&gt;/&lt;A title=System.Text.RegularExpressions.Capture href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.capture.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.capture.aspx"&gt;Capture&lt;/A&gt; hierarchy for a particular combination of sample data and &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt;. I encourage you to&amp;nbsp;follow along in&amp;nbsp;the evaluator as you read through the next section which describes the &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt; that extracts useful values from the&amp;nbsp;&lt;A href="http://www.ibuttonlink.com/Link_Details.htm" mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;Link45&lt;/A&gt; data.&lt;/P&gt;
&lt;H3&gt;&lt;STRONG&gt;Building An Expression to Extract Sensor ID, Temperature, and Humidity&lt;/STRONG&gt;&lt;/H3&gt;
&lt;P&gt;In the&amp;nbsp;&lt;A title="Home Automation Part 2 – Making Basic Measurements" href="http://blogs.msdn.com/mhop/archive/2006/12/06/home-automation-part-2-making-basic-measurements.aspx" mce_href="http://blogs.msdn.com/mhop/archive/2006/12/06/home-automation-part-2-making-basic-measurements.aspx"&gt;previous installment&lt;/A&gt; we saw that a "D" command to the &lt;A href="http://www.ibuttonlink.com/Link_Details.htm" mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;Link45&lt;/A&gt; would trigger a response that looks something like&lt;/P&gt;&lt;PRE class=csharpcode&gt;2621D453000000F1 19,17.81,64.00,44
262FD453000000E2 19,18.12,64.56,43
EOD
&lt;/PRE&gt;
&lt;P&gt;Basically, each sensor returns a line of data of the form&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;xxxxxxxxxxxxxxxx ss,ccc.cc,fff.ff,hhh&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;where 
&lt;UL&gt;
&lt;LI&gt;&lt;EM&gt;xxxxxxxxxxxxxxxx&lt;/EM&gt; is a 16 digit hexadecimal sensor ID 
&lt;LI&gt;&lt;EM&gt;ss&lt;/EM&gt; is the sensor type 
&lt;LI&gt;&lt;EM&gt;ccc.cc&lt;/EM&gt; is the temperature in degrees Celsius 
&lt;LI&gt;&lt;EM&gt;fff.ff&lt;/EM&gt; is the temperature in degrees Fahrenheit 
&lt;LI&gt;&lt;EM&gt;hhh&lt;/EM&gt; is the humidity&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;After the last sensor, the result set is terminated by a line containing the string "EOD". 
&lt;P&gt;&lt;STRONG&gt;Matching the Sensor ID.&lt;/STRONG&gt; Let’s start by building the portion of the &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt; that extracts the sensor ID into a named&amp;nbsp;&lt;A title=System.Text.RegularExpressions.Group href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx"&gt;Group&lt;/A&gt; called “Id”. We will start by using the [] &lt;A title="Character Class" href="http://msdn2.microsoft.com/en-us/library/20bw873z.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/20bw873z.aspx"&gt;character class&lt;/A&gt; construct to match a single hexadecimal digit. The []-construct will match a single instance of any character that appears between the square brackets. As an example, [abc] would match ‘a’, ‘b’, or ‘c’. We can match a single hexadecimal digit&amp;nbsp;with 
&lt;BLOCKQUOTE&gt;
&lt;P&gt;[0123456789ABCDEF]&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;The – operator allows us to specify ranges of Unicode characters in a &lt;A title="Character Class" href="http://msdn2.microsoft.com/en-us/library/20bw873z.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/20bw873z.aspx"&gt;character class&lt;/A&gt;, leading to more compact &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expressions&lt;/A&gt;. As an example, [a-z] will match any lower case English letter. Using – , we can write a the following equivalent but more compact expression to match a single hexadecimal digit: 
&lt;BLOCKQUOTE&gt;
&lt;P&gt;[0-9A-F]&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Sensor IDs consist of a sequence of 16 hexadecimal digits. We can use the {} &lt;A title=Quantifiers href="http://msdn2.microsoft.com/en-us/library/3206d374.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/3206d374.aspx"&gt;quantifier&lt;/A&gt; construct&amp;nbsp;to specify the number of adjacent digits to match. The {}&amp;nbsp;construct modifies a regular expression to match multiple copies of its original matching input. As an example, the regular expression a{3} will match “aaa” and is equivalent to the &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expressions&lt;/A&gt; aa{2} and aaa. We can match exactly 16 hexadecimal digits with 
&lt;BLOCKQUOTE&gt;
&lt;P&gt;[0-9A-F]{16}&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Now that we can match the sensor ID, we’d like to actually extract it from the input string. I use a &lt;A title="Grouping Constructs" href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx"&gt;parenthesized subexpression&lt;/A&gt; to put the sensor ID into a match &lt;A title=System.Text.RegularExpressions.Group href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx"&gt;Group&lt;/A&gt;: 
&lt;BLOCKQUOTE&gt;
&lt;P&gt;([0-9A-F]{16})&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Figure 3 shows the match/group/capture hierarchy for this expression when applied to sample data from the &lt;A href="http://www.ibuttonlink.com/Link_Details.htm" mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;Link45&lt;/A&gt;. Matches[0].Groups[0] is the match for the entire &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt;. Matches[0].Groups[1] is the match for the&amp;nbsp;&lt;A title="Grouping Constructs" href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx"&gt;parenthesized subexpression&lt;/A&gt;. Since the entire &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt; is parenthesized, the two &lt;A title=System.Text.RegularExpressions.Group href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx"&gt;Groups&lt;/A&gt;&amp;nbsp;correspond to the same matched substring. 
&lt;P&gt;&lt;A href="http://hopcroft.members.winisp.net/images/HomeAutomationPart3UsingRegularExpressio_BA4B/image05.png" atomicselection="true" mce_href="http://hopcroft.members.winisp.net/images/HomeAutomationPart3UsingRegularExpressio_BA4B/image05.png"&gt;&lt;IMG height=449 src="http://hopcroft.members.winisp.net/images/HomeAutomationPart3UsingRegularExpressio_BA4B/image0_thumb5.png" width=523 mce_src="http://hopcroft.members.winisp.net/images/HomeAutomationPart3UsingRegularExpressio_BA4B/image0_thumb5.png"&gt;&lt;/A&gt; 
&lt;P&gt;&lt;STRONG&gt;Figure 3.&amp;nbsp;This regular&amp;nbsp;expression extracts the first&amp;nbsp;sensor ID.&lt;/STRONG&gt; 
&lt;P&gt;Accessing the extracted sensor ID from code is simple: &lt;PRE class=csharpcode&gt;System.Console.WriteLine(Matches[0].Groups[1].Captures[0].Value);
&lt;/PRE&gt;
&lt;P&gt;This code is somewhat brittle&amp;nbsp;because it assumes that the sensor ID will always be in the second &lt;A title=System.Text.RegularExpressions.Group href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx"&gt;Group&lt;/A&gt;. Any change to the &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt; that introduces another &lt;A title="Grouping Constructs" href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx"&gt;subexpression&lt;/A&gt;&amp;nbsp;before the sensor ID &lt;A title="Grouping Constructs" href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx"&gt;subexpression&lt;/A&gt; will break the code. We can fix this problem by assigning a name to the sensor ID &lt;A title="Grouping Constructs" href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx"&gt;subexpression&lt;/A&gt; using&amp;nbsp;the ?&amp;lt;&amp;gt; construct. Here’s an expression that captures a sequence of 16 hexadecimal digits into a&amp;nbsp;&lt;A title=System.Text.RegularExpressions.Group href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx"&gt;Group&lt;/A&gt;&amp;nbsp;called “Id”: 
&lt;BLOCKQUOTE&gt;
&lt;P&gt;(?&amp;lt;Id&amp;gt;[0-9A-F]{16})&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;The code to access the sensor ID now becomes: &lt;PRE class=csharpcode&gt;System.Console.WriteLine(Matches[0].Groups["Id"].Captures[0].Value);
&lt;/PRE&gt;
&lt;P&gt;This code is better because it does not depend on the position of the sensor ID &lt;A title="Grouping Constructs" href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx"&gt;subexpression&lt;/A&gt; relative to other &lt;A title="Grouping Constructs" href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx"&gt;subexpressions&lt;/A&gt;. Now let's extend the &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt; to grab the temperature.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Matching the Temperature and Humidity.&lt;/STRONG&gt; I want to use Fahrenheit temperatures so after matching the sensor ID,&amp;nbsp;my expression will need to skip over the sensor type and Celsius temperature before matching the Fahrenheit temperature. Skipping over the sensor type is easy - we just need to skip over a space, a two digit sensor type, and a comma. This can be done by appending&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;\s\d\d,&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;to the end of the &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt; for the sensor ID. In this expression, \s denotes a&amp;nbsp;&lt;A title="Character Class" href="http://msdn2.microsoft.com/en-us/library/20bw873z.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/20bw873z.aspx"&gt;character class&lt;/A&gt; that matches any whitespace and&amp;nbsp;\d&amp;nbsp;denotes a &lt;A title="Character Class" href="http://msdn2.microsoft.com/en-us/library/20bw873z.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/20bw873z.aspx"&gt;character class&lt;/A&gt; that matches any decimal digit in the range 0 to 9.&lt;/P&gt;
&lt;P&gt;The Celsius temperature is a bit more complex because when the temperature is below freezing, it starts with a minus sign. After that there are 1-3 decimal digits followed by a decimal point followed two more decimal digits.&lt;/P&gt;
&lt;P&gt;We can use the ? &lt;A title=Quantifiers href="http://msdn2.microsoft.com/en-us/library/3206d374.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/3206d374.aspx"&gt;quantifier&lt;/A&gt; to handle the minus sign. ? modifies an expression to match 0 or 1 instances, so the expression -? matches the minus sign when its cold but doesn't complain when its hot. &lt;/P&gt;
&lt;P&gt;Earlier I used the {16} &lt;A title=Quantifiers href="http://msdn2.microsoft.com/en-us/library/3206d374.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/3206d374.aspx"&gt;quantifier&lt;/A&gt; to&amp;nbsp;match exactly 16 hexadecimal digits in the sensor ID. Another form of the {} &lt;A title=Quantifiers href="http://msdn2.microsoft.com/en-us/library/3206d374.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/3206d374.aspx"&gt;quantifier&lt;/A&gt;&amp;nbsp;allows you to specify&amp;nbsp;an upper and lower bound for the number of matches.&amp;nbsp;A &lt;A title=Quantifiers href="http://msdn2.microsoft.com/en-us/library/3206d374.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/3206d374.aspx"&gt;quantifier&lt;/A&gt; of the form&amp;nbsp;{x,y} will match at least x, but no more than y instances. Putting it all together, we can match the&amp;nbsp;Celsius temperature with&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;-?\d{1,3}\.\d{2}&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;In this expression, the period is preceded by a backslash to indicate a match to a period. Without the backslash, period corresponds to a &lt;A title="Character Class" href="http://msdn2.microsoft.com/en-us/library/20bw873z.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/20bw873z.aspx"&gt;character class&lt;/A&gt; that matches any character. &lt;/P&gt;
&lt;P&gt;We can use the identical expression to match the Fahrenheit&amp;nbsp;temperature, but we'll add a named &lt;A title="Grouping Constructs" href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx"&gt;subexpression&lt;/A&gt; to allow us to capture the value into a group:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;(?&amp;lt;Temp&amp;gt;-?\d{1,3}\.\d{2})&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;After this, matching the humidity is a snap - it is just a pair of decimal digits. There is one catch, though. The sensor network could contain sensors that only report temperature. In this case, the humidity value and the comma that separates it from the temperature would not appear in the&amp;nbsp;&lt;A href="http://www.ibuttonlink.com/Link_Details.htm" mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;Link45&lt;/A&gt; data. We can use the ? &lt;A title=Quantifiers href="http://msdn2.microsoft.com/en-us/library/3206d374.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/3206d374.aspx"&gt;quantifier&lt;/A&gt; to make the entire humidity expression optional:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;(,(?&amp;lt;Humidity&amp;gt;\d{2}))?&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;This expression is less efficient than it could be because it captures the humidity value twice, once with the comma and once without. The&amp;nbsp;reason is that I had to add a second&amp;nbsp;&lt;A title="Grouping Constructs" href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx"&gt;parenthesized subexpression&lt;/A&gt; in order to make the ? &lt;A title=Quantifiers href="http://msdn2.microsoft.com/en-us/library/3206d374.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/3206d374.aspx"&gt;quantifier&lt;/A&gt; apply to the sequence containing the comma &lt;EM&gt;and&lt;/EM&gt; the humidity. We can improve the expression by using ?: to denote a &lt;A title="Grouping Constructs" href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/bs2twtah.aspx"&gt;non-capturing group&lt;/A&gt;:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;(?:,(?&amp;lt;Humidity&amp;gt;\d{2}))?&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Now we're ready to put it all together and write one giant expression to grab the sensor IDs, temperatures, and humidity values from the &lt;A href="http://www.ibuttonlink.com/Link_Details.htm" mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;Link45&lt;/A&gt; data. We do this by appending the following expression fragments:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Match sensor ID:&lt;/STRONG&gt; (?&amp;lt;Id&amp;gt;[0-9A-F]{16}) 
&lt;LI&gt;&lt;STRONG&gt;Skip space, sensor type, and comma:&lt;/STRONG&gt; \s\d\d, 
&lt;LI&gt;&lt;STRONG&gt;Skip Celsius temperature and comma:&lt;/STRONG&gt; -?\d{1,3}\.\d{2}, 
&lt;LI&gt;&lt;STRONG&gt;Match Fahrenheit temperature:&lt;/STRONG&gt; (?&amp;lt;Temp&amp;gt;-?\d{1,3}\.\d{2}) 
&lt;LI&gt;&lt;STRONG&gt;Match optional humidity:&lt;/STRONG&gt; (?:,(?&amp;lt;Humidity&amp;gt;\d{2}))?&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Here's the final, almost incomprehensible&amp;nbsp;expression:&lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;(?&amp;lt;Id&amp;gt;[0-9A-F]{16})\s\d\d,-?\d{1,3}\.\d{2},(?&amp;lt;Temp&amp;gt;-?\d{1,3}\.\d{2})(?:,(?&amp;lt;Humidity&amp;gt;\d{2}))?&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;Figure 4 shows the expression in action.&lt;/P&gt;
&lt;P&gt;&lt;A href="http://hopcroft.members.winisp.net/images/HomeAutomationPart3UsingRegularExpressio_BA4B/image09.png" atomicselection="true" mce_href="http://hopcroft.members.winisp.net/images/HomeAutomationPart3UsingRegularExpressio_BA4B/image09.png"&gt;&lt;IMG height=553 src="http://hopcroft.members.winisp.net/images/HomeAutomationPart3UsingRegularExpressio_BA4B/image0_thumb9.png" width=523 mce_src="http://hopcroft.members.winisp.net/images/HomeAutomationPart3UsingRegularExpressio_BA4B/image0_thumb9.png"&gt;&lt;/A&gt; &lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Figure 4. The final regular expression applied to real &lt;A href="http://www.ibuttonlink.com/Link_Details.htm" mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;Link45&lt;/A&gt; data.&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Now that we have the &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt;, lets modify the &lt;A title="Home Automation Part 2 – Making Basic Measurements" href="http://blogs.msdn.com/mhop/archive/2006/12/06/home-automation-part-2-making-basic-measurements.aspx" mce_href="http://blogs.msdn.com/mhop/archive/2006/12/06/home-automation-part-2-making-basic-measurements.aspx"&gt;Super-Simple Serial Console&lt;/A&gt; from my &lt;A title="Home Automation Part 2 – Making Basic Measurements" href="http://blogs.msdn.com/mhop/archive/2006/12/06/home-automation-part-2-making-basic-measurements.aspx" mce_href="http://blogs.msdn.com/mhop/archive/2006/12/06/home-automation-part-2-making-basic-measurements.aspx"&gt;previous installment&lt;/A&gt; to poll the sensor network and print out the sensor IDs, temperatures, and humidity levels.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Polling the Sensor Network.&lt;/STRONG&gt; The goal here is to write a small console application that initializes the sensor network and then every&amp;nbsp;5 seconds, takes a set of readings and prints them to the console. This application will form the heart of a data logging service that we will implement in a future installment.&lt;/P&gt;
&lt;P&gt;It turns out that &lt;A title=System.IO.Ports.SerialPort href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.aspx"&gt;System.IO.Ports.SerialPort&lt;/A&gt; has a feature that allows us to greatly simplify the code&amp;nbsp;from the &lt;A title="Home Automation Part 2 – Making Basic Measurements" href="http://blogs.msdn.com/mhop/archive/2006/12/06/home-automation-part-2-making-basic-measurements.aspx" mce_href="http://blogs.msdn.com/mhop/archive/2006/12/06/home-automation-part-2-making-basic-measurements.aspx"&gt;Super-Simple Serial Console&lt;/A&gt;. Recall that the &lt;A title="Home Automation Part 2 – Making Basic Measurements" href="http://blogs.msdn.com/mhop/archive/2006/12/06/home-automation-part-2-making-basic-measurements.aspx" mce_href="http://blogs.msdn.com/mhop/archive/2006/12/06/home-automation-part-2-making-basic-measurements.aspx"&gt;Super-Simple Serial Console&lt;/A&gt; needed a bit of multiple-thread coordination to&amp;nbsp;avoid printing the prompt before the &lt;A href="http://www.ibuttonlink.com/Link_Details.htm" mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;Link45&lt;/A&gt; had finished responding. Since our new application only runs the &lt;A href="http://www.ibuttonlink.com/Link_Details.htm" mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;Link45&lt;/A&gt; "D" command, we can take advantage of the fact that all&amp;nbsp;of the &lt;A href="http://www.ibuttonlink.com/Link_Details.htm" mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;Link45&lt;/A&gt; responses will be terminated by the characters "EOD", allowing us to use the serial port's &lt;A title="SerialPort.ReadTo method" href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.readto.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.readto.aspx"&gt;ReadTo()&lt;/A&gt; method.&amp;nbsp;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;A title="SerialPort.ReadTo method" href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.readto.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.readto.aspx"&gt;ReadTo()&lt;/A&gt; takes a single string argument and blocks until this string appears in the serial port's input buffer or a read timeout limit has been exceeded. When &lt;A title="SerialPort.ReadTo method" href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.readto.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.readto.aspx"&gt;ReadTo()&lt;/A&gt; detects the termination string, it returns the string of characters received up until the termination string. The termination string itself is not returned, but it is removed from the serial port's input buffer. Listing&amp;nbsp;2 shows&amp;nbsp;the sensor reading application.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Listing 2. This application reads the sensor data&amp;nbsp;and prints it to the console.&lt;/STRONG&gt;&lt;/P&gt;
&lt;DIV class=csharpcode&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   1:  &lt;/SPAN&gt;&lt;SPAN class=kwrd&gt;using&lt;/SPAN&gt; System;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   2:  &lt;/SPAN&gt;&lt;SPAN class=kwrd&gt;using&lt;/SPAN&gt; System.IO.Ports;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   3:  &lt;/SPAN&gt;&lt;SPAN class=kwrd&gt;using&lt;/SPAN&gt; System.Text.RegularExpressions;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   4:  &lt;/SPAN&gt;&lt;SPAN class=kwrd&gt;using&lt;/SPAN&gt; System.Threading;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   5:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   6:  &lt;/SPAN&gt;&lt;SPAN class=kwrd&gt;namespace&lt;/SPAN&gt; SensorConsole&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   7:  &lt;/SPAN&gt;{&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   8:  &lt;/SPAN&gt;    &lt;SPAN class=rem&gt;// SensorConsole reads from the sensor network every 5 seconds and prints the&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   9:  &lt;/SPAN&gt;    &lt;SPAN class=rem&gt;// sensor id, temperature, and humidity for each sensor.&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  10:  &lt;/SPAN&gt;    &lt;SPAN class=kwrd&gt;class&lt;/SPAN&gt; SensorConsole&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  11:  &lt;/SPAN&gt;    {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  12:  &lt;/SPAN&gt;        &lt;SPAN class=kwrd&gt;private&lt;/SPAN&gt; SerialPort port;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  13:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  14:  &lt;/SPAN&gt;        &lt;SPAN class=kwrd&gt;public&lt;/SPAN&gt; &lt;SPAN class=kwrd&gt;void&lt;/SPAN&gt; Run()&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  15:  &lt;/SPAN&gt;        {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  16:  &lt;/SPAN&gt;            Console.WriteLine(&lt;SPAN class=str&gt;"Temperature and Humidity Logger"&lt;/SPAN&gt;);&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  17:  &lt;/SPAN&gt;            &lt;SPAN class=kwrd&gt;try&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  18:  &lt;/SPAN&gt;            {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  19:  &lt;/SPAN&gt;                Open();&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  20:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  21:  &lt;/SPAN&gt;                Regex r = &lt;SPAN class=kwrd&gt;new&lt;/SPAN&gt; Regex(&lt;SPAN class=str&gt;@"(?&amp;lt;Id&amp;gt;[0-9A-F]{16})\s\d\d,-?\d{1,3}\.\d{2},(?&amp;lt;Temp&amp;gt;-?\d{1,3}\.\d{2})(?:,(?&amp;lt;Humidity&amp;gt;\d{2}))?"&lt;/SPAN&gt;);&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  22:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  23:  &lt;/SPAN&gt;                &lt;SPAN class=kwrd&gt;while&lt;/SPAN&gt; (&lt;SPAN class=kwrd&gt;true&lt;/SPAN&gt;)&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  24:  &lt;/SPAN&gt;                {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  25:  &lt;/SPAN&gt;                    &lt;SPAN class=kwrd&gt;try&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  26:  &lt;/SPAN&gt;                    {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  27:  &lt;/SPAN&gt;                        port.Write(&lt;SPAN class=str&gt;"D"&lt;/SPAN&gt;);&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  28:  &lt;/SPAN&gt;                        &lt;SPAN class=kwrd&gt;string&lt;/SPAN&gt; s = port.ReadTo(&lt;SPAN class=str&gt;"EOD"&lt;/SPAN&gt;);&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  29:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  30:  &lt;/SPAN&gt;                        MatchCollection matches = r.Matches(s);&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  31:  &lt;/SPAN&gt;                        &lt;SPAN class=kwrd&gt;foreach&lt;/SPAN&gt; (Match m &lt;SPAN class=kwrd&gt;in&lt;/SPAN&gt; matches)&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  32:  &lt;/SPAN&gt;                        {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  33:  &lt;/SPAN&gt;                            Int64 id = Int64.Parse(m.Groups[&lt;SPAN class=str&gt;"Id"&lt;/SPAN&gt;].Value, System.Globalization.NumberStyles.HexNumber);&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  34:  &lt;/SPAN&gt;                            &lt;SPAN class=kwrd&gt;float&lt;/SPAN&gt; temp = &lt;SPAN class=kwrd&gt;float&lt;/SPAN&gt;.Parse(m.Groups[&lt;SPAN class=str&gt;"Temp"&lt;/SPAN&gt;].Value);&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  35:  &lt;/SPAN&gt;                            &lt;SPAN class=kwrd&gt;int&lt;/SPAN&gt; humidity = &lt;SPAN class=kwrd&gt;int&lt;/SPAN&gt;.Parse(m.Groups[&lt;SPAN class=str&gt;"Humidity"&lt;/SPAN&gt;].Value);&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  36:  &lt;/SPAN&gt;                            Console.WriteLine(&lt;SPAN class=str&gt;"{0:X}: {1} degrees F and {2}% humidity"&lt;/SPAN&gt;, id, temp, humidity);&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  37:  &lt;/SPAN&gt;                        }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  38:  &lt;/SPAN&gt;                        Console.WriteLine();&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  39:  &lt;/SPAN&gt;                    }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  40:  &lt;/SPAN&gt;                    &lt;SPAN class=kwrd&gt;catch&lt;/SPAN&gt; (System.Exception e)&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  41:  &lt;/SPAN&gt;                    {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  42:  &lt;/SPAN&gt;                        &lt;SPAN class=kwrd&gt;if&lt;/SPAN&gt; (e &lt;SPAN class=kwrd&gt;is&lt;/SPAN&gt; System.TimeoutException)&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  43:  &lt;/SPAN&gt;                        {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  44:  &lt;/SPAN&gt;                            Console.WriteLine(&lt;SPAN class=str&gt;"Error: timeout reading sensor data."&lt;/SPAN&gt;);&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  45:  &lt;/SPAN&gt;                        }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  46:  &lt;/SPAN&gt;                        &lt;SPAN class=kwrd&gt;else&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  47:  &lt;/SPAN&gt;                            &lt;SPAN class=kwrd&gt;throw&lt;/SPAN&gt; e;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  48:  &lt;/SPAN&gt;                    }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  49:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  50:  &lt;/SPAN&gt;                    &lt;SPAN class=rem&gt;// Wait 5s between sensor readings.&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  51:  &lt;/SPAN&gt;                    Thread.Sleep(5000);&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  52:  &lt;/SPAN&gt;                }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  53:  &lt;/SPAN&gt;            }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  54:  &lt;/SPAN&gt;            &lt;SPAN class=kwrd&gt;finally&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  55:  &lt;/SPAN&gt;            {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  56:  &lt;/SPAN&gt;                &lt;SPAN class=kwrd&gt;if&lt;/SPAN&gt; (port != &lt;SPAN class=kwrd&gt;null&lt;/SPAN&gt; &amp;amp;&amp;amp; port.IsOpen)&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  57:  &lt;/SPAN&gt;                    port.Close();&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  58:  &lt;/SPAN&gt;            }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  59:  &lt;/SPAN&gt;        }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  60:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  61:  &lt;/SPAN&gt;        &lt;SPAN class=kwrd&gt;void&lt;/SPAN&gt; Open()&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  62:  &lt;/SPAN&gt;        {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  63:  &lt;/SPAN&gt;            port = &lt;SPAN class=kwrd&gt;new&lt;/SPAN&gt; SerialPort(&lt;SPAN class=str&gt;"COM1"&lt;/SPAN&gt;, 9600, Parity.None, 8, StopBits.One);&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  64:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  65:  &lt;/SPAN&gt;            &lt;SPAN class=rem&gt;// The iButton Link45 needs DTR and RTS enabled.&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  66:  &lt;/SPAN&gt;            port.DtrEnable = &lt;SPAN class=kwrd&gt;true&lt;/SPAN&gt;;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  67:  &lt;/SPAN&gt;            port.RtsEnable = &lt;SPAN class=kwrd&gt;true&lt;/SPAN&gt;;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  68:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  69:  &lt;/SPAN&gt;            &lt;SPAN class=rem&gt;// Bump up the read timeout from the default 500ms to ensure time for &lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  70:  &lt;/SPAN&gt;            &lt;SPAN class=rem&gt;// reporting from large sensor networks.&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  71:  &lt;/SPAN&gt;            port.ReadTimeout = 5000;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  72:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  73:  &lt;/SPAN&gt;            &lt;SPAN class=rem&gt;// The serial port is now configured and ready to be opened.&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  74:  &lt;/SPAN&gt;            port.Open();&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  75:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  76:  &lt;/SPAN&gt;            &lt;SPAN class=rem&gt;// Shock the Link45 to life by sending a break.&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  77:  &lt;/SPAN&gt;            port.BreakState = &lt;SPAN class=kwrd&gt;true&lt;/SPAN&gt;;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  78:  &lt;/SPAN&gt;            port.BreakState = &lt;SPAN class=kwrd&gt;false&lt;/SPAN&gt;;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  79:  &lt;/SPAN&gt;        }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  80:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  81:  &lt;/SPAN&gt;        &lt;SPAN class=kwrd&gt;void&lt;/SPAN&gt; Close()&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  82:  &lt;/SPAN&gt;        {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  83:  &lt;/SPAN&gt;            port.Close();&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  84:  &lt;/SPAN&gt;        }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  85:  &lt;/SPAN&gt;    }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  86:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  87:  &lt;/SPAN&gt;    &lt;SPAN class=kwrd&gt;class&lt;/SPAN&gt; Program&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  88:  &lt;/SPAN&gt;    {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  89:  &lt;/SPAN&gt;        &lt;SPAN class=kwrd&gt;static&lt;/SPAN&gt; &lt;SPAN class=kwrd&gt;void&lt;/SPAN&gt; Main(&lt;SPAN class=kwrd&gt;string&lt;/SPAN&gt;[] args)&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  90:  &lt;/SPAN&gt;        {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  91:  &lt;/SPAN&gt;            SensorConsole sc = &lt;SPAN class=kwrd&gt;new&lt;/SPAN&gt; SensorConsole();&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  92:  &lt;/SPAN&gt;            sc.Run();&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  93:  &lt;/SPAN&gt;        }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  94:  &lt;/SPAN&gt;    }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  95:  &lt;/SPAN&gt;}&lt;/PRE&gt;&lt;/DIV&gt;
&lt;P&gt;Let's walk through the code in execution order, starting at the entry point on line 89. &lt;STRONG&gt;&lt;EM&gt;Main()&lt;/EM&gt;&lt;/STRONG&gt; just creates a &lt;STRONG&gt;&lt;EM&gt;SensorConsole&lt;/EM&gt;&lt;/STRONG&gt; and invokes its &lt;STRONG&gt;&lt;EM&gt;Run()&lt;/EM&gt;&lt;/STRONG&gt; method. The body of the &lt;STRONG&gt;&lt;EM&gt;Run()&lt;/EM&gt;&lt;/STRONG&gt; method is protected by a try-finally block (line 17-58) that ensures the serial port is closed before returning from &lt;STRONG&gt;&lt;EM&gt;Run()&lt;/EM&gt;&lt;/STRONG&gt;. &lt;/P&gt;
&lt;P&gt;The first order of business inside the try block is to call the &lt;STRONG&gt;&lt;EM&gt;Open()&lt;/EM&gt;&lt;/STRONG&gt; method (lines 61-79) which initializes the serial port. The &lt;STRONG&gt;&lt;EM&gt;Open()&lt;/EM&gt;&lt;/STRONG&gt; method is nearly the same as the one in the &lt;A title="Home Automation Part 2 – Making Basic Measurements" href="http://blogs.msdn.com/mhop/archive/2006/12/06/home-automation-part-2-making-basic-measurements.aspx" mce_href="http://blogs.msdn.com/mhop/archive/2006/12/06/home-automation-part-2-making-basic-measurements.aspx"&gt;Super-Simple Serial Console&lt;/A&gt;. The differences are that we no longer need to wire up the serial port's &lt;A title="SerialPort.DataReceived Event" href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.datareceived.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.datareceived.aspx"&gt;DataReceived&lt;/A&gt; event because we are using &lt;A title="SerialPort.ReadTo method" href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.readto.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.readto.aspx"&gt;ReadTo()&lt;/A&gt; and we bumped up the serial port's &lt;A title="SerialPort.ReadTimeout property" href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.readtimeout.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.readtimeout.aspx"&gt;ReadTimeout&lt;/A&gt; from 500ms to 5000ms to be on the safe side in case the &lt;A href="http://www.ibuttonlink.com/Link_Details.htm" mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;Link45&lt;/A&gt; takes more than a half a second to return data from a large sensor network.&lt;/P&gt;
&lt;P&gt;After returning from &lt;STRONG&gt;&lt;EM&gt;Open()&lt;/EM&gt;&lt;/STRONG&gt;, the&amp;nbsp;&lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt; is&amp;nbsp;built using the &lt;A title="System.Text.RegularExpressions.Regex class" href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.regex.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.regex.aspx"&gt;Regex&lt;/A&gt;&amp;nbsp;class (line 21). Note the @-sign immediately before the &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt;&amp;nbsp;string. The @-sign indicates to the compiler that the following string should be treated literally with no special meaning for the backslashes. If I didn't use the @-sign, I would have had to escape each of the backslashes with "\\" which would have led to an even less comprehensible &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;The main loop&amp;nbsp;spans from line 23 to 52. Right inside the loop a try block (lines 25-48) is used to separate serial port timeout exceptions from other potential problems. As this code morphs into a more sophisticated&amp;nbsp;always-on data logger, I will need to extend this error handling so that the logger stays up and running&amp;nbsp;in the presence of intermittent communication or sensor failures.&lt;/P&gt;
&lt;P&gt;Inside the try block, we issue a "D" command to the&amp;nbsp;&lt;A href="http://www.ibuttonlink.com/Link_Details.htm" mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;Link45&lt;/A&gt; on line 27 and then wait for a response on Line 28. Assuming we didn't get a timeout exception, control passes to line 30 where the our &lt;A title=".NET Framework Regular Expressions" href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/hs600312.aspx"&gt;regular expression&lt;/A&gt; is applied to the &lt;A href="http://www.ibuttonlink.com/Link_Details.htm" mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;Link45&lt;/A&gt; data. The foreach loop on lines 31-37 walks over each of the &lt;A title=System.Text.RegularExpressions.Match href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.match.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.match.aspx"&gt;Matches&lt;/A&gt;, extracting the sensor ID, temperature, and humidity. Since the expression is designed to return a single &lt;A title=System.Text.RegularExpressions.Capture href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.capture.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.capture.aspx"&gt;Capture&lt;/A&gt;&amp;nbsp;per &lt;A title=System.Text.RegularExpressions.Group href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx"&gt;Group&lt;/A&gt;, I've used the shorthand of &lt;STRONG&gt;&lt;EM&gt;m.Groups["Id"].Value&lt;/EM&gt;&lt;/STRONG&gt; instead of the more verbose &lt;STRONG&gt;&lt;EM&gt;m.Groups["Id"].Captures[0].Value&lt;/EM&gt;&lt;/STRONG&gt;. The &lt;A title=System.Text.RegularExpressions.Group href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.text.regularexpressions.group.aspx"&gt;Group&lt;/A&gt;&amp;nbsp;values are returned as strings so I pass the ID, temperature, and humidity&amp;nbsp;to &lt;A title="Int64.Parse method" href="http://msdn2.microsoft.com/en-gb/library/3b6b4bx3.aspx" mce_href="http://msdn2.microsoft.com/en-gb/library/3b6b4bx3.aspx"&gt;Int64.Parse()&lt;/A&gt;, &lt;A title="Single.Parse method" href="http://msdn2.microsoft.com/en-us/library/2thct5cb.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/2thct5cb.aspx"&gt;float.Parse()&lt;/A&gt;, and &lt;A title="Int32.Parse method" href="http://msdn2.microsoft.com/en-us/library/system.int32.parse.aspx" mce_href="http://msdn2.microsoft.com/en-us/library/system.int32.parse.aspx"&gt;int.Parse()&lt;/A&gt; in order to get the data into the correct types.&amp;nbsp;After converting the strings, we print the values back out to the console (line 36) and head to the top of the foreach loop (line 31) to process the next sensor's results. When we run out of sensors, we print out a blank line (line 38), sleep for 5 seconds (line 51) and then head to the top of the while loop (line 23) to do it all over again.&lt;/P&gt;
&lt;P&gt;We can now read the sensor network and extract useful fields like sensor ID, temperature, and humidity. Our next step will be to log the sensor values to a &lt;A title="Microsoft SQL Server 2005 Home" href="http://www.microsoft.com/sql/default.mspx" mce_href="http://www.microsoft.com/sql/default.mspx"&gt;SQL database&lt;/A&gt;. &lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Next Installment: Logging sensor values to a database.&lt;/STRONG&gt;&amp;nbsp;&lt;/P&gt;
&lt;STYLE type=text/css&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/STYLE&gt;
&lt;A href="http://www.dotnetkicks.com/kick/?url=http://blogs.msdn.com/mhop/archive/2007/01/07/home-automation-part-3-using-regular-expressions-to-parse-sensor-data.aspx"&gt;&lt;IMG alt="kick it on DotNetKicks.com" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http://blogs.msdn.com/mhop/archive/2007/01/07/home-automation-part-3-using-regular-expressions-to-parse-sensor-data.aspx" border=0&gt;&lt;/A&gt; &lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1433109" width="1" height="1"&gt;</content><author><name>mhop</name><uri>http://blogs.msdn.com/members/mhop.aspx</uri></author><category term="Home Automation" scheme="http://blogs.msdn.com/mhop/archive/tags/Home+Automation/default.aspx" /></entry><entry><title>Building a traffic signal with Christmas lights</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/mhop/archive/2006/12/19/building-a-traffic-signal-with-christmas-lights.aspx" /><id>http://blogs.msdn.com/mhop/archive/2006/12/19/building-a-traffic-signal-with-christmas-lights.aspx</id><published>2006-12-19T11:12:16Z</published><updated>2006-12-19T11:12:16Z</updated><content type="html">&lt;p&gt;It started out as a simple project. My children had become interested in basic electrical circuits so&amp;nbsp;I bought them some &lt;a title="Wikipedia - LED" href="http://en.wikipedia.org/wiki/Led" target="_blank"&gt;LEDs&lt;/a&gt;, batteries, wire, and &lt;a title="Wikipedia Breadboard" href="http://en.wikipedia.org/wiki/Breadboard" target="_blank"&gt;solderless breadboard&lt;/a&gt; to play around with. I soon found that these components weren't ideal for a number of reasons. While the solderless breadboard makes it easy to wire up circuits, it hides many of the electrical connections inside making it harder for children to grasp the concept of a circuit. In order to light up, the LEDs must be wired with the correct polarity and their uninsulated leads must not make a short. These factors combined to make it hard for the children to consistently build circuits that would light up.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;A simple idea.&lt;/strong&gt; I had a moment of inspiration one evening while walking&amp;nbsp;my dog through the neighborhood in the days leading up to Christmas. I realized that miniature Christmas lights actually only require a few volts to light up. A string of Christmas lights&amp;nbsp;runs on&amp;nbsp;120v, but the bulbs are wired in &lt;a title="Wikipedia - Series and parallel circuits" href="http://en.wikipedia.org/wiki/Series_circuit" target="_blank"&gt;series&lt;/a&gt;&amp;nbsp;so an individual bulb needs only 2-3 volts, a safe amount that can be obtained through a pair of flashlight batteries. (you can find out everything you ever wanted to know about Christmas lights &lt;a title="Christmas Lights and How to Fix Them" href="http://www.ciphersbyritter.com/RADELECT/LITES/XMSLITES.HTM" target="_blank"&gt;here&lt;/a&gt;)&lt;/p&gt; &lt;p&gt;Christmas lights have a number of advantages over LEDs. They come in sockets with insulated wires already attached, they come in many colors, they all use the same voltage, and they don't care about polarity. Another benefit is cost. At most hardware stores you can purchase five replacement bulbs for about a dollar, but they don't come in sockets (see Figure 1).&lt;/p&gt; &lt;p&gt;&lt;br&gt;&lt;a href="http://hopcroft.members.winisp.net/images/BuildingatrafficsignalwithChristmaslight_12138/IMG_58111.jpg" atomicselection="true"&gt;&lt;img height="480" src="http://hopcroft.members.winisp.net/images/BuildingatrafficsignalwithChristmaslight_12138/IMG_5811_thumb1.jpg" width="640"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Figure 1.&amp;nbsp;Five bulbs and&amp;nbsp;no sockets for&amp;nbsp;99¢ at the local hardware store.&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;A better deal is to purchase 100 bulbs and 100 sockets and 100 feet of wire&amp;nbsp;for about three dollars (see Figure 2) and if you wait until the day after Christmas you can probably do even better.&lt;a href="http://hopcroft.members.winisp.net/images/BuildingatrafficsignalwithChristmaslight_12138/IMG_5811.jpg" atomicselection="true"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;&lt;a href="http://hopcroft.members.winisp.net/images/BuildingatrafficsignalwithChristmaslight_12138/IMG_58125.jpg" atomicselection="true"&gt;&lt;img height="480" src="http://hopcroft.members.winisp.net/images/BuildingatrafficsignalwithChristmaslight_12138/IMG_5812_thumb5.jpg" width="640"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;&lt;strong&gt;Figure 2. These bulbs cost about 3¢ each and come in convenient pre-wired sockets.&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;I ended up getting&amp;nbsp;a few&amp;nbsp;strings of lights along with a battery holder, some alligator clips, and a knife switch. After cutting a light out of the string I was able to make a simple circuit (see Figure 3)&amp;nbsp;and I felt that it illustrated the concepts better than the LEDs and solderless breadboard. Everything in this circuit is visible and can be manipulated and arranged. I like how the knife switch really gets at the essence&amp;nbsp;of closing a circuit.&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;a href="http://hopcroft.members.winisp.net/images/BuildingatrafficsignalwithChristmaslight_12138/IMG_58131.jpg" atomicselection="true"&gt;&lt;img height="480" src="http://hopcroft.members.winisp.net/images/BuildingatrafficsignalwithChristmaslight_12138/IMG_5813_thumb1.jpg" width="640"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;&lt;strong&gt;Figure 3. This circuit is easy to understand because everything is visible.&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;To be safe, I cut off both 120v plugs from the string of lights and then gave the lights&amp;nbsp;to my kids along with a wire cutter/stripper&amp;nbsp;and the batteries, alligator clamps, and knife switches. They quickly made simple circuits and then proceeded to wire up lights for their &lt;a title="LEGO.com The Official Web Site of LEGO (r) products!" href="http://www.lego.com" target="_blank"&gt;Lego&lt;/a&gt; creations and dioramas. I probably should have stopped here.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;A simple idea&amp;nbsp;grows into&amp;nbsp;a bigger project.&lt;/strong&gt; As I watched the kids&amp;nbsp;play with the lights I got the idea to make them a stoplight. I&amp;nbsp;knew it would be too complex for them to wire up by themselves, but I figured they could make a nice cardboard box for it and put it to good use. I decided to use a 2-pole 6-throw rotary switch from &lt;a title="RadioShack.com" href="http://www.radioshack.com" target="_blank"&gt;Radio Shack&lt;/a&gt; to control 12 lamps. This would allow green, yellow, and red lamps for roads coming from 4 directions. Before getting out the soldering iron, I wrote down the&amp;nbsp;a simple&amp;nbsp;state table for the 6 positions of the rotary switch (see table 1). After writing&amp;nbsp;down the state table, I realized that a four-position switch would have worked better since the light cycles through four states before repeating. I&amp;nbsp;only had a six-position switch so I went with the circuit&amp;nbsp;in figure 4.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Table 1.&amp;nbsp;State diagram for 2 roads crossing.&amp;nbsp;&lt;/strong&gt;&lt;/p&gt; &lt;table border="solid black 1" border-collapse="true"&gt; &lt;tbody&gt; &lt;tr&gt; &lt;td&gt;Position&lt;/td&gt; &lt;td&gt;North/South Lamps&lt;/td&gt; &lt;td&gt;East/West Lamps&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;1&lt;/td&gt; &lt;td&gt;Green&lt;/td&gt; &lt;td&gt;Red&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;2&lt;/td&gt; &lt;td&gt;Yellow&lt;/td&gt; &lt;td&gt;Red&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;3&lt;/td&gt; &lt;td&gt;Red&lt;/td&gt; &lt;td&gt;Green&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;4&lt;/td&gt; &lt;td&gt;Red&lt;/td&gt; &lt;td&gt;Yellow&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;5&lt;/td&gt; &lt;td&gt;Green&lt;/td&gt; &lt;td&gt;Red&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt; &lt;td&gt;6&lt;/td&gt; &lt;td&gt;Yellow&lt;/td&gt; &lt;td&gt;Red&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;a href="http://hopcroft.members.winisp.net/images/BuildingatrafficsignalwithChristmaslight_12138/image01.png" atomicselection="true"&gt;&lt;img height="645" src="http://hopcroft.members.winisp.net/images/BuildingatrafficsignalwithChristmaslight_12138/image0_thumb1.png" width="738"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;&lt;strong&gt;Figure 4. Circuit for 12 lights on a 6-position rotary switch.&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;The circuit looks pretty simple, but it actually took me a while to build. The time consuming parts were scavenging and testing 12 lamps (figure 5), and soldering in close quarters on the rotary switch (figure 6).&lt;/p&gt; &lt;p&gt;&lt;a href="http://hopcroft.members.winisp.net/images/BuildingatrafficsignalwithChristmaslight_12138/IMG_58211.jpg" atomicselection="true"&gt;&lt;img height="480" src="http://hopcroft.members.winisp.net/images/BuildingatrafficsignalwithChristmaslight_12138/IMG_5821_thumb1.jpg" width="640"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;&lt;strong&gt;Figure 5. A stoplight seems pretty simple, but it actually has 12 lamps inside.&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;&lt;/strong&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;a href="http://hopcroft.members.winisp.net/images/BuildingatrafficsignalwithChristmaslight_12138/IMG_58321.jpg" atomicselection="true"&gt;&lt;img height="480" src="http://hopcroft.members.winisp.net/images/BuildingatrafficsignalwithChristmaslight_12138/IMG_5832_thumb1.jpg" width="360"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;&lt;strong&gt;Figure 6. Wiring the rotary switch was tricky.&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;By the time I was done, I was really glad that I didn't attempt&amp;nbsp;this project with the kids. They would have become bored long before the first solder melted. Figure 7 shows the completed circuit. I taped the bulbs to a board to help keep the wires under control and I made a long wire harness between the switch and the bulbs to simplify mounting in whatever enclosure the kids decide to invent.&lt;/p&gt; &lt;p&gt;&lt;a href="http://hopcroft.members.winisp.net/images/BuildingatrafficsignalwithChristmaslight_12138/IMG_58301.jpg" atomicselection="true"&gt;&lt;img height="480" src="http://hopcroft.members.winisp.net/images/BuildingatrafficsignalwithChristmaslight_12138/IMG_5830_thumb1.jpg" width="640"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;&lt;strong&gt;Figure 7. What started as a simple project got complex.&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Overall, I was fairly satisfied with the stoplight, but am already making plans for a better one. It turned out that the rotary switch presented a number of challenges. One was that is was very hard for the kids to turn - they just couldn't get a tight enough grip on the shaft. I can fix this by putting a knob on the switch. The other problem is that the switch has a stop after position 6. I would have preferred a switch that could advance from position 6 back to position 1. I thought about cutting off the metal stop inside the switch, but decided against it because of the risk of breaking the switch that took so long to wire up.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;This may be getting out of hand. &lt;/strong&gt;If I make another stoplight, I am going to place an emphasis on ease of assembly. I will probably use LEDs soldered directly onto a prototyping board as shown in figure 8.&amp;nbsp;Instead of a complicated wiring harness, I'll&amp;nbsp;use 8-conductor ribbon wire with a single inline plug. &lt;/p&gt; &lt;p&gt;&lt;a href="http://hopcroft.members.winisp.net/images/BuildingatrafficsignalwithChristmaslight_12138/image03.png" atomicselection="true"&gt;&lt;img height="581" src="http://hopcroft.members.winisp.net/images/BuildingatrafficsignalwithChristmaslight_12138/image0_thumb3.png" width="723"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;&lt;strong&gt;Figure 8. An easier-to-assemble design.&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;I'll also replace the rotary switch with a simple sequencing circuit or perhaps a micro-controller such as a &lt;a title="Basic Stamps" href="http://www.parallax.com/html_pages/products/basicstamps/basic_stamps.asp" target="_blank"&gt;Basic Stamp&lt;/a&gt;. I like the micro-controller idea because I'd get to write software for features like flashing red, sequenced lights, and even pressure sensors. If I do this I'm sure you will read about it here.&lt;/p&gt; &lt;p&gt;Have a wonderful holiday and a happy New Year.&lt;/p&gt; &lt;p&gt;-Mike&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1323586" width="1" height="1"&gt;</content><author><name>mhop</name><uri>http://blogs.msdn.com/members/mhop.aspx</uri></author></entry><entry><title>Save the date for PDC '07</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/mhop/archive/2006/12/13/save-the-date-for-pdc-07.aspx" /><id>http://blogs.msdn.com/mhop/archive/2006/12/13/save-the-date-for-pdc-07.aspx</id><published>2006-12-14T10:52:33Z</published><updated>2006-12-14T10:52:33Z</updated><content type="html">&lt;p&gt;Microsoft has just announced that the next &lt;a title="Professional Developers Conference" href="http://msdn.microsoft.com/events/pdc/"&gt;Professional Developers Conference&lt;/a&gt; (PDC) will be October 2-5, 2007 in Los Angeles, with two days of pre-conference on September 30 and October 1. The last two PDCs were fantastic and this one is likely to be just as exciting and informative. You can find more information &lt;a title="Professional Developers Conference" href="http://msdn.microsoft.com/events/pdc/"&gt;here&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Hope this helps.&lt;/p&gt; &lt;p&gt;-Mike&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1282346" width="1" height="1"&gt;</content><author><name>mhop</name><uri>http://blogs.msdn.com/members/mhop.aspx</uri></author><category term="Events" scheme="http://blogs.msdn.com/mhop/archive/tags/Events/default.aspx" /></entry><entry><title>Implicit and Explicit Interface Implementations</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/mhop/archive/2006/12/12/implicit-and-explicit-interface-implementations.aspx" /><id>http://blogs.msdn.com/mhop/archive/2006/12/12/implicit-and-explicit-interface-implementations.aspx</id><published>2006-12-13T10:59:13Z</published><updated>2006-12-13T10:59:13Z</updated><content type="html">&lt;p&gt;As I was putting together a post on &lt;a title="IEnumerable Interface" href="http://msdn2.microsoft.com/en-us/library/system.collections.ienumerable.aspx"&gt;IEnumerable&lt;/a&gt;&amp;nbsp;and &lt;a title="IEnumerator Interface" href="http://msdn2.microsoft.com/en-us/library/system.collections.ienumerator.aspx"&gt;IEnumerator&lt;/a&gt;&amp;nbsp;I was reminded of the subtleties of implicit and explicit interface implementations. C# does not support multiple inheritance, but a class has the option of implementing one or more interfaces. One challenge with interfaces is that they may include methods that have the same signatures as existing class members or members of other interfaces. Explicit interface implementations can be used to disambiguate class and interface methods that would otherwise conflict. Explicit interfaces can also be used to hide the details of an interface that the class developer considers private.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Disambiguating Methods&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Let's look at an example of method disambiguation. In Listing 1 we have started to write a class called &lt;strong&gt;C&lt;/strong&gt; that implements interfaces &lt;strong&gt;I1&lt;/strong&gt; and &lt;strong&gt;I2&lt;/strong&gt;, each of which defines a method &lt;strong&gt;A()&lt;/strong&gt;.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Listing 1. Class C implements interfaces I1 and I2.&lt;/strong&gt;&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;interface&lt;/span&gt; I1
{
    &lt;span class="kwrd"&gt;void&lt;/span&gt; A();
}

&lt;span class="kwrd"&gt;interface&lt;/span&gt; I2
{
    &lt;span class="kwrd"&gt;void&lt;/span&gt; A();
}

&lt;span class="kwrd"&gt;class&lt;/span&gt; C : I1, I2
{
    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; A()
    {
        Console.WriteLine(&lt;span class="str"&gt;"C.A()"&lt;/span&gt;);
    }
}
&lt;/pre&gt;
&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: consolas, "Courier New", courier, monospace;
	background-color: #ffffff;
	/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}
.csharpcode .lnum { color: #606060; }
&lt;/style&gt;

&lt;p&gt;In this case, &lt;strong&gt;A()&lt;/strong&gt; is a public class member that &lt;em&gt;implicitly&lt;/em&gt; implements a member of&amp;nbsp;&lt;em&gt;both&lt;/em&gt; interfaces. &lt;strong&gt;A()&lt;/strong&gt; can be invoked through either interface or through the class itself&amp;nbsp;as follows:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Listing 2. A() can be invoked from I1, I2, or C.&lt;/strong&gt;&lt;/p&gt;&lt;pre class="csharpcode"&gt;C c = &lt;span class="kwrd"&gt;new&lt;/span&gt; C();
I1 i1 = (I1)c;
I2 i2 = (I2)c;

i1.A();
i2.A();
c.A();&lt;/pre&gt;
&lt;p&gt;The output from this code is&lt;/p&gt;&lt;pre class="csharpcode"&gt;C.A()
C.A()
C.A()&lt;/pre&gt;
&lt;p&gt;This works fine if you want &lt;strong&gt;A()&lt;/strong&gt; to do the same thing in both interfaces. In most cases, however, methods in different interfaces have distinct purposes requiring wholly different implementations. This is where explicit interface implementations come in handy. To explicitly implement an interface member, just use its fully qualified name in the declaration. A fully qualified interface name takes the form&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;InterfaceName.MemberName&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;In Listing 3 we add an explicit implementation of &lt;strong&gt;I1&lt;/strong&gt;'s &lt;strong&gt;A()&lt;/strong&gt; method.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Listing 3. Class C explicitly implements I1.A().&lt;/strong&gt;&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;class&lt;/span&gt; C : I1, I2
{
    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; A()
    {
        Console.WriteLine(&lt;span class="str"&gt;"C.A()"&lt;/span&gt;);
    }

    &lt;span class="kwrd"&gt;void&lt;/span&gt; I1.A()
    {
        Console.WriteLine(&lt;span class="str"&gt;"I1.A()"&lt;/span&gt;);
    }
}&lt;/pre&gt;
&lt;p&gt;Now when we run the statements from Listing 2 we get&lt;/p&gt;&lt;pre class="csharpcode"&gt;I1.A()
C.A()
C.A()&lt;/pre&gt;
&lt;p&gt;When an interface method is explicitly implemented, it is no longer visible as a public member of the class. The only way to access it is through the&amp;nbsp;interface. As an example, suppose we deleted the implicit implementation of &lt;strong&gt;A()&lt;/strong&gt; as shown in Listing 4:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Listing 4. Class C does not implicitly implement A()&lt;/strong&gt;&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;class&lt;/span&gt; C : I1, I2
{
    &lt;span class="kwrd"&gt;void&lt;/span&gt; I1.A()
    {
        Console.WriteLine(&lt;span class="str"&gt;"I2.A()"&lt;/span&gt;);
    }
}
&lt;/pre&gt;
&lt;p&gt;In this case we would get a compile error saying that &lt;strong&gt;C&lt;/strong&gt; fails to implement &lt;strong&gt;I2.A()&lt;/strong&gt;. We could fix this error by changing the first line to&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;class&lt;/span&gt; C : I1&lt;/pre&gt;
&lt;p&gt;but we'd get another compile error when trying to invoke &lt;strong&gt;A()&lt;/strong&gt; as a member of &lt;strong&gt;C&lt;/strong&gt;:&lt;/p&gt;&lt;pre class="csharpcode"&gt;C c = &lt;span class="kwrd"&gt;new&lt;/span&gt; C();
c.A();&lt;/pre&gt;
&lt;p&gt;This time the compiler would report that class &lt;strong&gt;C&lt;/strong&gt; does not contain a definition for method &lt;strong&gt;A()&lt;/strong&gt;. We get the error because the explicit implementation of &lt;strong&gt;I1.A()&lt;/strong&gt; hides &lt;strong&gt;A()&lt;/strong&gt; from the class. The only way to call &lt;strong&gt;I1.A()&lt;/strong&gt; now is through&amp;nbsp;&lt;strong&gt;C&lt;/strong&gt;'s &lt;strong&gt;I1&lt;/strong&gt; interface:&lt;/p&gt;&lt;pre class="csharpcode"&gt;C c = &lt;span class="kwrd"&gt;new&lt;/span&gt; C();
I1 i1 = (I1)c;
i1.A();&lt;/pre&gt;
&lt;p&gt;Explicit interface implementations are&amp;nbsp;sometimes necessary when classes implement multiple interfaces with conflicting member definitions. A common example&amp;nbsp;involves collection enumerators&amp;nbsp;that implement &lt;a title="System.Collections.IEnumerator" href="http://msdn2.microsoft.com/en-us/library/system.collections.ienumerator.aspx"&gt;System.Collections.IEnumerator&lt;/a&gt; and &lt;a title="System.Collections.Generic.IEnumerator" href="http://msdn2.microsoft.com/en-us/library/78dfe2yb.aspx"&gt;System.Collections.Generic.IEnumerator&lt;t&gt;&lt;/a&gt;. &amp;nbsp;In this case both interfaces specify a get-accessor for the &lt;a title="System.Collections.IEnumerator.Current" href="http://msdn2.microsoft.com/en-us/library/system.collections.ienumerator.current.aspx"&gt;Current&lt;/a&gt; property.&amp;nbsp;To create a class that compiles, at&amp;nbsp;least one of the two get-accessors must be explicitly implemented.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hiding Interface Details. &lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In some cases explicit interface implementation can be useful even when disambiguation is unnecessary. One example is to use an explicit implementation to hide the details of an interface that the class developer considers private. While the level of privacy is not as great as that afforded by the &lt;a title="private (C# Reference)" href="http://msdn2.microsoft.com/en-us/library/st6sy9xe.aspx"&gt;private&lt;/a&gt; keyword, it can be useful in some circumstances. Listing&amp;nbsp;5 shows a common pattern involving &lt;a title="IDisposable Interface" href="http://msdn2.microsoft.com/en-us/library/system.idisposable.aspx"&gt;IDisposable&lt;/a&gt;. In this case, the &lt;strong&gt;Dispose()&lt;/strong&gt; method is hidden by explicit implementation because&amp;nbsp;the method&amp;nbsp;is really an implementation detail that is not germane to the users of class &lt;strong&gt;MyFile&lt;/strong&gt;. At the very least, the explicit implementation keeps the interface members out of the class' &lt;a title="Using IntelliSense" href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsintro7/html/vcovrautomaticstatementcompletion.asp"&gt;Intellisense&lt;/a&gt; list. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Listing 5.&amp;nbsp;Using explicit implementation to hide&amp;nbsp;the details of IDisposable.&lt;/strong&gt;&lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;interface&lt;/span&gt; IDisposable
{
    &lt;span class="kwrd"&gt;void&lt;/span&gt; Dispose();
}

&lt;span class="kwrd"&gt;class&lt;/span&gt; MyFile : IDisposable
{
    &lt;span class="kwrd"&gt;void&lt;/span&gt; IDisposable.Dispose()
    {
        Close();
    }

    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; Close()
    {
        &lt;span class="rem"&gt;// Do what's necessary to close the file&lt;/span&gt;
        System.GC.SuppressFinalize(&lt;span class="kwrd"&gt;this&lt;/span&gt;);
    }
}
&lt;/pre&gt;
&lt;p&gt;You can read more about implicit and explicit interface implementations in this &lt;a title="Explicit Interface Implementation Tutorial" href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csref/html/vcwlkExplicitInterfaceImplementationTutorial.asp"&gt;MSDN Tutorial&lt;/a&gt; or in &lt;a title="C# Language Specification 1.2" href="http://download.microsoft.com/download/5/e/5/5e58be0a-b02b-41ac-a4a3-7a22286214ff/csharp%20language%20specification%20v1.2.doc"&gt;Version 1.2 of the C# Language Specification&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://download.microsoft.com/download/5/e/5/5e58be0a-b02b-41ac-a4a3-7a22286214ff/csharp language specification v1.2.doc"&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Hope this helps.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;-Mike Hopcroft&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;a href="http://www.dotnetkicks.com/kick/?url=http://blogs.msdn.com/mhop/archive/2006/12/12/implicit-and-explicit-interface-implementations.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http://blogs.msdn.com/mhop/archive/2006/12/12/implicit-and-explicit-interface-implementations.aspx" border="0" alt="kick it on DotNetKicks.com"&gt;&lt;/a&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1272921" width="1" height="1"&gt;</content><author><name>mhop</name><uri>http://blogs.msdn.com/members/mhop.aspx</uri></author></entry><entry><title>MSDN Community Content</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/mhop/archive/2006/12/07/msdn-community-content.aspx" /><id>http://blogs.msdn.com/mhop/archive/2006/12/07/msdn-community-content.aspx</id><published>2006-12-08T10:04:57Z</published><updated>2006-12-08T10:04:57Z</updated><content type="html">&lt;p&gt;&lt;a title="MSDN" href="http://msdn2.microsoft.com/en-us/default.aspx"&gt;MSDN&lt;/a&gt; recently added Wiki functionality to the portion of the site dealing with Visual Studio 2005 and .NET Framework 2.0. This already looks like it is going to be a great resource - at the time of this post, there were almost 1000 topics with Community Content from over 300 contributors.&amp;nbsp;You can read more about it at &lt;a title="MSDN Community Content FAQ" href="http://msdn2.microsoft.com/en-us/library/communitymsdnwikifaq.aspx"&gt;MSDN Community Content FAQ&lt;/a&gt;. &lt;/p&gt; &lt;p&gt;Here's a sample screenshot of a community content block on an &lt;a title="Implement ISerializable correctly" href="http://msdn2.microsoft.com/en-us/library/ms182342(vs.80).aspx"&gt;MSDN library page&lt;/a&gt; (click the image to see a full size version).&lt;/p&gt; &lt;p&gt;&lt;a href="http://hopcroft.members.winisp.net/images/MSDNCommunityContent_14492/image03.png" atomicselection="true"&gt;&lt;img height="233" src="http://hopcroft.members.winisp.net/images/MSDNCommunityContent_14492/image0_thumb3.png" width="240"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;Hope this helps.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;-Mike&lt;/p&gt; &lt;a href="http://www.dotnetkicks.com/kick/?url=http://blogs.msdn.com/mhop/archive/2006/12/07/msdn-community-content.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http://blogs.msdn.com/mhop/archive/2006/12/07/msdn-community-content.aspx" border="0" alt="kick it on DotNetKicks.com"&gt;&lt;/a&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1237192" width="1" height="1"&gt;</content><author><name>mhop</name><uri>http://blogs.msdn.com/members/mhop.aspx</uri></author></entry><entry><title>Home Automation Part 2 – Making Basic Measurements</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/mhop/archive/2006/12/06/home-automation-part-2-making-basic-measurements.aspx" /><id>http://blogs.msdn.com/mhop/archive/2006/12/06/home-automation-part-2-making-basic-measurements.aspx</id><published>2006-12-06T12:39:00Z</published><updated>2006-12-06T12:39:00Z</updated><content type="html">&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;In the &lt;A title="Home Automation Part 1 - Burning $200 of Propane" href="http://blogs.msdn.com/mhop/archive/2006/11/27/home-automation-part-1-burning-200-of-propane.aspx" mce_href="http://blogs.msdn.com/mhop/archive/2006/11/27/home-automation-part-1-burning-200-of-propane.aspx"&gt;previous installment&lt;/A&gt; I described my decision to build a temperature and humidity sensor network using&amp;nbsp;modules from &lt;A title=IButtonLink.com href="http://www.ibuttonlink.com/" target=_blank mce_href="http://www.ibuttonlink.com/"&gt;IButtonLink.com&lt;/A&gt;. The heart&lt;FONT color=#ff0000&gt; &lt;/FONT&gt;&lt;FONT color=#000000&gt;of the system is&amp;nbsp;the &lt;/FONT&gt;&lt;A title=Link45 href="http://www.ibuttonlink.com/Link_Details.htm" target=_blank mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;&lt;FONT color=#000000&gt;Link45&lt;/FONT&gt;&lt;/A&gt;&amp;nbsp;which connects the PC’s serial port to the sensor network and provides an ASCII interface to the sensors. 
&lt;P&gt;In previous home automation projects&amp;nbsp;using .NET 1.1 I had no built-in class to access the serial port&amp;nbsp;and therefore&amp;nbsp;had to rely on solutions like VB6’s &lt;A title="VCTERM Sample: Uses the Microsoft Communications ActiveX Control" href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcsample/html/_sample_mfc_vcterm.asp" mce_href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcsample/html/_sample_mfc_vcterm.asp"&gt;MsComm32.ocx&lt;/A&gt; or &lt;A title="Corrado Cavalli's blog" href="http://blogs.ugidotnet.org/ccavalli" target=_blank mce_href="http://blogs.ugidotnet.org/ccavalli"&gt;Corrado Cavalli’s&lt;/A&gt; excellent &lt;A title="Serial Communications in VB.NET" href="http://www.codeworks.it/net/VBNetRs232.htm" target=_blank mce_href="http://www.codeworks.it/net/VBNetRs232.htm"&gt;CRs232 class&lt;/A&gt;. For this project I decided to use &lt;A title=".NET Framework Developer Center" href="http://msdn2.microsoft.com/en-us/netframework/default.aspx" mce_href="http://msdn2.microsoft.com/en-us/netframework/default.aspx"&gt;.NET Framework 2.0&lt;/A&gt; so that I could try out the new &lt;A title="SerialPort Class  (System.IO.Ports)" href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.aspx"&gt;System.IO.Ports.SerialPort&lt;/A&gt; class. 
&lt;P&gt;I needed to familiarize myself with the &lt;A title=Link45 href="http://www.ibuttonlink.com/Link_Details.htm" target=_blank mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;&lt;FONT color=#000000&gt;Link45&lt;/FONT&gt;&lt;/A&gt;&amp;nbsp;and learn how to enumerate the sensors on the network and take a reading from an individual sensor. With this in mind, I started by implementing a Super-Simple Serial Console that would let me type commands to the &lt;A title=Link45 href="http://www.ibuttonlink.com/Link_Details.htm" target=_blank mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;&lt;FONT color=#000000&gt;Link45&lt;/FONT&gt;&lt;/A&gt;&amp;nbsp;and see its responses in the console window. Most of the code was straight forward (see&amp;nbsp;Listing 1, below) but I did run into a few gotchas related to serial port settings, &lt;A title=Link45 href="http://www.ibuttonlink.com/Link_Details.htm" target=_blank mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;&lt;FONT color=#000000&gt;Link45&lt;/FONT&gt;&lt;/A&gt;&amp;nbsp;initialization, and multi-threading. 
&lt;P&gt;&lt;STRONG&gt;Listing&amp;nbsp;1. The Super-Simple Serial Console&amp;nbsp;consists of&amp;nbsp;about&amp;nbsp;100 lines&amp;nbsp;of code.&lt;/STRONG&gt;&lt;/P&gt;
&lt;DIV class=csharpcode&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   1:  &lt;/SPAN&gt;&lt;SPAN class=kwrd&gt;using&lt;/SPAN&gt; System;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   2:  &lt;/SPAN&gt;&lt;SPAN class=kwrd&gt;using&lt;/SPAN&gt; System.IO.Ports;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   3:  &lt;/SPAN&gt;&lt;SPAN class=kwrd&gt;using&lt;/SPAN&gt; System.Threading;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   4:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   5:  &lt;/SPAN&gt;&lt;SPAN class=kwrd&gt;namespace&lt;/SPAN&gt; s3c&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   6:  &lt;/SPAN&gt;{&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   7:  &lt;/SPAN&gt;    &lt;SPAN class=rem&gt;// Super-simple Serial Console&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   8:  &lt;/SPAN&gt;    &lt;SPAN class=rem&gt;// Provides an interactive console session with an iButtonLink Link45.&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   9:  &lt;/SPAN&gt;    &lt;SPAN class=kwrd&gt;class&lt;/SPAN&gt; S3C&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  10:  &lt;/SPAN&gt;    {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  11:  &lt;/SPAN&gt;        &lt;SPAN class=kwrd&gt;private&lt;/SPAN&gt; &lt;SPAN class=kwrd&gt;int&lt;/SPAN&gt; nCharsReceived;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  12:  &lt;/SPAN&gt;        &lt;SPAN class=kwrd&gt;private&lt;/SPAN&gt; SerialPort port;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  13:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  14:  &lt;/SPAN&gt;        &lt;SPAN class=kwrd&gt;public&lt;/SPAN&gt; &lt;SPAN class=kwrd&gt;void&lt;/SPAN&gt; Run()&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  15:  &lt;/SPAN&gt;        {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  16:  &lt;/SPAN&gt;            Console.WriteLine(&lt;SPAN class=str&gt;"Super-Simple Serial Console"&lt;/SPAN&gt;);&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  17:  &lt;/SPAN&gt;            &lt;SPAN class=kwrd&gt;try&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  18:  &lt;/SPAN&gt;            {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  19:  &lt;/SPAN&gt;                Open();&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  20:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  21:  &lt;/SPAN&gt;                &lt;SPAN class=kwrd&gt;while&lt;/SPAN&gt; (&lt;SPAN class=kwrd&gt;true&lt;/SPAN&gt;)&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  22:  &lt;/SPAN&gt;                {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  23:  &lt;/SPAN&gt;                    &lt;SPAN class=rem&gt;// Show &amp;gt; prompt, read line from console and transmit to serial port.&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  24:  &lt;/SPAN&gt;                    Console.Write(&lt;SPAN class=str&gt;"&amp;gt;"&lt;/SPAN&gt;);&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  25:  &lt;/SPAN&gt;                    String line = System.Console.ReadLine();&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  26:  &lt;/SPAN&gt;                    &lt;SPAN class=kwrd&gt;if&lt;/SPAN&gt; (line.Length == 0)&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  27:  &lt;/SPAN&gt;                        &lt;SPAN class=kwrd&gt;return&lt;/SPAN&gt;;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  28:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  29:  &lt;/SPAN&gt;                    nCharsReceived = 0;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  30:  &lt;/SPAN&gt;                    port.Write(line);&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  31:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  32:  &lt;/SPAN&gt;                    &lt;SPAN class=rem&gt;// Wait until 200ms after the last data received from port before &lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  33:  &lt;/SPAN&gt;                    &lt;SPAN class=rem&gt;// prompting for next line.&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  34:  &lt;/SPAN&gt;                    &lt;SPAN class=kwrd&gt;while&lt;/SPAN&gt; (&lt;SPAN class=kwrd&gt;true&lt;/SPAN&gt;)&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  35:  &lt;/SPAN&gt;                    {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  36:  &lt;/SPAN&gt;                        &lt;SPAN class=kwrd&gt;int&lt;/SPAN&gt; n = nCharsReceived;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  37:  &lt;/SPAN&gt;                        Thread.Sleep(200);&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  38:  &lt;/SPAN&gt;                        &lt;SPAN class=kwrd&gt;if&lt;/SPAN&gt; (n == nCharsReceived)&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  39:  &lt;/SPAN&gt;                            &lt;SPAN class=kwrd&gt;break&lt;/SPAN&gt;;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  40:  &lt;/SPAN&gt;                    }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  41:  &lt;/SPAN&gt;                }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  42:  &lt;/SPAN&gt;            }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  43:  &lt;/SPAN&gt;            &lt;SPAN class=kwrd&gt;finally&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  44:  &lt;/SPAN&gt;            {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  45:  &lt;/SPAN&gt;                &lt;SPAN class=kwrd&gt;if&lt;/SPAN&gt; (port != &lt;SPAN class=kwrd&gt;null&lt;/SPAN&gt; &amp;amp;&amp;amp; port.IsOpen)&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  46:  &lt;/SPAN&gt;                    port.Close();&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  47:  &lt;/SPAN&gt;            }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  48:  &lt;/SPAN&gt;        }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  49:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  50:  &lt;/SPAN&gt;        &lt;SPAN class=kwrd&gt;public&lt;/SPAN&gt; &lt;SPAN class=kwrd&gt;void&lt;/SPAN&gt; Open()&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  51:  &lt;/SPAN&gt;        {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  52:  &lt;/SPAN&gt;            port = &lt;SPAN class=kwrd&gt;new&lt;/SPAN&gt; SerialPort(&lt;SPAN class=str&gt;"COM1"&lt;/SPAN&gt;, 9600, Parity.None, 8, StopBits.One);&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  53:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  54:  &lt;/SPAN&gt;            &lt;SPAN class=rem&gt;// The iButton Link45 needs DTR and RTS enabled.&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  55:  &lt;/SPAN&gt;            port.DtrEnable = &lt;SPAN class=kwrd&gt;true&lt;/SPAN&gt;;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  56:  &lt;/SPAN&gt;            port.RtsEnable = &lt;SPAN class=kwrd&gt;true&lt;/SPAN&gt;;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  57:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  58:  &lt;/SPAN&gt;            &lt;SPAN class=rem&gt;// Hook up callback to display data received from Link45&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  59:  &lt;/SPAN&gt;            port.DataReceived += &lt;SPAN class=kwrd&gt;new&lt;/SPAN&gt; SerialDataReceivedEventHandler(port_DataReceived);&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  60:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  61:  &lt;/SPAN&gt;            &lt;SPAN class=rem&gt;// The serial port is now configured and ready to be opened.&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  62:  &lt;/SPAN&gt;            port.Open();&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  63:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  64:  &lt;/SPAN&gt;            &lt;SPAN class=rem&gt;// Shock the Link45 to life by sending a break.&lt;/SPAN&gt;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  65:  &lt;/SPAN&gt;            port.BreakState = &lt;SPAN class=kwrd&gt;true&lt;/SPAN&gt;;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  66:  &lt;/SPAN&gt;            port.BreakState = &lt;SPAN class=kwrd&gt;false&lt;/SPAN&gt;;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  67:  &lt;/SPAN&gt;        }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  68:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  69:  &lt;/SPAN&gt;        &lt;SPAN class=kwrd&gt;public&lt;/SPAN&gt; &lt;SPAN class=kwrd&gt;void&lt;/SPAN&gt; Close()&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  70:  &lt;/SPAN&gt;        {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  71:  &lt;/SPAN&gt;            port.Close();&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  72:  &lt;/SPAN&gt;        }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  73:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  74:  &lt;/SPAN&gt;        &lt;SPAN class=kwrd&gt;void&lt;/SPAN&gt; port_DataReceived(&lt;SPAN class=kwrd&gt;object&lt;/SPAN&gt; sender, SerialDataReceivedEventArgs e)&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  75:  &lt;/SPAN&gt;        {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  76:  &lt;/SPAN&gt;            &lt;SPAN class=kwrd&gt;string&lt;/SPAN&gt; s = port.ReadExisting();&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  77:  &lt;/SPAN&gt;            &lt;SPAN class=kwrd&gt;if&lt;/SPAN&gt; (s != &lt;SPAN class=kwrd&gt;null&lt;/SPAN&gt;)&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  78:  &lt;/SPAN&gt;            {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  79:  &lt;/SPAN&gt;                nCharsReceived += s.Length;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  80:  &lt;/SPAN&gt;                Console.Write(s);&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  81:  &lt;/SPAN&gt;            }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  82:  &lt;/SPAN&gt;        }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  83:  &lt;/SPAN&gt;    }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  84:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  85:  &lt;/SPAN&gt;    &lt;SPAN class=kwrd&gt;class&lt;/SPAN&gt; Program&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  86:  &lt;/SPAN&gt;    {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  87:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  88:  &lt;/SPAN&gt;        &lt;SPAN class=kwrd&gt;static&lt;/SPAN&gt; &lt;SPAN class=kwrd&gt;void&lt;/SPAN&gt; Main(&lt;SPAN class=kwrd&gt;string&lt;/SPAN&gt;[] args)&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  89:  &lt;/SPAN&gt;        {&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  90:  &lt;/SPAN&gt;            S3C s3c = &lt;SPAN class=kwrd&gt;new&lt;/SPAN&gt; S3C();&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  91:  &lt;/SPAN&gt;            s3c.Run();&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  92:  &lt;/SPAN&gt;        }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  93:  &lt;/SPAN&gt;&amp;nbsp;&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  94:  &lt;/SPAN&gt;    }&lt;/PRE&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;  95:  &lt;/SPAN&gt;}&lt;/PRE&gt;&lt;/DIV&gt;
&lt;STYLE type=text/css&gt;.csharpcode {
	FONT-SIZE: small; COLOR: black; FONT-FAMILY: consolas, "Courier New", courier, monospace; BACKGROUND-COLOR: #ffffff
}
.csharpcode PRE {
	FONT-SIZE: small; COLOR: black; FONT-FAMILY: consolas, "Courier New", courier, monospace; BACKGROUND-COLOR: #ffffff
}
.csharpcode PRE {
	MARGIN: 0em
}
.csharpcode .rem {
	COLOR: #008000
}
.csharpcode .kwrd {
	COLOR: #0000ff
}
.csharpcode .str {
	COLOR: #006080
}
.csharpcode .op {
	COLOR: #0000c0
}
.csharpcode .preproc {
	COLOR: #cc6633
}
.csharpcode .asp {
	BACKGROUND-COLOR: #ffff00
}
.csharpcode .html {
	COLOR: #800000
}
.csharpcode .attr {
	COLOR: #ff0000
}
.csharpcode .alt {
	MARGIN: 0em; WIDTH: 100%; BACKGROUND-COLOR: #f4f4f4
}
.csharpcode .lnum {
	COLOR: #606060
}
&lt;/STYLE&gt;

&lt;STYLE type=text/css&gt;.csharpcode {
	FONT-SIZE: small; COLOR: black; FONT-FAMILY: consolas, "Courier New", courier, monospace; BACKGROUND-COLOR: #ffffff
}
.csharpcode PRE {
	FONT-SIZE: small; COLOR: black; FONT-FAMILY: consolas, "Courier New", courier, monospace; BACKGROUND-COLOR: #ffffff
}
.csharpcode PRE {
	MARGIN: 0em
}
.csharpcode .rem {
	COLOR: #008000
}
.csharpcode .kwrd {
	COLOR: #0000ff
}
.csharpcode .str {
	COLOR: #006080
}
.csharpcode .op {
	COLOR: #0000c0
}
.csharpcode .preproc {
	COLOR: #cc6633
}
.csharpcode .asp {
	BACKGROUND-COLOR: #ffff00
}
.csharpcode .html {
	COLOR: #800000
}
.csharpcode .attr {
	COLOR: #ff0000
}
.csharpcode .alt {
	MARGIN: 0em; WIDTH: 100%; BACKGROUND-COLOR: #f4f4f4
}
.csharpcode .lnum {
	COLOR: #606060
}
&lt;/STYLE&gt;

&lt;P mce_keep="true"&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Let's walk through the code in execution-order. The program entry point is the Main() function at&amp;nbsp;at line 88. Main() just news up an S3C object (S3C is short for Super-Simple Serial Console) and calls it's Run() method.&amp;nbsp;The Run() method does four things:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;prints a welcome message at line 16. 
&lt;LI&gt;initializes the serial port by calling Open() at line 19. 
&lt;LI&gt;performs the main input and&amp;nbsp;serial port write loop in lines 21-41. 
&lt;LI&gt;closes the serial port in lines 43-47.&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;&lt;STRONG&gt;Initializing the Serial Port.&lt;/STRONG&gt; My first challenge was getting the serial port settings correct&amp;nbsp;in the Open() method, lines 50-67. The &lt;A title=Link45 href="http://www.ibuttonlink.com/Link_Details.htm" target=_blank mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;&lt;FONT color=#000000&gt;Link45&lt;/FONT&gt;&lt;/A&gt;&amp;nbsp;had some of the port settings conveniently printed on the side of the device. These included 9600 baud, no parity and 8 bits of data. I figured out the stop bits by trial and error. All of these values were passed to the constructor of the &lt;A title="SerialPort Class (System.IO.Ports)" href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.aspx"&gt;System.IO.Ports.SerialPort&lt;/A&gt; in line 52. I also found that I needed to enable &lt;A title="DTR Enable Property" href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.dtrenable.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.dtrenable.aspx"&gt;DTR&lt;/A&gt; and &lt;A title="RTS Enable Property" href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.rtsenable.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.rtsenable.aspx"&gt;RTS&lt;/A&gt;&amp;nbsp;which I do on lines 55 and 56.&amp;nbsp;If you want to learn&amp;nbsp;more about serial port esoterics in general, check out &lt;A title="Wikipedia RS232" href="http://en.wikipedia.org/wiki/RS232" mce_href="http://en.wikipedia.org/wiki/RS232"&gt;Wikipedia RS323&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;After creating and configuring the serial port, I wire up a handler for the port's &lt;A title="SerialPort.DataReceived Event" href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.datareceived.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.datareceived.aspx"&gt;DataReceived&lt;/A&gt; event on line 59. This is where the multithreading comes in. Serial port communications are inherently asynchronous - once you transmit some data your main thread continues on to do other work and at some point in the future when a response arrives back at the port, the &lt;A title="SerialPort.DataReceived Event" href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.datareceived.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.datareceived.aspx"&gt;DataReceived&lt;/A&gt;&amp;nbsp;event is fired on another thread, which results in a call to port_DataReceived() which is defined on line 74.&lt;/P&gt;
&lt;P&gt;Once the event handler is wired up, it is time to actually open the serial port (line 62). I thought I was done configuring the port at this point, but I found in my experiments that some Link45 commands such as the help command would sometimes fail if issued before a sensor read command. I ended up fixing this problem by toggling the serial port's&amp;nbsp;&lt;A title="BreakState Property" href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.breakstate.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.breakstate.aspx"&gt;break state&lt;/A&gt; to true and then back to false (lines 65-66). This seems to have the effect of resetting the communication channel to the Link45 and ensures that all of the Link45 commands work consistently the first time.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;The Main Loop.&lt;/STRONG&gt; After initializing the serial port, we enter the main loop on line 21. The loop runs until an empty input line is detected (lines 26-27) or an exception is thrown (lines 43-47).&amp;nbsp;At the&amp;nbsp;top of the loop we&amp;nbsp;print a prompt and get user input (lines 24-25) and then send the user's text to the serial port (line 30). After sending the text, we wait a brief period for a response to be displayed (lines 34-40) and then head back to the top of the loop to prompt the user for more input.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;A Bit of Multithreading.&lt;/STRONG&gt; Responses from the Link45 trigger the serial port's &lt;A title="SerialPort.DataReceived Event" href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.datareceived.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.datareceived.aspx"&gt;DataReceived&lt;/A&gt; event. For the Super-Simple Serial Console, the role of the &lt;A title="SerialPort.DataReceived Event" href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.datareceived.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.datareceived.aspx"&gt;DataReceived&lt;/A&gt; event handler is simple - it&amp;nbsp;just reads any data that is available in the serial port's input buffer and then prints that data to the console. This all happens on lines 74-82. The only part that isn't completely straight forward is line 79: &lt;/P&gt;
&lt;DIV class=csharpcode&gt;&lt;PRE&gt;&lt;SPAN class=lnum&gt;   79:  &lt;/SPAN&gt;                nCharsReceived += s.Length;&lt;/PRE&gt;&lt;/DIV&gt;
&lt;P&gt;This is where the &lt;A title="SerialPort.DataReceived Event" href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.datareceived.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.datareceived.aspx"&gt;DataReceived&lt;/A&gt; thread communicates with the main thread.&amp;nbsp;The reason we need some sort of handshaking between the threads is that we'd like the serial port responses to be interleaved with the user input - basically a repeating prompt-input-response pattern. Without this handshaking, the prompt for the next command would be printed before the response from the current command.&lt;/P&gt;
&lt;P&gt;One way to accomplish&amp;nbsp;this interleaving is to blindly Sleep() for a fixed amount of time in the main loop after every command is transmitted to the serial port. This approach will work with a long enough sleep, but it leads to a sluggish user experience because&amp;nbsp;every Sleep() needs to be long enough to wait for the slowest &lt;A title=Link45 href="http://www.ibuttonlink.com/Link_Details.htm" target=_blank mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;&lt;FONT color=#000000&gt;Link45&lt;/FONT&gt;&lt;/A&gt;&amp;nbsp;command.&amp;nbsp;Experiments showed that the &lt;A title=Link45 href="http://www.ibuttonlink.com/Link_Details.htm" target=_blank mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;&lt;FONT color=#000000&gt;Link45&lt;/FONT&gt;&lt;/A&gt;&amp;nbsp;was always pretty responsive, but that some commands resulted in a verbose response that could take a second or more&amp;nbsp;to receive. &lt;/P&gt;
&lt;P&gt;My approach was to wait 200ms beyond the time the last character was received. If, after 200ms, no additional characters had been received, I would assume the response was complete and head back up to the top of the loop for more user input. I used the &lt;STRONG&gt;&lt;EM&gt;nCharsReceived&lt;/EM&gt;&lt;/STRONG&gt; variable to track the number of characters received while waiting. I clear &lt;STRONG&gt;&lt;EM&gt;nCharsReceived&lt;/EM&gt;&lt;/STRONG&gt; (line 29) immediately before writing to the serial port. The &lt;A title="SerialPort.DataReceived Event" href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.datareceived.aspx" target=_blank mce_href="http://msdn2.microsoft.com/en-us/library/system.io.ports.serialport.datareceived.aspx"&gt;DataReceived&lt;/A&gt; event handler updates &lt;STRONG&gt;&lt;EM&gt;nCharsReceived&lt;/EM&gt;&lt;/STRONG&gt; on line 79. Lines 34-39 in the main loop alternately Sleep() for 200ms and check &lt;EM&gt;&lt;STRONG&gt;nCharsReceived&lt;/STRONG&gt;&lt;/EM&gt; for more data until a Sleep() occurs with no new data received. Technically speaking one should probably protect &lt;STRONG&gt;&lt;EM&gt;nCharsReceived&lt;/EM&gt;&lt;/STRONG&gt; with a lock in case we ever encounter an architecture where integer writes aren't atomic.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Putting It All Together.&lt;/STRONG&gt; Once I had the Super-Simple Serial Console working, I proceeded to wire up a pair of sensors to the serial port in the back of my laptop. The connections were easy since the sensors&amp;nbsp;have RJ-45 connectors. Figure&amp;nbsp;1 shows the setup. 
&lt;P&gt;&lt;A href="http://hopcroft.members.winisp.net/images/HomeAutomationPart2MakingBasicMeasuremen_9C3B/SensorsAndLaptop2.jpg" atomicselection="true" mce_href="http://hopcroft.members.winisp.net/images/HomeAutomationPart2MakingBasicMeasuremen_9C3B/SensorsAndLaptop2.jpg"&gt;&lt;IMG height=480 src="http://hopcroft.members.winisp.net/images/HomeAutomationPart2MakingBasicMeasuremen_9C3B/SensorsAndLaptop_thumb2.jpg" width=640 mce_src="http://hopcroft.members.winisp.net/images/HomeAutomationPart2MakingBasicMeasuremen_9C3B/SensorsAndLaptop_thumb2.jpg"&gt;&lt;/A&gt; 
&lt;P&gt;&lt;STRONG&gt;Figure 1.&amp;nbsp; CAT-5 networking cable connects a pair&amp;nbsp;of sensors to the serial port via the &lt;A title=Link45 href="http://www.ibuttonlink.com/Link_Details.htm" target=_blank mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;&lt;FONT color=#000000&gt;Link45&lt;/FONT&gt;&lt;/A&gt;.&lt;/STRONG&gt; 
&lt;P&gt;After plugging in the sensors I fired up the Super-Simple Serial Console. I began by typing the character &lt;B&gt;h&lt;/B&gt; to see the list of commands available. The transcript in Figure&amp;nbsp;2 shows&amp;nbsp;the response from the &lt;A title=Link45 href="http://www.ibuttonlink.com/Link_Details.htm" target=_blank mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;&lt;FONT color=#000000&gt;Link45&lt;/FONT&gt;&lt;/A&gt;. You can read more about the available commands in the &lt;A title="LinkTH Users Manual V10e" href="http://www.ibuttonlink.com/PDFs/LinkTH%20Users%20Manual%20V10e.pdf." mce_href="http://www.ibuttonlink.com/PDFs/LinkTH%20Users%20Manual%20V10e.pdf."&gt;Link45 Users Manual&lt;/A&gt;.&amp;nbsp;&amp;nbsp; 
&lt;P&gt;&lt;A href="http://hopcroft.members.winisp.net/images/HomeAutomationPart2MakingBasicMeasuremen_9C3B/image01.png" atomicselection="true" mce_href="http://hopcroft.members.winisp.net/images/HomeAutomationPart2MakingBasicMeasuremen_9C3B/image01.png"&gt;&lt;IMG height=464 src="http://hopcroft.members.winisp.net/images/HomeAutomationPart2MakingBasicMeasuremen_9C3B/image0_thumb1.png" width=709 mce_src="http://hopcroft.members.winisp.net/images/HomeAutomationPart2MakingBasicMeasuremen_9C3B/image0_thumb1.png"&gt;&lt;/A&gt; &lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Figure 2. First communications with the Link45.&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;To get the list of sensors on the network I typed the character &lt;STRONG&gt;I&lt;/STRONG&gt;,&amp;nbsp;requesting that the &lt;A title=Link45 href="http://www.ibuttonlink.com/Link_Details.htm" target=_blank mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;&lt;FONT color=#000000&gt;Link45&lt;/FONT&gt;&lt;/A&gt;&amp;nbsp;report the sensor inventory. The results are shown in Figure 3. Each sensor&amp;nbsp;appears on&amp;nbsp;a line consisting of a 16 hex digit sensor ID followed by a 2 digit sensor type. My network shows two temperature+humidity sensors. Here’s the cheat sheet for the sensor types: &lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;00 – temperature 
&lt;LI&gt;19 – temperature + humidity 
&lt;LI&gt;1A – temperature + voltage 
&lt;LI&gt;1B – temperature + light&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;&lt;A href="http://hopcroft.members.winisp.net/images/HomeAutomationPart2MakingBasicMeasuremen_9C3B/image03.png" atomicselection="true" mce_href="http://hopcroft.members.winisp.net/images/HomeAutomationPart2MakingBasicMeasuremen_9C3B/image03.png"&gt;&lt;IMG height=204 src="http://hopcroft.members.winisp.net/images/HomeAutomationPart2MakingBasicMeasuremen_9C3B/image0_thumb3.png" width=707 mce_src="http://hopcroft.members.winisp.net/images/HomeAutomationPart2MakingBasicMeasuremen_9C3B/image0_thumb3.png"&gt;&lt;/A&gt; &lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Figure 3. The I command reports the list of sensors on the network.&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Finally, I used the &lt;STRONG&gt;D&lt;/STRONG&gt; command to&amp;nbsp;request temperature and humidity readings from all sensors on the network. Figure&amp;nbsp;4 shows the results. According to the &lt;A title=Link45 href="http://www.ibuttonlink.com/Link_Details.htm" target=_blank mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;&lt;FONT color=#000000&gt;Link45&lt;/FONT&gt;&lt;/A&gt;&amp;nbsp;documentation, the data returned for each sensor is of the form &lt;/P&gt;
&lt;BLOCKQUOTE&gt;
&lt;P&gt;xxxxxxxxxxxxxxxx ss,ccc.cc,fff.ff,hhh &lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;where 
&lt;UL&gt;
&lt;LI&gt;&lt;EM&gt;xxxxxxxxxxxxxxxx&lt;/EM&gt; is a 16 digit hexadecimal sensor ID 
&lt;LI&gt;&lt;EM&gt;ss&lt;/EM&gt; is the sensor type 
&lt;LI&gt;&lt;EM&gt;ccc.cc&lt;/EM&gt; is the temperature in degrees Celsius 
&lt;LI&gt;&lt;EM&gt;fff.ff&lt;/EM&gt; is the temperature in degrees Fahrenheit 
&lt;LI&gt;&lt;EM&gt;hhh&lt;/EM&gt; is the humidity&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;My network shows that the temperatures near my computer are in the mid 60s and with humidity around 44%. 
&lt;P&gt;&lt;A href="http://hopcroft.members.winisp.net/images/HomeAutomationPart2MakingBasicMeasuremen_9C3B/image05.png" atomicselection="true" mce_href="http://hopcroft.members.winisp.net/images/HomeAutomationPart2MakingBasicMeasuremen_9C3B/image05.png"&gt;&lt;IMG height=126 src="http://hopcroft.members.winisp.net/images/HomeAutomationPart2MakingBasicMeasuremen_9C3B/image0_thumb5.png" width=706 mce_src="http://hopcroft.members.winisp.net/images/HomeAutomationPart2MakingBasicMeasuremen_9C3B/image0_thumb5.png"&gt;&lt;/A&gt; 
&lt;P&gt;&lt;STRONG&gt;Figure 4. The D command takes temperature and humidity readings from all sensors.&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;The &lt;A title=Link45 href="http://www.ibuttonlink.com/Link_Details.htm" target=_blank mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;&lt;FONT color=#000000&gt;Link45&lt;/FONT&gt;&lt;/A&gt;&amp;nbsp;has a lot of functionality and accepts many more commands than the ones I've shown here. You can learn more from the &lt;A title="LinkTH Users Manual V10e" href="http://www.ibuttonlink.com/PDFs/LinkTH%20Users%20Manual%20V10e.pdf." mce_href="http://www.ibuttonlink.com/PDFs/LinkTH%20Users%20Manual%20V10e.pdf."&gt;Link45 Users Manual&lt;/A&gt;.&amp;nbsp;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;With the code from the Super-Simple Serial Console and the &lt;A title=Link45 href="http://www.ibuttonlink.com/Link_Details.htm" target=_blank mce_href="http://www.ibuttonlink.com/Link_Details.htm"&gt;&lt;FONT color=#000000&gt;Link45&lt;/FONT&gt;&lt;/A&gt;&amp;nbsp;commands for sensor inventory and sensor reading, we have all the building blocks we need to write a program to monitor the temperature and humidity.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Next installment: &lt;/STRONG&gt;&lt;A class="" id=bp___ctl00___RecentPosts___postlist___EntryItems_ctl00_PostTitle title="Home Automation Part 3 - Using Regular Expressions to Parse Sensor Data " href="http://blogs.msdn.com/mhop/archive/2007/01/07/home-automation-part-3-using-regular-expressions-to-parse-sensor-data.aspx" mce_href="http://blogs.msdn.com/mhop/archive/2007/01/07/home-automation-part-3-using-regular-expressions-to-parse-sensor-data.aspx"&gt;&lt;FONT color=#444433&gt;Using Regular Expressions to Parse Sensor Data&lt;/FONT&gt;&lt;/A&gt;&lt;/P&gt;&lt;A href="http://www.dotnetkicks.com/kick/?url=http://blogs.msdn.com/mhop/archive/2006/12/06/home-automation-part-2-making-basic-measurements.aspx" mce_href="http://www.dotnetkicks.com/kick/?url=http://blogs.msdn.com/mhop/archive/2006/12/06/home-automation-part-2-making-basic-measurements.aspx"&gt;&lt;IMG alt="kick it on DotNetKicks.com" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http://blogs.msdn.com/mhop/archive/2006/12/06/home-automation-part-2-making-basic-measurements.aspx" border=0 mce_src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http://blogs.msdn.com/mhop/archive/2006/12/06/home-automation-part-2-making-basic-measurements.aspx"&gt;&lt;/A&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1219306" width="1" height="1"&gt;</content><author><name>mhop</name><uri>http://blogs.msdn.com/members/mhop.aspx</uri></author><category term="Home Automation" scheme="http://blogs.msdn.com/mhop/archive/tags/Home+Automation/default.aspx" /></entry><entry><title>MSDN Online Chat: LINQ to SQL</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/mhop/archive/2006/12/03/msdn-online-chat-linq-to-sql.aspx" /><id>http://blogs.msdn.com/mhop/archive/2006/12/03/msdn-online-chat-linq-to-sql.aspx</id><published>2006-12-04T06:45:20Z</published><updated>2006-12-04T06:45:20Z</updated><content type="html">&lt;p&gt;Members of the C# team will be hosting an online chat session about &lt;a title="The LINQ Project" href="http://msdn2.microsoft.com/en-us/netframework/aa904594.aspx"&gt;LINQ to SQL&lt;/a&gt; on Tuesday, December 12, 2006. Here's the description:&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;The LINQ Project is a codename for a set of extensions to the .NET Framework that encompass language-integrated query, set, and transform operations. LINQ to SQL is a component of LINQ which allows access to data stored in relational databases. We’re actively designing and developing this technology - show up and join the LINQ to SQL team to participate in that process or just get a better understanding!&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;For more information go to &lt;a title="http://msdn.microsoft.com/chats/" href="http://msdn.microsoft.com/chats/"&gt;http://msdn.microsoft.com/chats/&lt;/a&gt;. &lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1202511" width="1" height="1"&gt;</content><author><name>mhop</name><uri>http://blogs.msdn.com/members/mhop.aspx</uri></author><category term="Events" scheme="http://blogs.msdn.com/mhop/archive/tags/Events/default.aspx" /></entry><entry><title>New For VS2005 - Abstract Stub Generation</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/mhop/archive/2006/11/30/new-for-vs2005-abstract-stub-generation.aspx" /><id>http://blogs.msdn.com/mhop/archive/2006/11/30/new-for-vs2005-abstract-stub-generation.aspx</id><published>2006-12-01T05:52:00Z</published><updated>2006-12-01T05:52:00Z</updated><content type="html">&lt;p&gt;&lt;a title="Visual Studio 2005" href="http://msdn.microsoft.com/vstudio/vshome.aspx"&gt;Visual Studio 2005&lt;/a&gt; includes a new feature to generate stubs for methods in an &lt;a title="Abstract and sealed classes and class members (C# programming guide)" href="http://msdn2.microsoft.com/en-us/library/ms173150.aspx"&gt;abstract base class&lt;/a&gt;. Here's an example of how it works. &lt;/p&gt; &lt;p&gt;Suppose you want to create a class called MyDerivedClass that inherits from an abstract class called MyAbstractClass:&lt;/p&gt; &lt;p&gt;&lt;a href="http://hopcroft.members.winisp.net/images/NewForVS2005AbstractStubGeneration_C327/image056.png" atomicselection="true"&gt;&lt;img height="142" src="http://hopcroft.members.winisp.net/images/NewForVS2005AbstractStubGeneration_C327/image0_thumb34.png" width="408"&gt;&lt;/a&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Before &lt;a title="Visual Studio 2005" href="http://msdn.microsoft.com/vstudio/vshome.aspx"&gt;Visual Studio 2005&lt;/a&gt;, you would have had to look up and then type in&amp;nbsp;the function&amp;nbsp;prototypes for each of the abstract methods in the&amp;nbsp;inheritance chain for MyDerivedClass. Just finding these methods to implement can be hard because the inheritance chain may include multiple abstract classes and any ancestor class can implement any abstract method. If you had the source code to all of the classes in the inheritance chain you could figure out which&amp;nbsp;methods were still abstract for your class&amp;nbsp;and then&amp;nbsp;go to their definitions and copy-paste the function prototypes, replacing the &lt;strong&gt;&lt;em&gt;&lt;a title="abstract (C# reference)" href="http://msdn2.microsoft.com/en-us/library/sf985hc5.aspx"&gt;abstract&lt;/a&gt;&lt;/em&gt;&lt;/strong&gt; keyword with &lt;em&gt;&lt;strong&gt;&lt;a title="override (C# reference)" href="http://msdn2.microsoft.com/en-us/library/ebca9ah3.aspx"&gt;override&lt;/a&gt;. &lt;/strong&gt;&lt;/em&gt;If you didn't have the source code for the entire inheritance chain, you might end up using the Object viewer to identify and copy the function definitions. Either way, you would be in for a bit of work.&lt;/p&gt; &lt;p&gt;In &lt;a title="Visual Studio 2005" href="http://msdn.microsoft.com/vstudio/vshome.aspx"&gt;Visual Studio 2005&lt;/a&gt;, a small blue rectangle called a smart tag appears whenever you start to declare a class that derives from an abstract type:&lt;/p&gt; &lt;p&gt;&lt;a href="http://hopcroft.members.winisp.net/images/NewForVS2005AbstractStubGeneration_C327/image058.png" atomicselection="true"&gt;&lt;img height="200" src="http://hopcroft.members.winisp.net/images/NewForVS2005AbstractStubGeneration_C327/image0_thumb36.png" width="408"&gt;&lt;/a&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Hovering over the tag or pressing Shift+Alt+F10 will produce a menu of alternatives for implementing the abstract methods:&lt;/p&gt; &lt;p&gt;&lt;a href="http://hopcroft.members.winisp.net/images/NewForVS2005AbstractStubGeneration_C327/image060.png" atomicselection="true"&gt;&lt;img height="191" src="http://hopcroft.members.winisp.net/images/NewForVS2005AbstractStubGeneration_C327/image0_thumb38.png" width="510"&gt;&lt;/a&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Clicking on the down arrow opens the menu, which in this example shows a single option:&lt;/p&gt; &lt;p&gt;&lt;a href="http://hopcroft.members.winisp.net/images/NewForVS2005AbstractStubGeneration_C327/image066.png" atomicselection="true"&gt;&lt;img height="202" src="http://hopcroft.members.winisp.net/images/NewForVS2005AbstractStubGeneration_C327/image0_thumb44.png" width="514"&gt;&lt;/a&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Invoking this option implements stubs for&amp;nbsp;the overrides&amp;nbsp;of the abstract methods as follows:&lt;/p&gt; &lt;p&gt;&lt;a href="http://hopcroft.members.winisp.net/images/NewForVS2005AbstractStubGeneration_C327/image064.png" atomicselection="true"&gt;&lt;img height="332" src="http://hopcroft.members.winisp.net/images/NewForVS2005AbstractStubGeneration_C327/image0_thumb42.png" width="651"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;It’s that easy. Now all you need to do is fill in the implementation for each method and you are up and running.&lt;/p&gt; &lt;p&gt;Hope this helps.&lt;/p&gt; &lt;p&gt;Mike&amp;nbsp;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt;  &lt;a href="http://www.dotnetkicks.com/kick/?url=http://blogs.msdn.com/mhop/archive/2006/11/30/new-for-vs2005-abstract-stub-generation.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http://blogs.msdn.com/mhop/archive/2006/11/30/new-for-vs2005-abstract-stub-generation.aspx" border="0" alt="kick it on DotNetKicks.com"&gt;&lt;/a&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1173727" width="1" height="1"&gt;</content><author><name>mhop</name><uri>http://blogs.msdn.com/members/mhop.aspx</uri></author></entry><entry><title>VS 2005 IDE Tour</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/mhop/archive/2006/11/30/vs-2005-ide-tour.aspx" /><id>http://blogs.msdn.com/mhop/archive/2006/11/30/vs-2005-ide-tour.aspx</id><published>2006-11-30T07:57:24Z</published><updated>2006-11-30T07:57:24Z</updated><content type="html">&lt;p&gt;&lt;a title="Karen's Space" href="http://blogs.msdn.com/karenliu/default.aspx"&gt;Karen Liu&lt;/a&gt;,&amp;nbsp;a C#&amp;nbsp;IDE program manager recently sent me the specs for all of the new &lt;a title="Visual C# and Visual Studio IDE" href="http://msdn2.microsoft.com/en-us/vstudio/aa336807.aspx"&gt;C# IDE&lt;/a&gt; features in &lt;a title="Visual Studio 2005" href="http://msdn2.microsoft.com/en-us/vstudio/aa973782.aspx"&gt;Visual Studio 2005&lt;/a&gt;. As part of my rampup as the new C# PUM, I plan to make my way through this list and blog about some of the more interesting changes. Watch this space for articles tagged &lt;a title="VS 2005 Tag" href="http://blogs.msdn.com/mhop/archive/tags/VS+2005/default.aspx"&gt;'VS 2005'&lt;/a&gt; and &lt;a title="IDE Tag" href="http://blogs.msdn.com/mhop/archive/tags/IDE/default.aspx"&gt;'IDE'&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Hope this helps.&lt;/p&gt; &lt;p&gt;Mike&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1175919" width="1" height="1"&gt;</content><author><name>mhop</name><uri>http://blogs.msdn.com/members/mhop.aspx</uri></author></entry><entry><title>Home Automation Part 1 - Burning $200 of propane</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/mhop/archive/2006/11/27/home-automation-part-1-burning-200-of-propane.aspx" /><id>http://blogs.msdn.com/mhop/archive/2006/11/27/home-automation-part-1-burning-200-of-propane.aspx</id><published>2006-11-28T06:50:00Z</published><updated>2006-11-28T06:50:00Z</updated><content type="html">&lt;p&gt;&lt;/p&gt; &lt;p&gt;I like to use my knowledge of technology to solve problems outside of work. In this series I explain how I used C# and the Internet to remotely monitor a propane stove in my cabin. While reading about my adventures you will learn about C#, embedded technology, regular expressions, databases, web servers, web cams and much more.  &lt;p&gt;A few years back my wife and I bought a cabin in the mountains and it is heated by a propane gas hearth (see &lt;a title="Figure1: The hearth at my cabin in the Cascades" href="#fig1"&gt;figure 1&lt;/a&gt;). I’m guessing that the hearth was added as an afterthought because when I bought the place, there were no thermostat wires in the walls to control the hearth. The previous owner used a wireless remote control thermostat that looks like the thing you use to change the channel on the cable box. The thermostat sends signals by IR or radio or telepathy to a little box near the hearth and the little box turns the flame on and off. At least this is how it is supposed to work. The problem is that when the batteries in the controller or the little box get low, the system switches into a failsafe mode where the hearth never shuts off. I don’t know if this behavior is by design or just random and I suppose it has the advantage of keeping the pipes from freezing, but one fall I arrived at the cabin to find the hearth burning away in a balmy 100 degree room and shortly thereafter had to pay $200 to refill the propane tank. I have no idea how long the hearth had been burning.  &lt;p&gt;&lt;a href="http://hopcroft.members.winisp.net/images/HomeAutomationPart1Burning200ofpropane_D0A3/clip_image00254.jpg" atomicselection="true"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="179" alt="The hearth in my cabin in the Cascades" src="http://hopcroft.members.winisp.net/images/HomeAutomationPart1Burning200ofpropane_D0A3/clip_image00253.jpg" width="240" border="0"&gt;&lt;/a&gt;  &lt;p&gt;&lt;b name="fig1"&gt;Figure 1: The hearth at my cabin in the Cascades&lt;/b&gt;  &lt;p&gt;My first action was to replace the wireless thermostat with a hard wired unit. Fortunately, the hearth was next to an outside wall so I was able to stand on a ladder in a blizzard and run the thermostat wire under some trim on the outside of the wall while looking in at my wife and kids reading stories on the couch. The new thermostat increased the reliability of the system, but I was still concerned because the consequences of a failure were too costly. If the hearth failed to ignite I could end up with frozen pipes. If it failed to extinguish, I’d burn through all my propane and then have the pipes freeze. Also, the propane tank lies under ten feet of snow most of the winter, making a refill problematic.  &lt;p&gt;I decided to build a monitoring system that could alert me to problems at the cabin and even let me inspect the&amp;nbsp;premises over the web. My first order of business was to find a simple way to monitor the temperature. I wanted something that was unobtrusive, reliable, inexpensive, could easily&amp;nbsp;interface with&amp;nbsp;a PC, and did not require its own power supply. A brief search of the web turned up an amazing device called a &lt;a title="What is an iButton?" href="http://www.maxim-ic.com/1-Wire.cfm" target="_blank"&gt;1-wire sensor&lt;/a&gt;. It’s manufactured by Dallas Semiconductor and is basically a CPU with a sensor and serial I/O on a die the size of a wheat kernel. The 1-wire&amp;nbsp;sensor gets its power from the data wire used for communication with the outside world. Since each sensor has a unique&amp;nbsp;64 bit serial number, it is possible to connect multiple sensors to a single data line and then interrogate each in turn by sending its serial number and a command over the wire. Dallas Semiconductor sells 1-wire sensor chips and&amp;nbsp;&lt;a title="What is an IButton?" href="http://www.maxim-ic.com/products/ibutton/ibuttons/" target="_blank"&gt;IButtons&lt;/a&gt; which enclose the chip in a round stainless steel can. Other vendors&amp;nbsp;provide&amp;nbsp;1-wire&amp;nbsp;devices in modules with all sorts of sensors including temperature, humidity, water, light, voltage and current. I&amp;nbsp;ended up purchasing temperature+humidity modules from &lt;a title="iButtonLink.com" href="http://www.ibuttonlink.com/" target="_blank"&gt;iButtonLink.com&lt;/a&gt;. These sensor modules consist of a very small printed circuit holding the 1-wire chip and a pair of RJ-45 jacks for the communication line. Building a network of sensors is as easy as plugging all the modules together with CAT-5 wire. &lt;a title="Figure 2: The IButtonLink temperature+humidity sensor I purchased from http://www.iButtonLink.com." href="#fig2"&gt;Figure 2&lt;/a&gt; shows an&amp;nbsp;&lt;a title="iButtonLink Temperature+Humidity module" href="http://www.ibuttonlink.com/Display%20Tables/MultiSensor_Display_Table.htm" target="_blank"&gt;iButtonLink temperature+humidity module&lt;/a&gt;. Newer versions come in nice a plastic enclosure.  &lt;p&gt;&lt;a href="http://hopcroft.members.winisp.net/images/HomeAutomationPart1Burning200ofpropane_D0A3/clip_image0034.jpg" atomicselection="true"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="145" alt="The IButton temperature+humidity sensor I purchased from http://www.iButtonLink.com." src="http://hopcroft.members.winisp.net/images/HomeAutomationPart1Burning200ofpropane_D0A3/clip_image0033.jpg" width="240" border="0"&gt;&lt;/a&gt;  &lt;p&gt;&lt;b name="#fig2"&gt;Figure 2: The temperature+humidity sensor module&amp;nbsp;I purchased from &lt;a title="iButtonLink.com" href="http://www.iButtonLink.com"&gt;iButtonLink.com&lt;/a&gt;.&lt;/b&gt;  &lt;p&gt;I also purchased an &lt;a title="iButtonLink Link45" href="http://www.ibuttonlink.com/Link_Details.htm"&gt;iButtonLink Link45&lt;/a&gt; which provides an ASCII interface to the sensor network via an RS-232 port (see &lt;a title="Figure 3: The Link45 provides an ASCII interface to the sensor network via an RS-232 port." href="#fig3"&gt;figure 3&lt;/a&gt;). The Link45 is about an inch and a half long and includes a DE-9 connector at one end for the serial port and an RJ-45 on the other for the sensor network.  &lt;p&gt;&lt;a href="http://hopcroft.members.winisp.net/images/HomeAutomationPart1Burning200ofpropane_D0A3/clip_image0044.jpg" atomicselection="true"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="173" alt="The Link45 provides an ASCII interface to the sensor network via an RS-232 port." src="http://hopcroft.members.winisp.net/images/HomeAutomationPart1Burning200ofpropane_D0A3/clip_image0043.jpg" width="240" border="0"&gt;&lt;/a&gt;  &lt;p&gt;&lt;b name="#fig3"&gt;Figure 3: The Link45 provides an ASCII interface to the sensor network via an RS-232 port.&lt;/b&gt;  &lt;p&gt;With a Link45, a handful of sensor modules, 100 feet of CAT-5, and an old PC, I was ready to start coding.  &lt;p&gt;&lt;b&gt;Next installment:&lt;/b&gt; &lt;a title="Home Automation Part 2 - Making Basic Measurements" href="http://blogs.msdn.com/mhop/archive/2006/12/06/home-automation-part-2-making-basic-measurements.aspx"&gt;Making Basic Measurements&lt;/a&gt;  &lt;p&gt; &lt;a href="http://www.dotnetkicks.com/kick/?url=http://blogs.msdn.com/mhop/archive/2006/11/27/home-automation-part-1-burning-200-of-propane.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http://blogs.msdn.com/mhop/archive/2006/11/27/home-automation-part-1-burning-200-of-propane.aspx" border="0" alt="kick it on DotNetKicks.com"&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;em&gt;&lt;font color="#0080ff"&gt;(edited: updated link to next installment)&lt;/font&gt;&lt;/em&gt;&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1161600" width="1" height="1"&gt;</content><author><name>mhop</name><uri>http://blogs.msdn.com/members/mhop.aspx</uri></author><category term="Home Automation" scheme="http://blogs.msdn.com/mhop/archive/tags/Home+Automation/default.aspx" /></entry><entry><title>Welcome</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/mhop/archive/2006/11/27/welcome.aspx" /><id>http://blogs.msdn.com/mhop/archive/2006/11/27/welcome.aspx</id><published>2006-11-27T21:01:50Z</published><updated>2006-11-27T21:01:50Z</updated><content type="html">&lt;p&gt;&lt;/p&gt; &lt;p&gt;Hi. I’m Michael Hopcroft, the new Product Unit Manager for Visual C#. I started the job a few weeks ago and this is my first blog post. Working with the C# team is a dream job for me. I’ve liked C# since the day it came out and used it extensively in my previous job as Development Manager for Windows Presentation Foundation. One of my first tasks as Product Unit Manager is to really learn the technology in depth while getting to know the team. I hope you will join me in the coming weeks as I explore the product and its uses.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1160496" width="1" height="1"&gt;</content><author><name>mhop</name><uri>http://blogs.msdn.com/members/mhop.aspx</uri></author></entry></feed>