Hydra - Serial Port LineReceivedEventHandler problem (Solved!)

I am having a issue with receiving data from a device that is set to 115200 baud The event handler does not initiate when data is presented. I am on SDK 4.2 using the Hydra mainboard. I do not have any issues on the same port with devices set to 9600. I have similar code for full .net framework on a PC with no problems getting data from this device. I have tried the DataReceivedEventHandler and it initiates but I get 0 bytesToRead so I am really puzzled. From what I understand I can not change the ReadTimeout on my Hydra. I have it set to 500 on the PC version of the software. Debug indicates that this is -1 on the NETMF version, which I don’t know what this represents. It may no have anything to do with the issue. I am using the GHI serial port board. I forgot to add that the device in question ends the line with a /r/n. The sample code is as follows:

using System;
using System.Text;
using System.ComponentModel;
using System.IO;
using System.IO.Ports;
using System.Collections;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Input;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Controls;
using Microsoft.SPOT.Presentation.Media;
using Microsoft.SPOT.Presentation.Shapes;
using Microsoft.SPOT.Touch;
using Microsoft.SPOT.Hardware;

using Gadgeteer.Networking;
using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using Gadgeteer.Modules.GHIElectronics;

namespace TestSerialPortHandlers
{
    public partial class Program
    {
        
                    
        bool ConnectOpen = false;
        private System.Text.Decoder decoder = System.Text.UTF8Encoding.UTF8.GetDecoder();
        private byte[] buffer;
        private int startIndex = 0;
        private int endIndex = 0;
        private char[] charBuffer;

        // This method is run when the mainboard is powered up or reset.   
        void ProgramStarted()
        {

            /*******************************************************************************************
            Modules added in the Program.gadgeteer designer view are used by typing 
            their name followed by a period, e.g.  button.  or  camera.
            
            Many modules generate useful events. Type +=<tab><tab> to add a handler to an event, e.g.:
                button.ButtonPressed +=<tab><tab>
            
            If you want to do something periodically, use a GT.Timer and handle its Tick event, e.g.:
                GT.Timer timer = new GT.Timer(1000); // every second (1000ms)
                timer.Tick +=<tab><tab>
                timer.Start();
            *******************************************************************************************/


            // Use Debug.Print to show messages in Visual Studio's "Output" window during debugging.
            Debug.Print("Program Started");

            try
            {
                            // Open the port #1
                rs232.Initialize(9600);
                rs232.serialPort.LineReceivedEventDelimiter = "\r";
                rs232.serialPort.AutoReadLineEnabled = true;
                rs232.serialPort.Open();
                ConnectOpen = true;
                rs232.serialPort.LineReceived += new GT.Interfaces.Serial.LineReceivedEventHandler(serialPort_LineReceived1);
            }
            catch
            {
                Debug.Print("ERROR Opening port: 1");
                ConnectOpen = false;
                return;
            }

            try
            {
                // Open the port #2
                rs2322.Initialize(9600);
                rs2322.serialPort.LineReceivedEventDelimiter = "\r";
                rs2322.serialPort.AutoReadLineEnabled = true;
                rs2322.serialPort.Open();
                ConnectOpen = true;
                rs2322.serialPort.LineReceived += new GT.Interfaces.Serial.LineReceivedEventHandler(serialPort_LineReceived2);
            }
            catch
            {
                Debug.Print("ERROR Opening port: 2");
                ConnectOpen = false;
                return;
            }

            try
            {
                // Open the port #3
                int baudRate3 = 115200;
                rs2323.Initialize(baudRate3);
                rs2323.serialPort.LineReceivedEventDelimiter = "\n";
                rs2323.serialPort.AutoReadLineEnabled = true;
                rs2323.serialPort.Open();
                ConnectOpen = true;
                //rs2323.serialPort.DataReceived += new GT.Interfaces.Serial.DataReceivedEventHandler(data_received);
                rs2323.serialPort.LineReceived += new GT.Interfaces.Serial.LineReceivedEventHandler(serialPort_LineReceived3);
            }
            catch
            {
                Debug.Print("ERROR Opening port: 3");
                ConnectOpen = false;
                return;
            }

            createwriteData();

        }



        private void serialPort_LineReceived1(GT.Interfaces.Serial sender, string line)
        {
            string serialLine = line;
            Debug.Print(serialLine);
        }

        private void serialPort_LineReceived2(GT.Interfaces.Serial sender, string line)
        {
            string serialLine = line;
            Debug.Print(serialLine);
        }

