Transferring files and monitoring Bluetooth ports in C#






Author: Josip Zohil, Koper, Slovenija, Josip.Zohil1@guest.arnes.si

Sometimes there are problems transferring data between two Bluetooth devices. We can't Transfer some type of files, we can't discover the data transfer service (protocols) , for example the OBEX or FTP protocol, which is above the RFCOMM layer of the Bluetooth protocol stack. But the devices can communicate on the lower RFCOMM layer. If we have control over two devices, for example PC or Pocket PC, we can create C# programs for file transfer. We will show you, how to transfer files with Bluetooth technology without the use of additional software and how to monitor the serial port bytes traffic in C#. You can reuse the code in this article to create your application to transfer data also in environment when you can't do that with standard Bluetooth application.

Creating Bluetooth application in C#

Creating Bluetooth server and client application in C# is similar to creating server and client application for the most known TCP communication. With the installation of the Bluetooth driver (stack), it generates virtual comm ports which provide RS-232 serial cable emulation for Bluetooth devices as a serial cable link. Usually in the configuration or settings menu of the device we create a connection between the client and server, for example the virtual port com42 on the device (C) is connected to the virtual port com7 on device (S). We can look at this configuration in Start->Settings->Control Panel-Bluetooth menu of each device. We transfer data from client to server over the Bluetooth connection between the two virtual ports. As mentioned earlier the driver (stack) creates the basis for this connection.

A client (in Bluetooth terminology a client service) creates an outbound RFCOMM connection to a server virtual serial port. A Bluetooth server uses a virtual comm port (server service) to communicate with the client. When we send data to the client serial port (for example virtual port com42), the stack software makes a wireless connection to the server (for example virtual port com7). Once the client and server connect to each other, they exchange data until the client or server terminates the connection or until the connection is lost.

If we know the names of both virtual serial ports (client and server), we can create a C# program to send data (messages and files of all type) from the client to the server using the RFCOMM layer. This transfer is similar to socket bytes transfer. Because we have control over both devices, we can create our own protocol for file transfer.

The simplest method of file transfer is when on the client side we send bytes to the comm port and we accept the incoming bytes on the server device virtual comm port. In more elaborate protocol we will first send the file length to the server, and than the file bytes.

In this way on the server side we can monitor the progress of the transfer. In some cases we add also a file name in the start portion of the bytes stream. So, on the server side we now the name and location of the file to save.

In the OBEX protocol the transfer is organized in packets. The client sends a connection packet to the server and waits for the OK response. Than it sends a put packet with file length and name. When the server response with continue packet, the client send the next packet. It sends the last bytes of the file in the put packet with the final bit (0x82).

In this article we shell show you how to create OBEX packet in C#, how to send and receive them and how to monitor port bytes traffic when developing and testing the application. In Microsoft Visual Studio 2005 we will create four projects: three file transfer clients and the file transfer server. Firstly we present a simple client project which send a connect OBEX packet and receive an OK packet. In the second project we shall extend the project functions to the client able of sending small files and finally the client for sending files of all types. The sources of all projects are downloadable.

Creating an OBEX packet in C#

The OBEX packet is an array of bytes composed of headers and data. Same headers bytes are in Listing 1.

private byte obex_conn = (byte)0x80;    
private byte obex_disconn = (byte)0x81; 
private byte obex_put = (byte)0x02;
private byte obex_put_final_bit = (byte)0x82;
private byte obex_ok = (byte)0xA0;
private byte obex_continue = (byte)0x90;
private byte obex_body = (byte)0x48;
private byte obex_end_body = (byte)0x49;
private byte obex_version = (byte)0x10;
private byte obex_conn_flags = (byte)0x00;
private byte obex_name = (byte)0x01;
private byte obex_length = (byte)0xC3;


Listing 1. Headers bytes used in OBEX protocol

//mPacketSize is the maximum paket size allowed
public byte[] ConnectPacket(Int16 mPacketSize)
        {
            MemoryStream ms = new MemoryStream();
            byte[] bufxx = new byte[] { obex_conn };  //first byte identify the packet
            ms.Write(bufxx, 0, 1);
            bufxx = BitConverter.GetBytes((UInt16)(7));  //the length of the packet
            ms.Write(bufxx, 0, 2);
            bufxx = new byte[] { obex_version };
            ms.Write(bufxx, 0, 1);
            bufxx = new byte[] { obex_conn_flags };
            ms.Write(bufxx, 0, 1);
            bufxx = BitConverter.GetBytes((Int16)mPacketSize);  // The number Int16 is 
            ms.Write(bufxx, 0, 2);                                                    //converted in two bytes. 
            ms.Position = 0;
            return ms.ToArray();
        }


