dcsimg
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Make Bluetooth Work for You: Build a Sample Chat Application  : Page 2

Learn how to write applications that work over Bluetooth short-range networks, using the .NET Compact Framework. Extend the sample chat application in this story to build any kind of Bluetooth applications you'd like.


advertisement
Building the User Interface>
To create the sample application, start Visual Studio .NET 2003 and create a new Smart Device Application Project.

 /assets/articlefigs/5257.png
Figure 2: Build Form1 for the Main UI. Form1 is populated with three controls—a TextBox, a Button, and a MainMenu.

Let's first populate the default Form1 with the following controls:
  • TextBox (for entering the text of a chat message)
  • Button (for sending the text)
  • MainMenu

The populated Form1 is shown in Figure 2.

Communicating using Bluetooth
Windows CE 3.0—the operating system for Pocket PC devices—does not come with any API for Bluetooth communication. As such, you have to use other methods, such as platform invoke, to communicate using Bluetooth. The lack of an API for Bluetooth means that you are not able to program Bluetooth communications in the same way you use sockets for TCP/IP communications. Instead, Bluetooth is just another serial port. And so, Bluetooth communication is actually done via serial connections. You can see for yourself by going to Bluetooth Manager—>Tools—>Settings for All Devices—>Serial Port in your Pocket PC's main menu (see Figure 3).

 /assets/articlefigs/5258.png
Figure 3: Serial Connections. The Bluetooth connection is just another pair of serial ports, in this case ports 7 and 8.

The Bluetooth connection is actually mapped to two COM ports—one for incoming and one for outgoing traffic. On my test device the incoming port is COM7 and the outgoing port is COM8 (see Figure 3). We will program the Bluetooth application using serial communication techniques.

Unfortunately, the .NET Compact Framework class library does not contain managed classes for serial communication. You have to rely on the operating system calls to provide that. In particular, we need to make use of functions provided by Windows CE 3.0's "coredll.dll" library.

In the .NET CF, use the <DllImport()> attribute to import the library that you want to use. In our case, I have imported four functions from the coredll.dll library. They are:

  • CreateFile()—Opens a connection to a COM port.
  • ReadFile()—Reads data from a COM port.
  • WriteFile()—Writes data to a COM port.
  • CloseHandle()—Closes a COM port.

Author's Note: Many of the details involved in serial communications are beyond the scope of this article. For more information on building serial-comm applications in Windows CE, refer to http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wceseril/htm/cmconProgrammingSerialConnections.asp. The example in this article does not contain all the error-handling mechanisms required for serial communications.

The VB.NET code for the import is shown in Listing 1.

Now that you have the functions that you'll need, you can begin building the applications. Start by declaring some global variables:


    Dim infileHandler As Long
    Dim outfileHandler As Long
    Dim numReadWrite As Integer
    Dim t1 As System.Threading.Thread
    Dim stopThread As Boolean = False

We need to create a Connect menu item that, when tapped, will call the connect() method to open the serial connections. We need to open the connections to the inbound and outbound port so that data can be both received and sent. The code below handles the Connect menu selection:


    Private Sub MenuItem2_Click(ByVal sender As System.Object, _
                          ByVal e As System.EventArgs) _
                          Handles MenuItem2.Click
        connect()
        MenuItem2.Enabled = False '---disable the Connect item
        MenuItem3.Enabled = True '---enable the Disconnect item
    End Sub

The method we are using for reading incoming messages, ReadFile() is a blocking one. Therefore, we need to invoke a thread to continuously poll for incoming data while allowing our application to be responsive to users' input at the same time.



    Public Sub connect()
        '---port number for Bluetooth connection
        Dim inPort As Short = 7
        Dim outPort As Short = 8

        '---Opens the port for Bluetooth
        infileHandler = CreateFile("COM" & inPort & ":", _
                                    &HC0000000, 0, 0, 3, 0, 0)
        Application.DoEvents()
        outfileHandler = CreateFile("COM" & outPort & ":", _
                                    &HC0000000, 0, 0, 3, 0, 0)
        Application.DoEvents()

        '---invoke the thread to receive incoming messages
        stopThread = False
        t1 = New Threading.Thread(AddressOf receiveLoop)
        t1.Start()
    End Sub