        private void serialPort_LineReceived3(GT.Interfaces.Serial sender, string line)
        {
            string serialLine = line;
            Debug.Print(serialLine);
        }

        private void data_received(GT.Interfaces.Serial sender, SerialData bytee)
        {

            Debug.Print("Byte in");
            Thread.Sleep(2000);

            int bytesToRead = rs2323.serialPort.BytesToRead;


        }

        public void createwriteData()
        {
            string StartData = "55 AA 00 00 01 00 00 C1 00 00 07 00 00 00 01 01 07 00 00 00 80 00 10 00 02 00 45 00 00 0D 0A";
            byte[] leddata = HexStringToByteArray(StartData);
            SendBinaryData(leddata);
        }
        
        public void SendBinaryData(byte[] ledData)
        {
            try
            {
                rs2323.serialPort.Write(ledData, 0, ledData.Length);
                Thread.Sleep(100);
            }
            catch
            {
                Debug.Print("Write error, port: 3");
            }
        }

        /// <summary> Convert a string of hex digits (ex: E4 CA B2) to a byte array. </summary>
        /// <param name="s"> The string containing the hex digits (with or without spaces). </param>
        /// <returns> Returns an array of bytes. </returns>
        private byte[] HexStringToByteArray(string s)
        {
            StringBuilder sb = new StringBuilder(s);
            sb.Replace(" ", "");

            if (sb.Length % 2 == 1)
                throw new Exception("The binary key cannot have an odd number of digits");

            byte[] buffer = new byte[sb.Length >> 1];

            for (int i = 0; i < sb.Length >> 1; ++i)
            {
                buffer[i] = (byte)((GetHexVal(sb[i << 1]) << 4) + (GetHexVal(sb[(i << 1) + 1])));
            }

            return buffer;
        }

        public static int GetHexVal(char hex)
        {
            int val = (int)hex;
            //For uppercase A-F letters:
            //return val - (val < 58 ? 48 : 55);
            //For lowercase a-f letters:
            //return val - (val < 58 ? 48 : 87);
            //Or the two combined, but a bit slower:
            return val - (val < 58 ? 48 : (val < 97 ? 55 : 87));
        }

        /// <summary> Converts an array of bytes into a formatted string of hex digits (ex: E4 CA B2)</summary>
        /// <param name="data"> The array of bytes to be translated into a string of hex digits. </param>
        /// <returns> Returns a well formatted string of hex digits with spacing. </returns>
        private string ByteArrayToHexString(byte[] data)
        {
            StringBuilder sb = new StringBuilder(data.Length * 3);
            string blankspace = " ";
            string HexAlphabet = "0123456789ABCDEF";
            //StringBuilder sb = new StringBuilder(data.Length * 3);
            foreach (byte b in data)
            {
                sb.Append(HexAlphabet[(int)(b >> 4)]);
                sb.Append(HexAlphabet[(int)(b & 0xF)]);
                sb.Append(blankspace);
            }
            return sb.ToString().ToUpper();
        }

    }
}

linerecieved is a Gadgeteer addition, it’s not a core netmf feature… so the way you’ve structured your example code is possibly contributing to the problem. You should let the ProgramStarted() finish properly which then lets the framework set up the Gadgeteer scheduler properly; you can then start a timer to run your createwriteData() method, and then stop the timer, or you can get it in it’s own thread - but I’d hold off assuming that the environment is correctly configured until ProgramStarted() is done.

( but since you say the same code works ok at 9600 you may find that you’re just saturating the processor’s handling of strings, which are not that quick - perhaps you should look at DataRecieved handler and wrapping your own EOL termination checks into that )

Thanks Brett for the suggestion, revised my example code to include a timer. However same results. I was concerned about saturation when it worked at 9600, that is why I tried DataReceived (it is commented out in example) and started to write the handler but as I mentioned the bytesToRead is always zero. The good thing is that the DataReceived handler initiates. The revised code is:


using System;
using System.Text;
using System.ComponentModel;
using System.IO;
using System.IO.Ports;
using System.Collections;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Input;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Controls;
using Microsoft.SPOT.Presentation.Media;
using Microsoft.SPOT.Presentation.Shapes;
using Microsoft.SPOT.Touch;
using Microsoft.SPOT.Hardware;

using Gadgeteer.Networking;
using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using Gadgeteer.Modules.GHIElectronics;