Listing 2 The method that generate an array of bytes - the connect packet In Listing 2 is an example of the program which creates a connect packet (byte array). We create a memory stream (MemoryStream ms = new MemoryStream();) and a one dimensional byte array (byte[] bufxx = new byte[] { obex_conn }; ) in which we store the byte 0x80 (obex connect header). We write the bufxx byte array to the memory stream (ms.Write(bufxx, 0, 1);). The packet length is 7 bytes. The command ((Int16)(7)) parse this integer to Int16, so the function (BitConverter.GetBytes((Int16)(7))) give us a two byte array bufxx. We write it to the memory stream. The same we do with the maximum packet length. Two one byte arrays with the obex version (0x10) and connection flags are also written to the memory stream. The method return a byte array so the memory stream is converted to it with the function (ms.ToArray()).

public byte[] SuccessConnectPacket(Int16 mPacketSize)
        {
            MemoryStream ms = new MemoryStream();
            byte[] bufxx = new byte[] { obex_ok };
            ms.Write(bufxx, 0, 1);
            bufxx = BitConverter.GetBytes((Int16)(7));
            ms.Write(bufxx, 0, 2);
            bufxx = new byte[] { 0x10 };
            ms.Write(bufxx, 0, 1);
            bufxx = new byte[] { obex_conn_flags };
            ms.Write(bufxx, 0, 1);
            bufxx = BitConverter.GetBytes((Int16)mPacketSize);
            ms.Write(bufxx, 0, 2);
            ms.Position = 0;
            return ms.ToArray();
        }


Listing 3. SuccessConnectPacket that the server send as a response of the connect packet

Listing 3 show the program that create the packet that the server send as a response to the connect packet.

//bufferx is a byte array,  which we use to read  the received bytes. numR is the number of bytes received.
public int DecodeReceivedA0(byte[] bufferx, int numR)
        {
            int maxPacketSize = 0;
            try
            {
                //receive connect packet  A0     //   if (bufferx[0] == (byte)0xA0)
                byte[] buffer1 = new byte[2];
                int    iix = 4;
                while (iix < 6)
                {
                    buffer1.SetValue(bufferx.GetValue(iix+1 ), iix-4);
                    iix += 1;
                }
                maxPacketSize = (int)BitConverter.ToInt16(buffer1, 0);
            }
            catch (Exception ex)
            {
                this.error="Error in response packet." + ex.Message.ToString();
                return 0;
            }
            return maxPpacketSize;
        }


Listing 4. The method for decoding the received packet (byte array).

When the server or the client receives a packet, they must decode it: read the information in it. Listing 4. show a method for decoding the packet. We read the received bytes in byte array named bufferx. The number of received bytes is numR (numR is less or equal bufferx length). After reading the bytes in the byte array, we extract the first byte. If its value is 0xA0, we know, that this is a response to a connect packet and we apply a DecodeReceivedA0 method to extract the data from it: packet length, OBEX version; maximum packet size. By decoding the packet we obtain two information: the packet first byte 0xA0, that tell the client that it is connected to the server and the maximum packet size allowed in the transfer process.

Those and other methods for creating OBEX packet are in the TransferSerial class of the project.

Sending and receiving packets

Let us see how we send and receive OBEX packet in C#. For example, we send a connect packet and receive a response (ok) packet.

private void sendobex_Click(object sender, EventArgs e)
        {
            this.monitor.Text = "";
          
            this.lblMessage.Text = "Connecting...";
            this.tra = new TransferSerial();
            if (!tra.Connect(this.serialPort1)) return;
            this.com.Text = this.serialPort1.PortName.ToString();
            this.serialPort1.Write(tra.conn, 0, tra.conn.Length); //write the Connect packet
            this.Invoke(new LabelWriteByte(this.LogByte), tra.conn, tra.connLength);
            if (!tra.ConnectResponse(this.serialPort1))
            {
                this.lblMessage.Text = tra.ErrorMes();
                return; //didn't receive ok packet from server
            }
            this.Invoke(new LabelWriteByte(this.LogByte), tra.conn, tra.connLength);
            this.lblMessage.Text = "Connected.";
            this.disconnected = false;
            //response connect ok
            this.confirm = true;
             }
