The previous article in this series covered implementing an Arduino device as the remote app that send the UI configuration at start up to the UWP app so tha the UWP app can configure the UI for it as an array of buttons. The Arduino device executes a state machine. The article covers the BT connectivity from within the UWP app and the implementation of the UWP apps mirror of the state machine.

 

Code Repository : https://github.com/djaus2/SurfPad

Status: Bluetooth connectivity between UWP app and Arduino device implemented.

 

Articles

  1. The UI
  2. Json Configuration
  3. Text Output
  4. Bluetooth Connectivity-Connectivity
  5. Bluetooth Connectivity-Arduino Software

    Bluetooth Connectivity-UWP Software (this)
    Bluetooth Connectivity-Win 10 IoT-Core Remote App 

  6. USB Serial Connectivity

 

The Bluetooth Terminal

The app reuses some code from a previous GitHub project: iotAzureSensors 
It uses BluetoothSerialTerminal.xaml  XAML page and its codebehind page BluetoothSerialTerminal.xaml.cs form the IoTCoreMenu project within that repository project.

 

The BluetoothSerialTerminal (XAML) page:

image

 

When this page loads it lists the available BT device-services in the blue menu in the middle.

In this case Dev B from the HC-05, the Arduino Shield.is listed.

Normally there will be only be one device.service.If none then check that the device is paired.

You double click on it and its Id gets listed above and your good to go.

You then click on [Back] which returns you to the UI Menu (Main) page.

You can return to this page and press  [Stop Recv] to disconnect then reconnect.

 

The BluetoothSerialSerialTerminal page code behind outline is:

image_thumb[2]

 

InitializeRfcommDeviceService()

This gets the list of available BT Serial devices. You have to query RfcommDeviceService to get the list.

                DeviceInformationCollection DeviceInfoCollection = 
await DeviceInformation.FindAllAsync(RfcommDeviceService.GetDeviceSelector(RfcommServiceId.SerialPort));

 

Note that under UWP, opening a COM port, whether virtual or actual must be done with a handle to the device rather than just opening the COM port as one would classically

ConnectDevices_DoubleTapped()

Then gets the selected device-service and passes it to ConnectDevice_Click().

That then connects to the device-service  as outlined below

DeviceInformation DeviceInfo = ((PairedDeviceInfo)ConnectDevices.SelectedItem).DeviceInfo;// await DeviceInformation.
_service = await RfcommDeviceService.FromIdAsync(DeviceInfo.Id);
_socket = new StreamSocket();
await _socket.ConnectAsync(_service.ConnectionHostName, _service.ConnectionServiceName);
_Mode = Mode.JustConnected;
SendCh('0'); //Send("ACK0"); 
Listen();

It send the first acknowledgement ‘0’ to the device and then becomes the List thread
The _socket is what is used (as streams) to send and receive data over BT Serial.

SendCh()

The outline of this method is:

dataWriteObject = new DataWriter(_socket.OutputStream);
await WriteChar(ch); 

WriteChar()

The outline this method is:

Task<UInt32> storeAsyncTask;
byte byt = (byte)ch; ;
dataWriteObject.WriteByte(byt);
storeAsyncTask = dataWriteObject.StoreAsync().AsTask();
UInt32 bytesWritten = await storeAsyncTask;

 

The key declarations on this page are:

private StreamSocket _socket;
DataWriter dataWriteObject = null;
DataReader dataReaderObject = null;

ObservableCollection<PairedDeviceInfo> _pairedDevices;
private CancellationTokenSource ReadCancellationTokenSource;

enum Mode
{
    Disconnected,
    JustConnected,
    Connected,
    AwaitJson,
    JsonConfig
}
Mode _Mode = Mode.Disconnected;

Listen()

This is a continuous loop that calls ReadAsync():

ReadCancellationTokenSource = new CancellationTokenSource();
dataReaderObject = new DataReader(_socket.InputStream);
while (true) 
{                 
      await ReadAsync(ReadCancellationTokenSource.Token);
}

ReadAsync()