namespace TestSerialPortHandlers
{
    public partial class Program
    {
        
                    
        bool ConnectOpen = false;
        bool ConnectSetup = false;
        bool StringFlip = false;
        private System.Text.Decoder decoder = System.Text.UTF8Encoding.UTF8.GetDecoder();
        private byte[] buffer;
        private int startIndex = 0;
        private int endIndex = 0;
        private char[] charBuffer;

        // This method is run when the mainboard is powered up or reset.   
        void ProgramStarted()
        {

            /*******************************************************************************************
            Modules added in the Program.gadgeteer designer view are used by typing 
            their name followed by a period, e.g.  button.  or  camera.
            
            Many modules generate useful events. Type +=<tab><tab> to add a handler to an event, e.g.:
                button.ButtonPressed +=<tab><tab>
            
            If you want to do something periodically, use a GT.Timer and handle its Tick event, e.g.:
                GT.Timer timer = new GT.Timer(1000); // every second (1000ms)
                timer.Tick +=<tab><tab>
                timer.Start();
            *******************************************************************************************/


            // Use Debug.Print to show messages in Visual Studio's "Output" window during debugging.
            Debug.Print("Program Started");

            GT.Timer timer = new GT.Timer(4000);
            timer.Tick += new GT.Timer.TickEventHandler(timer_Tick);
            timer.Start();

        }

        void timer_Tick(GT.Timer timer)
        {
            if (ConnectSetup == false)
            {
                try
                {
                    // Open the port #1
                    rs232.Initialize(9600);
                    rs232.serialPort.LineReceivedEventDelimiter = "\r";
                    rs232.serialPort.AutoReadLineEnabled = true;
                    rs232.serialPort.Open();
                    ConnectOpen = true;
                    rs232.serialPort.LineReceived += new GT.Interfaces.Serial.LineReceivedEventHandler(serialPort_LineReceived1);
                }
                catch
                {
                    Debug.Print("ERROR Opening port: 1");
                    ConnectOpen = false;
                    return;
                }

                try
                {
                    // Open the port #2
                    rs2322.Initialize(9600);
                    rs2322.serialPort.LineReceivedEventDelimiter = "\r";
                    rs2322.serialPort.AutoReadLineEnabled = true;
                    rs2322.serialPort.Open();
                    ConnectOpen = true;
                    rs2322.serialPort.LineReceived += new GT.Interfaces.Serial.LineReceivedEventHandler(serialPort_LineReceived2);
                }
                catch
                {
                    Debug.Print("ERROR Opening port: 2");
                    ConnectOpen = false;
                    return;
                }

                try
                {
                    // Open the port #3
                    int baudRate3 = 115200;
                    rs2323.Initialize(baudRate3);
                    rs2323.serialPort.LineReceivedEventDelimiter = "\n";
                    rs2323.serialPort.AutoReadLineEnabled = true;
                    rs2323.serialPort.Open();
                    ConnectOpen = true;
                    //rs2323.serialPort.DataReceived += new GT.Interfaces.Serial.DataReceivedEventHandler(data_received);
                    rs2323.serialPort.LineReceived += new GT.Interfaces.Serial.LineReceivedEventHandler(serialPort_LineReceived3);
                }
                catch
                {
                    Debug.Print("ERROR Opening port: 3");
                    ConnectOpen = false;
                    return;
                }
                ConnectSetup = true;
            }

            openPorts();

        }

        public void openPorts()
        {
            try
            {
                // Open the port #1
                rs232.serialPort.Open();
                ConnectOpen = true;
                rs232.serialPort.LineReceived += new GT.Interfaces.Serial.LineReceivedEventHandler(serialPort_LineReceived1);
            }
            catch
            {
                Debug.Print("ERROR Opening port: 1");
                ConnectOpen = false;
                return;
            }

            try
            {
                // Open the port #2
                rs2322.serialPort.Open();
                ConnectOpen = true;
                rs2322.serialPort.LineReceived += new GT.Interfaces.Serial.LineReceivedEventHandler(serialPort_LineReceived2);
            }
            catch
            {
                Debug.Print("ERROR Opening port: 2");
                ConnectOpen = false;
                return;
            }

            try
            {
                // Open the port #3
                rs2323.serialPort.Open();
                ConnectOpen = true;
                rs2323.serialPort.DataReceived += new GT.Interfaces.Serial.DataReceivedEventHandler(data_received);
                //rs2323.serialPort.LineReceived += new GT.Interfaces.Serial.LineReceivedEventHandler(serialPort_LineReceived3);
            }
            catch
            {
                Debug.Print("ERROR Opening port: 3");
                ConnectOpen = false;
                return;
            }

            createwriteData();
        }