Listing 5. A Click event send a connect packet and receive an OK from the server

In Listing 5. we called a Connect method to a serial port (!tra.Connect(this.serialPort1)) that generate a byte array conn in which we store the put packet created with the method in Listing 6. The command this.serialPort1.Write(tra.conn, 0, tra.conn.Length) write the array (packet) to the selected comm port serialPort1 stream and the Bluetooth stack software transfer these bytes to the server port. The command tra.ConnectResponse(this.serialPort1) says to the system to listen on port serialPort1 for the response from server. The function decodes the received packet and return true if the server response with an OK response packet (Listing 7).

public bool Connect(System.IO.Ports.SerialPort serialport1)
        {
            if (serialport1.PortName == "COM0")
            {
                MessageBox.Show("Select comm port!");
                return false;
            }
            if (!this.OpenSerialPort(serialport1)) return false; //open a serial port
            try
            {
                this._conn = this.ConnectPacket(); //create a connect packet
                this._connLength = this._conn.Length;
            }
            catch (Exception ex)
            {
                MessageBox.Show("Can not connect to remote service. (Remote server not  
listen?)." + this.ErrorMes() + ex.Message.ToString());
                return false;
            }
            return true;
        }.


Listing 6. Connect method

The Connect method (Listing 6) opens a selected comm port and call the ConnectPacket method (Listing 2.), that generates the put packet and stores its bytes in a byte array. It returns also a put packet length (connLength).

//read response first 6 byte from connect packet
        public bool ConnectResponse(System.IO.Ports.SerialPort serialport1)
        {
            if (!this.ReadResponsePacket6(serialport1))  //read response from server
            {
                MessageBox.Show("Did not received response Ok from server.");
                return false;
            }
            return true;
        }


Listing 7. ConnectResponse method that waits on port serialport1 the response from the server.

The ConnectResponse method (Listing 7) waits on port serialport1 the response from the server.

private bool ReadResponsePacket6(System.IO.Ports.SerialPort serialport1)
        {
          while (serialport1.BytesToRead < 3) Thread.Sleep(10);
            byte[] ComBuffer1 = new byte[7];
            int lenRead = serialport1.Read(ComBuffer1, 0, 7);
           //extract the packet length (byte 2 and 3 of the readed bytes) 
           byte[] bufferPacket = new byte[2];
            int iix = 0;
            while (iix < 2)
            {
                bufferPacket.SetValue(ComBuffer1.GetValue(iix + 1), iix);
                iix += 1;
            }
            int packetLenToRead  = ((int)BitConverter.ToInt16(bufferPacket, 0)) ;
            this._connLength = lenRead; //return the packet length to the calling procedure
            if (packetLenToRead  == 3 && ComBuffer1[0] == obex_ok)  // ok without max packet length
              {
                 this._conn = ComBuffer1;
                 return true;
              }
            if (lenRead < 6)
              {
                    lenRead = serialport1.Read(ComBuffer1, 3, 4);
                    this._connLength = 3 + lenRead; //return to the calling procedure
                }
                else
                {
                    this._connLength = lenRead;
                }
            if (ComBuffer1[0] == obex_ok)
            {
                //client listen response from server
                int pr = this.DecodeReceivedA0(ComBuffer1, 6);
                if (pr > 0)  //max packet length >0
                {
                    this.mPacketSize = Convert.ToInt16(pr);  //max packet size
          //read the remaining bytes of the packet
                    if (packetLenToRead > 7)
                    {
                        lenRead = serialport1.Read(ComBuffer1, 7, packetLenToRead - 6);
                        this._connLength += lenRead;
                    }
                    this._conn = ComBuffer1; //return to the calling procedure
                    return true;
                }
                else
                {
                    MessageBox.Show("Not connected. Error in response packet.Try again.");
                    return false;
                }
            }
            else
            {
                return false;
            }
        } 


Listing 8. A method for reading and decoding received packet.

The ReadResponsePacket6 method read the port stream and decode its first 3 (6) bytes (Listing 8). The command bufferPacket.SetValue(ComBuffer1.GetValue(iix + 1), iix); write the byte with index iix+1 of the byte array ComBuffer1 to the byte with the index iix of the byte array bufferPacket and the command (int)BitConverter.ToInt16(bufferPacket, 0)) converts the two byte array to an integer - the packet length. If the lenRead is less than 6 and packet length is grater than 3, we read the remaining bytes (lenRead = serialport1.Read(ComBuffer1, 3, 4);).

