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 that 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.
Index of Articles
Next: Bluetooth Connectivity-Win 10 IoT-Core Remote App 4/4
Prev: Bluetooth Connectivity-Arduino Software 2/4
Code Repository: (Github) djaus2/SurfPad
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:
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:
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
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 threadThe _socket is what is used (as streams) to send and receive data over BT Serial.
The outline of this method is:
dataWriteObject = new DataWriter(_socket.OutputStream); await WriteChar(ch);
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;
This is a continuous loop that calls ReadAsync():
ReadCancellationTokenSource = new CancellationTokenSource(); dataReaderObject = new DataReader(_socket.InputStream); while (true) { await ReadAsync(ReadCancellationTokenSource.Token); }
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 = ""; }
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