        private void serialPort_LineReceived1(GT.Interfaces.Serial sender, string line)
        {
            string serialLine = line;
            Debug.Print(serialLine);
        }

        private void serialPort_LineReceived2(GT.Interfaces.Serial sender, string line)
        {
            string serialLine = line;
            Debug.Print(serialLine);
        }

        private void serialPort_LineReceived3(GT.Interfaces.Serial sender, string line)
        {
            string serialLine = line;
            Debug.Print(serialLine);
        }

        private void data_received(GT.Interfaces.Serial sender, SerialData data)
        {

            int timeout = 0;
            int count = 0;

            while (true)
            {
                while (rs2323.serialPort.BytesToRead > 0)
                {
                    char c = (char)rs2323.serialPort.ReadByte();
                    charBuffer[count] = c;
                    count++;
                    if (c == '\n' || count == 18)
                    timeout = 0;
                }

                if (count > 0)
                {
                    timeout++;
                    if (timeout > 100)
                        break;
                }
                Thread.Sleep(100);
            }
        }

        public void createwriteData()
        {
            string StartData;
            string StartDataPri = "55 AA 00 00 01 00 00 C1 00 00 07 00 00 00 01 01 07 00 00 00 80 00 10 00 01 00 45 00 00 0D 0A";
            string StartDataAlt = "55 AA 00 00 01 00 00 C1 00 00 07 00 00 00 01 01 07 00 00 00 80 00 10 00 02 00 45 00 00 0D 0A";
            if (StringFlip  == false)
            { StartData = StartDataPri; StringFlip = true; }
            else
            { StartData = StartDataAlt; StringFlip = false; }
            byte[] leddata = HexStringToByteArray(StartData);
            SendBinaryData(leddata);
        }
        
        public void SendBinaryData(byte[] ledData)
        {
            try
            {
                rs2323.serialPort.Write(ledData, 0, ledData.Length);
                Thread.Sleep(100);
            }
            catch
            {
                Debug.Print("Write error, port: 3");
            }
        }

        /// <summary> Convert a string of hex digits (ex: E4 CA B2) to a byte array. </summary>
        /// <param name="s"> The string containing the hex digits (with or without spaces). </param>
        /// <returns> Returns an array of bytes. </returns>
        private byte[] HexStringToByteArray(string s)
        {
            StringBuilder sb = new StringBuilder(s);
            sb.Replace(" ", "");

            if (sb.Length % 2 == 1)
                throw new Exception("The binary key cannot have an odd number of digits");

            byte[] buffer = new byte[sb.Length >> 1];

            for (int i = 0; i < sb.Length >> 1; ++i)
            {
                buffer[i] = (byte)((GetHexVal(sb[i << 1]) << 4) + (GetHexVal(sb[(i << 1) + 1])));
            }

            return buffer;
        }

        public static int GetHexVal(char hex)
        {
            int val = (int)hex;
            //For uppercase A-F letters:
            //return val - (val < 58 ? 48 : 55);
            //For lowercase a-f letters:
            //return val - (val < 58 ? 48 : 87);
            //Or the two combined, but a bit slower:
            return val - (val < 58 ? 48 : (val < 97 ? 55 : 87));
        }

        /// <summary> Converts an array of bytes into a formatted string of hex digits (ex: E4 CA B2)</summary>
        /// <param name="data"> The array of bytes to be translated into a string of hex digits. </param>
        /// <returns> Returns a well formatted string of hex digits with spacing. </returns>
        private string ByteArrayToHexString(byte[] data)
        {
            StringBuilder sb = new StringBuilder(data.Length * 3);
            string blankspace = " ";
            string HexAlphabet = "0123456789ABCDEF";
            //StringBuilder sb = new StringBuilder(data.Length * 3);
            foreach (byte b in data)
            {
                sb.Append(HexAlphabet[(int)(b >> 4)]);
                sb.Append(HexAlphabet[(int)(b & 0xF)]);
                sb.Append(blankspace);
            }
            return sb.ToString().ToUpper();
        }

    }
}

I haven’t tested this, but here’s a few other suggestions. BUT… I want to really understand what your challenge is ? If the datarecieved handler fires, then there must be data there - so there’s no way the bytesToRead will be zero ! Can you explain more ?

Anyway, onto some suggestions…