In the line int pr = this.DecodeReceivedA0(ComBuffer1, 6); we decode the first 6 bytes and from this we extract the maximum packet length.

Later, we shall explain the delegate LabelWriteByte.

The log files and the port monitor

In the development and test phase of the project we usually register the process steps in the log file. We will also write the bytes traffic (sent and received) on comm port to the file logByte and monitor this traffic in the text box named 'monitor' on the form.

// logByteFileName is the name of the log byte file, for example C:\myByteLog.txt
private void LogByte(byte[] bWrite, int byteLen)
        {
  if (fLogByte == null) fLogByte = new FileStream(logByteFileName, FileMode.Create,  
FileAccess.Write);
            fLogByte.Write(bWrite, 0, byteLen);
            //Only in the test phase. Write the byte array in hexadecimal format
            string hexString = newLine + newLine; //separete with a blank line
            int jj = 0;
            while (jj < byteLen)
            {
                hexString += String.Format("{0:X2}", bWrite[jj]);  //write the bytes in  
string format
                if (hexString.Length > 80)      //the nunmer of columns don't excede 80.
                {
                    this.monitor.Text += hexString;
                    hexString = "";
                }
                jj += 1;
            }
            if (hexString.Length > 0)
            {
                this.monitor.Text += hexString;   //write the string to the monitor textbox.
            }
        }


Listing 9. A method for writing a byte array to the file and textbox on the form.

On every read or write of bytes on the comm port, we write the bytes to the logByte file and monitor text box. This operation is executed with the form invoke method and the delegate LabelWriteByte (Listing 9). For example the command this.Invoke(new LabelWriteByte(this.LogByte), ComBuffer1, lenRead) use the delegate LabelWriteByte to call the logByte method to write the first lenRead bytes of the ComBuffer1 byte array to the file and textbox.

Let us look at the command in the logByte method:

hexString += String.Format("{0:X2}", bWrite[jj]);  //write the bytes in string format


The string hexString show us a byte array in hexadecimal format (as we see the bytes in the file binary editor).

http://programmersheaven.com/articles/bluetooth/simpletranfile.gif
Figure 1. Bytes traffic on comm port sending a file of small size.


In the simple project SimpleTranConnect we use the methods described till now. It can be downloaded. In Figure 1. are presented the visual controls used in the project. There is also the serial port component that is not visible. In the textbox we see the hex representation of the bytes traffic on the comm port. The first sequence of bytes (80070010000004) is the connect packet sent to the server. Its length is 7 bytes. The response packet is the second sequence (A0070010000004) etc. The last sequence of bytes (810300) is a disconnect packet with the length of 3 bytes. If you would like to test the project, you have to install the FileTranServer (see the download).

Sending files of small size

When the file is small enough to put it in one packet, we send the file in one put packet. The body header of this packet is 0x82. Normally the body header is byte 0x02, only the body header of the last packet is 0x82. When the server receive the put packet with body header 0x02 it respond with the continue packet and when it receives body header 0x82, it respond with OK packet with the header 0xA0.

private void sendobex_Click(object sender, EventArgs e)
        {
            this.monitor.Text = "";
            this.progressBar1.Value = 0;
            this.lblMessage.Text = "Connecting...";
            this.tra = new TransferSerial();
            if (!tra.Connect(this.serialPort1)) return;
            this.com.Text = this.serialPort1.PortName.ToString();
            this.serialPort1.Write(tra.conn, 0, tra.conn.Length); //write the Connect packet
            this.Invoke(new LabelWriteByte(this.LogByte), tra.conn, tra.connLength);
            if (!tra.ConnectResponse(this.serialPort1))
            {
                this.lblMessage.Text = tra.ErrorMes();
                return; //didn't receive ok packet from server
            }
            this.Invoke(new LabelWriteByte(this.LogByte), tra.conn, tra.connLength);
            this.lblMessage.Text = "Connected.";
            file = tra.FileStreamToSend(this.destination.Text.Trim());
            if (file == null) return;
            this.fileName = tra.fileName;
            this.destName = this.destination.Text.Trim();
            //response connect ok
            try
            {
                //receive connect packet  A0  and send put    //   if (bufferx[0] ==  
(byte)0xA0)
                tra.CreatePutPacketAndSend(serialPort1, this.destName, this.fileName,  
this.file, this.streamRead);
                this.serialPort1.Write(tra.conn, 0, tra.connLength);
                streamRead = streamRead + tra.chunkReadFile;
                if (file.Length - streamRead > 0) this.transfering = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error:" + ex.Message.ToString());
            }