private async Task ReadAsync(CancellationToken cancellationToken)
     {
         Task<UInt32> loadAsyncTask;
         // Set InputStreamOptions to complete the asynchronous read operation when one or more bytes is available
         dataReaderObject.InputStreamOptions = InputStreamOptions.Partial;
         // Create a task object to wait for data on the serialPort.InputStream
         loadAsyncTask = dataReaderObject.LoadAsync(ReadBufferLength).AsTask(cancellationToken);
         // Launch the task and wait
         UInt32 bytesRead = await loadAsyncTask;
         if (bytesRead > 0)
         {
             try
             {
                 byte bytes  = new byte[bytesRead];
                 dataReaderObject.ReadBytes(bytes);
                 if (_Mode == Mode.JustConnected)
                 {
                     if ('1' == (char)bytes[0])
                     {
                         _Mode = Mode.AwaitJson;
                         SendCh('2'); //Send("ACK2"); 
                     }
                 }
                 else if (_Mode == Mode.AwaitJson)
                 {
                     if ('3' == (char)bytes[0])
                         recvdtxt = "";
                         _Mode = Mode.Connected;
                         SendCh('4'); //Send("ACK4"); 
                     }
                 }
                 else if (_Mode == Mode.Connected)
                 {
                     byte byt = bytes[0];
                     switch (byt)
                     {
                         case 47:  //'!'
                             _Mode = Mode.JsonConfig;
                             recvdtxt = "";
                             SendCh('/');
                             break;
                         default:
                             recvdtxt = "" + (char)rt[0];
                             await MainPage.MP.UpdateTextAsync(recvdtxt);
                             recvdtxt = "";
                             System.Diagnostics.Debug.WriteLine("bytes read successfully!");
                             break;
                     }
                 }
                 else if (_Mode == Mode.JsonConfig)
                 {
                          //Jason segment   
                 }
             }            
         }
     }


The Json segment of the code gets the Config string and send it to the app MainPage, signals the device to send the MainMenu, gets this and passes it to the app MainPage:

                 else if (_Mode == Mode.JsonConfig)
                 {
                     recvdtxt += Encoding.UTF8.GetString(bytes);
                     if (recvdtxt.Substring(recvdtxt.Length - 1) == EOStringStr)
                     {
                         await MainPage.MP.UpdateTextAsync(recvdtxt);//.Send string to app MainPage
                         if (recvdtxt.Substring(0, "{\"ElementConfig\":".Length) == "{\"ElementConfig\":")
                             SendCh('~');
                         else if (recvdtxt.Substring(0, "{\"MainMenu\":".Length) == "{\"MainMenu\":")
                             _Mode = Mode.Connected;
                         else
                         {
                                 //Error 
                         }
                         recvdtxt = "";
                     }

 

App MainPage

UpDateTextAsync()

The app MainPage UpDateTextAsync() outline is:

 private string config = "";
 internal async Task UpdateTextAsync(string recvdtxt)
 { // If the received text has ‘~’ on end then its a string. If not its just one char
     if(recvdtxt.Substring(recvdtxt.Length - 1,1) == EOStringStr)
     {
         if (recvdtxt.Substring(0, "{\"Config\":".Length) == "{\"Config\":")
         {
             config = recvdtxt;
             config = config.Replace(EOStringStr, "");
         }
         else if (recvdtxt.Substring(0, "{\"MainMenu\":".Length) == "{\"MainMenu\":")
         {
             string menus = recvdtxt;
             menus = menus.Replace(EOStringStr, "");
             string jsonData = "";  //Concatenate the two json strings into one
             Setup(jsonData);    // (Re)Configure the UI
         }
         else
         {
             await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
             { // Otherwise display the string in the ListView box
                 listView1.Items.Insert(0, recvdtxt.Substring(0, recvdtxt.Length - 1));
             });
         }

     }
     else
     { // Received text is just one char so look up the key that was pressed and list it
         char ch = recvdtxt[0];
         int index = ch - ((int)' ');
         foreach (uc.MyUserControl1 buts in buttons)
         {
             var but = from n in buts where n.Id == index select n.Text;

             if (but.Count() != 0)
             {
                 await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                 {
                     listView1.Items.Insert(0, but.First());
                 });
                 break;
             }
         }
     }
 }

Errata: The line above string jsonData = "” //Concatenate the two json strings into one

should be:

string jsonData = "[ " + config + " , " + menus + " ]"; //Concatenate the two json strings into one


Next: Win 10 IoT-Core version of the remote app