There’s no need to put your timer code, except for the call to openPorts(), in the timer. In fact, most of the code in openPorts seems to duplicate your setup in the timer. Just do it once and that’s it… The createwriteData() method is the one that you should put in the timer !

Move the declaration of the timer outside the scope of the ProgramStarted() method.

Inside the timer, at the start of the code, stop itself so it doesn’t fire again in 4 seconds - at least until you get things working at least once.

You are doing lots of string work - you might want to consider testing this with byte arrays, particularly if the arrays don’t change.

For your testing, I would suggest you find a way to wire the TX pin on one U socket to the RX pin on another, and send out one port and you should then see data come in the other UART.

One final suggestion is you move to netmf 4.3 and a later build of Gadgeteer as I remember there was a bug in Gadgeteer LineReceived implementation, I think this is the item: http://gadgeteer.codeplex.com/workitem/1678 (but I did think the fault was more significant than this but still might be playing a part here)

Solved my problem! When I changed from the LineReceivedEventHandler to the DataReceivedEventHandler, I forgot to comment out the AutoReadLineEnabled line. Once I did this I was able to receive bytes. In my final test code, I used the netmf Toolbox Chars2Bytes function (Copyright 2011-2014 Stefan Thoolen (http://www.netmftoolbox.com/)). Never could get the LineReceivedEventHandler to work above 9600 baud. For faster rates had to use the DataReceivedEventHandler. My working test code for receiving serial port data from multiple serial ports is as follows:


using System;
using System.Text;
using System.ComponentModel;
using System.IO;
using System.IO.Ports;
using System.Collections;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Input;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Controls;
using Microsoft.SPOT.Presentation.Media;
using Microsoft.SPOT.Presentation.Shapes;
using Microsoft.SPOT.Touch;
using Microsoft.SPOT.Hardware;

using Gadgeteer.Networking;
using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using Gadgeteer.Modules.GHIElectronics;

namespace TestSerialPortHandlers
{
    public partial class Program
    {


        bool ConnectOpen = false;
        bool ConnectSetup = false;
        bool StringFlip = false;
        private System.Text.Decoder decoder = System.Text.UTF8Encoding.UTF8.GetDecoder();
        private int startIndex = 0;
        private int endIndex = 0;




        // This method is run when the mainboard is powered up or reset.  
        void ProgramStarted()
        {

            /*******************************************************************************************
            Modules added in the Program.gadgeteer designer view are used by typing
            their name followed by a period, e.g.  button.  or  camera.
           
            Many modules generate useful events. Type +=<tab><tab> to add a handler to an event, e.g.:
                button.ButtonPressed +=<tab><tab>
           
            If you want to do something periodically, use a GT.Timer and handle its Tick event, e.g.:
                GT.Timer timer = new GT.Timer(1000); // every second (1000ms)
                timer.Tick +=<tab><tab>
                timer.Start();
            *******************************************************************************************/


            // Use Debug.Print to show messages in Visual Studio's "Output" window during debugging.
            Debug.Print("Program Started");

            // Intialize port #1
            rs232.Initialize(9600);
            rs232.serialPort.LineReceivedEventDelimiter = "\r";
            rs232.serialPort.AutoReadLineEnabled = true;



            // Intialize port #2
            rs2322.Initialize(9600);
            rs2322.serialPort.LineReceivedEventDelimiter = "\r";
            rs2322.serialPort.AutoReadLineEnabled = true;

            // Intialize port #3
            int baudRate3 = 115200;
            rs2323.Initialize(baudRate3);

            GT.Timer timer = new GT.Timer(4000);
            timer.Tick += new GT.Timer.TickEventHandler(timer_Tick);
            timer.Start();

        }

        void timer_Tick(GT.Timer timer)
        {


            openPorts();

        }

        public void openPorts()
        {
            try
            {
                // Open the port #1
                rs232.serialPort.Open();
                ConnectOpen = true;
                rs232.serialPort.LineReceived += new GT.Interfaces.Serial.LineReceivedEventHandler(serialPort_LineReceived1);
            }
            catch
            {
                Debug.Print("ERROR Opening port: 1");
                ConnectOpen = false;
                return;
            }

            try
            {
                // Open the port #2
                rs2322.serialPort.Open();
                ConnectOpen = true;
                rs2322.serialPort.LineReceived += new GT.Interfaces.Serial.LineReceivedEventHandler(serialPort_LineReceived2);
            }
            catch
            {
                Debug.Print("ERROR Opening port: 2");
                ConnectOpen = false;
                return;
            }

            try
            {
                // Open the port #3
                rs2323.serialPort.Open();
                ConnectOpen = true;
                rs2323.serialPort.DataReceived += new GT.Interfaces.Serial.DataReceivedEventHandler(data_received);
            }
            catch
            {
                Debug.Print("ERROR Opening port: 3");
                ConnectOpen = false;
                return;
            }

            createwriteData();
        }


        private void serialPort_LineReceived1(GT.Interfaces.Serial sender, string line)
        {
            string serialLine = line;
            Debug.Print(serialLine);
        }

        private void serialPort_LineReceived2(GT.Interfaces.Serial sender, string line)
        {
            string serialLine = line;
            Debug.Print(serialLine);
        }

        private void serialPort_LineReceived3(GT.Interfaces.Serial sender, string line)
        {
            string serialLine = line;
            Debug.Print(serialLine);
        }

        private void data_received(GT.Interfaces.Serial sender, SerialData data)
        {

            int count = 0;
            int pos = 0;
            int btr = sender.BytesToRead;
            char[] charbuffer = new char[btr];
            byte[] bybuffer = new byte[btr];
            byte[] charbyte = new byte[btr];

            while (pos < btr)
            {
                char c = (char)sender.ReadByte();
                charbuffer[count] = c;
                string s = c.ToString();
                byte[] b = Encoding.UTF8.GetBytes(s);
                int blength = b.Length;
                if (blength == 2) { bybuffer[count] = b[1]; }
                if (blength == 1) { bybuffer[count] = b[0]; }
                Array.Clear(b,0,b.Length);
                count++;
                pos++;
            }
            charbyte = Toolbox.NETMF.Tools.Chars2Bytes(charbuffer);
            string hexstring = ByteArrayToHexString(charbyte);

            Thread.Sleep(100);

        }

        public void createwriteData()
        {
            string StartData = "55 AA 00 00 01 00 00 A1 00 00 01 00 00 00 01 01 01 00 00 00 02 00 00 0D 0A";
            byte[] leddata = HexStringToByteArray(StartData);
            SendBinaryData(leddata);
        }

        public void SendBinaryData(byte[] ledData)
        {
            try
            {
                rs2323.serialPort.Write(ledData, 0, ledData.Length);
                Thread.Sleep(200);
            }
            catch
            {
                Debug.Print("Write error, port: 3");
            }
        }

        /// <summary> Convert a string of hex digits (ex: E4 CA B2) to a byte array. </summary>
        /// <param name="s"> The string containing the hex digits (with or without spaces). </param>
        /// <returns> Returns an array of bytes. </returns>
        private byte[] HexStringToByteArray(string s)
        {
            StringBuilder sb = new StringBuilder(s);
            sb.Replace(" ", "");

            if (sb.Length % 2 == 1)
                throw new Exception("The binary key cannot have an odd number of digits");

            byte[] buffer = new byte[sb.Length >> 1];

            for (int i = 0; i < sb.Length >> 1; ++i)
            {
                buffer[i] = (byte)((GetHexVal(sb[i << 1]) << 4) + (GetHexVal(sb[(i << 1) + 1])));
            }

            return buffer;
        }

        public static int GetHexVal(char hex)
        {
            int val = (int)hex;
            //For uppercase A-F letters:
            //return val - (val < 58 ? 48 : 55);
            //For lowercase a-f letters:
            //return val - (val < 58 ? 48 : 87);
            //Or the two combined, but a bit slower:
            return val - (val < 58 ? 48 : (val < 97 ? 55 : 87));
        }

        /// <summary> Converts an array of bytes into a formatted string of hex digits (ex: E4 CA B2)</summary>
        /// <param name="data"> The array of bytes to be translated into a string of hex digits. </param>
        /// <returns> Returns a well formatted string of hex digits with spacing. </returns>
        private string ByteArrayToHexString(byte[] data)
        {
            StringBuilder sb = new StringBuilder(data.Length * 3);
            string blankspace = " ";
            string HexAlphabet = "0123456789ABCDEF";
            //StringBuilder sb = new StringBuilder(data.Length * 3);
            foreach (byte b in data)
            {
                sb.Append(HexAlphabet[(int)(b >> 4)]);
                sb.Append(HexAlphabet[(int)(b & 0xF)]);
                sb.Append(blankspace);
            }
            return sb.ToString().ToUpper();
        }

    }
}