Listing 10. The button click event for sending small files.

In Listing 10 we try to connect to the server with the click on the button. If the connection (tra.Connect(this.serialPort1) is successful, we create the file stream (file) of the file we wish to send ( file=this.FileStreamToSend()). The method tra.CreatePutPacketAndSend create the put packet. We send it with this.serialPort1.Write(tra.conn, 0, tra.connLength);

If the file length in bytes is smaller or equal to bytes read, we call the DisconetService method, otherwise the control of the program execution is passed to the serial port datareceived event.

public byte[] PutPacket(string destName, string fileName, FileStream file, int streamRead)
        {
            // int chunk = 1024;
            int chunk = this.packetSize-nameF.Length-16;
//calculate the chunk of file bytes to read 
            if (file.Length < this.packetSize -nameF.Length-16) chunk = (int)file.Length;
            if (file.Length - streamRead < chunk) chunk = (int)file.Length -  
(int)streamRead;
            //read from file
            byte[] fiReadChunk = new byte[chunk];
            int      chunkR= file.Read(fiReadChunk, 0, chunk);
            byte[] nameF = new UnicodeEncoding().GetBytes(destName); 
//in the byte array nameF we store the destination file name
            //sum of the header length (in revers order). Look at bufxx.Write!
            int packetLength = (1 + chunkR + 2 + 1) + (4 + 1) + (2 + nameF.Length + 2 + 1) +  
2;
// chunkR+ nameF.Length+16
            MemoryStream ms = new MemoryStream();
            byte[] bufxx;
            if (file.Length - streamRead - chunkR <= 0)
            {
                bufxx = new byte[] { obex_put_final_bit };
            }
            else
            {
                bufxx = new byte[] { obex_put };
            }
            ms.Write(bufxx, 0, 1);
            //packet length        
            bufxx = BitConverter.GetBytes((UInt16)(packetLength));
            ms.Write(bufxx, 0, 2);
            bufxx = new byte[] { obex_version };
            ms.Write(bufxx, 0, 1);
            //name header
            bufxx = new byte[] { obex_name };
            ms.Write(bufxx, 0, 1);
            bufxx = BitConverter.GetBytes((UInt16)(nameF.Length + 3 + 2));
            ms.Write(bufxx, 0, 2);
            ms.Write(nameF, 0, nameF.Length);
            //null terminated
            bufxx = new byte[] { 0x00, 0x00 };
            ms.Write(bufxx, 0, 2);
            //  object length header
            bufxx = new byte[] { obex_length };
            ms.Write(bufxx, 0, 1);
            bufxx = BitConverter.GetBytes((file.Length));
            ms.Write(bufxx, 0, 4);
            //body header
            if (file.Length - streamRead - chunkR == 0)
            {
                bufxx = new byte[] { this.obex_end_body };
            }
            else
            {
                bufxx = new byte[] { obex_body };
            }
            ms.Write(bufxx, 0, 1);
           // bufxx = BitConverter.GetBytes((UInt16)(fiReadChunk.Length + 3));
            bufxx = BitConverter.GetBytes((UInt16)(chunkR + 3));
            ms.Write(bufxx, 0, 2);
            //  bufxx = fiReadChunk;
            ms.Write(fiReadChunk, 0, chunkR);
            //END of Body
            ms.Position = 0;
           this._chunkReadFile = chunkR;
            return ms.ToArray();
        }
Listing11. The C# program that create the put packet

The central part of the method CreatePutPacketAndSend is the method PutPacket (Listing 11) of the class TransferSerial. This method creates a put packet with header 0x02 or 0x82 for the last packet of the file. With the put packet we send the file data and other information. It contains also the headers and the data for the file length, destination file name and the packet size. In the last phase of the procedure (Listing 11) we create the body header and the file data that we read from the file stream. With the variable streamRead we control the portion of the read file stream. Usually the last put packet is of smaller size. In it we put the remaining bytes to send. We send the packet bytes stream (conn) to the comm port with the command serialPort2.Write(conn, 0, conn.Length).

In Figure 1. we see the sent and received bytes on the virtual serial port in hexadecimal format. There are the results of connect, transfer and disconnect process sending the file with contents "ABCDANDABCD". Its hexadecimal representation is "41424344414E4441424344"

The destination file name is c:\jumar.txt. The code for this project is in the downloadable file SimpleTranFile.zip.

Sending files of larger size - multiple put packet

When the file is of larger size, we send it in a sequence of put packets. How do we send the first packet is described in the previous section.

private serialPort1_DataReceived(object sender,System.IO.Ports.SerialDataReceivedEventArgs  e)
        {
            if (this.transfering)
            {
                //Receiving packet from server and sending put packet
                bool cont =tra.Receive(this.serialPort1 );
                if (this.disconnected)
                {
                    this.Invoke(new LabelWrite(this.LabelMes), ">>Client received success  
0xA0 when at end of file and disconnected. File length:" + this.file.Length.ToString() + "  
read:" + this.streamRead.ToString());
                }
                else
                {
                    this.Invoke(new LabelWrite(this.LabelMes), ">>Client received success  
0xA0 when at end of file. ");
                }
                 this.Invoke(new LabelWriteByte(this.LogByte), tra.conn , tra.connLength);
                if (!cont)  
                {
                    this.Invoke(new LabelWrite(this.LabelMes), "End file transfer." );
                  this.DisconnectService();
                    return; //didn't receive a continue packet
                }
                 //1) received confirm of connection 80 or put packet send
      //          this.confirm = true;
                this.Invoke(new LabelWrite(this.LabelMes), ">>Client recceive continue  
(packet 0x90) and send new put packet." + this.file.Length.ToString() + " read:" +  
this.streamRead.ToString());
                tra.CreatePutPacketAndSend(this.serialPort1 ,this.destName, this.fileName,  
this.file, this.streamRead);
                this.serialPort1.Write(tra.conn, 0, tra.connLength);
                streamRead = streamRead + tra.chunkReadFile;
                this.Invoke(new LabelWriteByte(this.LogByte), tra.conn, tra.connLength);
            }
        }


Listing 12. The serial port DataReceived event

The second and next packets traffic are controlled by the event serialPort1_DataReceived (Listing 12). We read the bytes received on the comm port with the method Receive(), which return a Boolean value cont (inue). If the value is true (we receive at the comm port the packet continue), we send the next packet and otherwise we terminate the transfer.

The method CreatePutPacketAndSend will create a put packet and will store it in a byte array conn. We write this array to the comm port (we send it to the server). The event serialPort1_DataReceived wait for the next received byte. (With the modification of the presented programs in this method you can control also the connect and first put packets).

Conclusion

To improve performance in the production code of your file transfer project, you should exclude the monitor part of the presented programs. You should build the client and the server function in the same project to have the possibility to send and receive files on booth devices. We separate the functions for pedagogical reason. We don't comment the server side project FileTranServer. In it we use the same technique as in the client project. It is possible to hide the complexity of encoding, decoding and transferring packet over Bluetooth serial ports in helper class and we can transfer and receive files with only a few line of code as is shown in the events sendobex_Click and serialPort1_DataReceived.

Downloads

SimpleTranConnect.zip, SimpleTranFile.zip, FileTranClient.zip, FileTranServer.zip

Internet Links

  1. OBEX. Transfer files between a Pocket PC and a Palm or a Symbian device
    By Viraj Chatterjee, July 24, 2002.
  2. IrDA INTEROPERABILITY
    Elements of the OBEX protocol.
  3. A Simple Guide To Mobile Phone File Transferring
    By hesicong
  4. Reading binary data in C Sharp
  5. An Introduction to Bluetooth programming in GNU/Linux
    Albert Huang
  6. SerialPort (RS-232 Serial COM Port) in C Sharp.NET
    Noah Coad


 

Other Views

corner

Recent Jobs

Official Programmer's Heaven Blogs
Web Hosting | Browser and Social Games | Gadgets

Popular resources on Programmersheaven.com
Assembly | Basic | C | C# | C++ | Delphi | Flash | Java | JavaScript | Pascal | Perl | PHP | Python | Ruby | Visual Basic
© Copyright 2011 Programmersheaven.com - All rights reserved.
Reproduction in whole or in part, in any form or medium without express written permission is prohibited.
Violators of this policy may be subject to legal action. Please read our Terms Of Use and Privacy Statement for more information.
Operated by CommunityHeaven, a BootstrapLabs company.