The send() method writes a message to the COM port using the WriteFile() method. After a message is sent, a copy of the message is added to the TextBox control:



    Public Function send(ByVal message As String) As Integer
        '---send the message through the serial port
        Dim value As String = message & vbCrLf
        Dim retCode As Integer = WriteFile(outfileHandler, _
                                           stringToByteArray(value), _
                                           value.Length(), _
                                           numReadWrite, _
                                           0)
        txtMessageLog.Text += value
        Return retCode
    End Function

The receiveLoop() method continuously polls for incoming messages. As Windows controls are not thread-safe, accessing Windows controls within a thread will have unpredictable results. As such, you need to use a delegate method to call the updateMessage() method to update the TextBox control with the received message.



    Public Sub receiveLoop()
        '---receive the message through the serial port
        Dim inbuff(300) As Byte
        Dim retCode As Integer = ReadFile(infileHandler, _
                                          inbuff, _
                                          inbuff.Length, _
                                          numReadWrite, _
                                          0)
        Application.DoEvents()
        While True
            If retCode = 0 Or stopThread Then
                '---either error or stop is requested
                Exit While
            Else
                Dim updateDelegate As New _
                    myDelegate(AddressOf updateMessageLog)

                updateDelegate.Invoke(byteArrayToString(inbuff))
                ReDim inbuff(300)
                retCode = ReadFile(infileHandler, _
                                   inbuff, _
                                   inbuff.Length, _
                                   numReadWrite, _
                                   0)
                Application.DoEvents()
            End If
        End While
    End Sub

The myDelegate() method has the same signature as the updateMessageLog() method. myDelegate() is called when updating the TextBox control. You should not directly call the updateMessageLog() within the thread; only the main thread can directly call it.



    Public Delegate Sub myDelegate(ByVal str As String)
    Public Sub updateMessageLog(ByVal str As String)
        If str.Length > 0 Then
            txtMessageLog.Text += "-->" & str
        End If
    End Sub

Clicking the Send button invokes the send() method:



    Private Sub Button1_Click(ByVal sender As System.Object, _
                              ByVal e As System.EventArgs) _
                              Handles Button1.Click
        If send(txtMessage.Text) = 0 Then
            MsgBox("Error sending message.")
        End If
    End Sub

Finally, clicking on the Disconnect menu item invokes the disconnect() method:



    Private Sub MenuItem3_Click(ByVal sender As System.Object, _
                                ByVal e As System.EventArgs) _
                                Handles MenuItem3.Click
        disconnect()
        MenuItem2.Enabled = True '---enable the Connect button
        MenuItem3.Enabled = False '---disable the Connect button
    End Sub

The disconnect() method will set a global flag for the thread to stop receiving incoming messages. It will also close the two open ports:



    Public Sub disconnect()
        stopThread = True
        CloseHandle(infileHandler)
        CloseHandle(outfileHandler)
    End Sub

Byte to String and Back Again
Throughout this application, we have used two supporting methods: stringToByteArray() and byteArrayToString(). These two methods are necessary as the ReadFile() and WriteFile() methods both take in a byte array containing the message to be read and sent, respectively.

The stringToByteArray() method converts a string into a byte array:



    Public Function stringToByteArray(ByVal str As String) As Byte()
        '---e.g. "abcdefg" to {a,b,c,d,e,f,g}
        Dim s As Char()
        s = str.ToCharArray
        Dim b(s.Length - 1) As Byte
        Dim i As Integer
        For i = 0 To s.Length - 1
            b(i) = Convert.ToByte(s(i))
        Next
        Return b
    End Function

The byteArrayToString() method converts a byte array into a string:



    Function byteArrayToString(ByVal b() As Byte) As String
        '---e.g. {a,b,c,d,e,f,g} to "abcdefg" 
        Dim str As String
        Dim enc As System.Text.ASCIIEncoding
        enc = New System.Text.ASCIIEncoding
        str = enc.GetString(b, 0, b.Length())
        Return str
    End Function

When sending a message, the message to be sent would need to be converted to a byte array, and vice versa, when a message is received as a byte array, it needs to be converted back to a string so that it can be displayed on the device.



Thanks for your registration, follow us on our social networks to keep up-to-date