This article reworks the previous few articles that use BT Serial and the connectivity so that USB virtual serial is used for communication. It covers in detail USB Serial connectivity between a UWP app and an Arduino devices as well as with a Windows 10 IoT-Core device (eg RPI).
Code Repository : https://github.com/djaus2/SurfPad
Status: Bluetooth and USB Serial connectivity between UWP app and Arduino device implemented.
Bluetooth Connectivity-UWP Software Bluetooth Connectivity-Win 10 IoT-Core Remote App
USB Serial Connectivity (this)
SurfPad States
The app reuses some code from a previous GitHub project: iotAzureSensors It reworks the SerialTerminal.xaml XAML page and its codebehind page SerialTerminal.xaml.cs form the IoTCoreMenu project within that repository project. Code now on GitHub. Look at the Pages/USBSerialTerminal.xaml.cs page.
When used as the connectivity between the UWP UI app an the remote app (sketch) on an Arduino device, connectivity is straight forward as the USB cable used to download the sketch from a development machine, and to also power the Arduino device, can also be used as a USB Serial conduit.
With the Raspberry PI things are a bit mre complex as the USB Serial cable used to power the RPI from the development machine is only a power connection. It dose not support USB Serial. In the early days of Windows 10 IOT-Core on a RPI, serial connectivity was a vexed question. The support for USB to SErial chipsets was very underdone*. Indeed the most popular chipset FTDI wasn’t supported. Also UART0 on Pins 9 and 10 was implemented. As a community discussion, I suggested using the FTDI driver for the RT Surface (Windows 8/8.1) be ported which was taken up and subsequently implemented. See the discussion and links at:
Win 10 IoT Core: FTDI Serial Driver on embedded101.com
* Silicon Labs Chipset (eg CP2102EK USB-UART Bridge Eval Kit) was supported and worked for me then.
Subsequently support for FTDI chipset was added to IOT-Core and the native UART (UART0) was also added.
So if you want to implement USB-Serial from a RPI to a desktop/laptop etc., you need to implement the USB-Serial at both end of the connection. At the desktop and at RPI. You may plug a USB-Serial dongle (eg FTDI ) into both ends and connect their DB9 with a serial crossover cable. Not as simple as with the Arduino device. Or you might use, say, a SparkFun USB to Serial Breakout connected to the RPI Tx and Rx 3.3V UART0 signals on Pins 8 and 10 respectively into RS232 Signals encoded in a USB connection that can be plugged via cable in to a desktop USB 2/3 Host port.
The outline of the connectivity code follows. The State Machine for the connectivity is covered elsewhere in these articles.
private async void ListAvailablePorts() { bool done = false; try { string aqs = SerialDevice.GetDeviceSelector(); var dis = await DeviceInformation.FindAllAsync(aqs); listofDevices.Clear(); if (numDevices != 0) { for (int i = 0; i < numDevices; i++) { if ((dis[i].Name.Contains("USB")) || (dis[i].Id.Contains("FTDI"))) this.listofDevices.Add(dis[i]); } //User selects the USB Serial device from the list DeviceListSource.Source = this.listofDevices; comPortInput.IsEnabled = true; ConnectDevices.SelectedIndex = -1; //Actual code (see GitHub repository) here contains code to automatically connect // ..if device Id (disi.Id is listed in the Json Configuration data. // .. or if only one device connect to it. } } catch (Exception ex) { } }
USBSerialTerminal ListAvailablePorts() method
public async Task<bool> ConnectSerial(DeviceInformation entry) { bool ret = false; try { serialPort = await SerialDevice.FromIdAsync(entry.Id); if (serialPort == null) {; return false; } // Configure serial settings serialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000); serialPort.ReadTimeout = TimeSpan.FromMilliseconds(1000); serialPort.BaudRate = MainPage.cBAUD; serialPort.Parity = SerialParity.None; serialPort.StopBits = SerialStopBitCount.One; serialPort.DataBits = 8; // Create cancellation token object to close I/O operations when closing the device ReadCancellationTokenSource = new CancellationTokenSource(); ret = true; } catch (Exception ex) { } return ret; }
USBSerialTerminal ConnectSerial() Method
public async void SendCh(char ch) { string msg = "" + ch; .... // Create the DataWriter object and attach to OutputStream dataWriteObject = new DataWriter(serialPort.OutputStream); //Launch the WriteAsync task to perform the write await WriteAsync(msg); ... } private async Task WriteAsync(string msg) { Task<UInt32> storeAsyncTask; if (msg.Length != 0) { // Load the text from the sendText input text box to the dataWriter object dataWriteObject.WriteString(msg); // Launch an async task to complete the write operation storeAsyncTask = dataWriteObject.StoreAsync().AsTask(); UInt32 bytesWritten = await storeAsyncTask; } else { } }
USBSerialTerminal WriteAsync() Method
private async void Listen() { ReadCancellationTokenSource = new CancellationTokenSource(); try { if (serialPort != null) { dataReaderObject = new DataReader(serialPort.InputStream); // keep reading the serial input while (true) { await ReadAsync(ReadCancellationTokenSource.Token); } } } catch (Exception ex) { } finally { // Cleanup once complete } }
USBSerialTerminal Listen() Thread
private async Task ReadAsync(CancellationToken cancellationToken) { Task<UInt32> loadAsyncTask; uint ReadBufferLength = 1024; // If task cancellation was requested, comply cancellationToken.ThrowIfCancellationRequested(); // 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); string currenbtRecvdText = Encoding.UTF8.GetString(bytes); ... ... } catch (Exception ex) { } } }
USBSerialTerminal RecvAsync Method
setup() { Serial.begin(9600); while (!Serial) { } Serial.print((char)StartedSignal); }
The Arduino Remote App (Sketch) setup() function
void loopUSBSerial() { thisByte = Serial.peek(); if (thisByte != -1) { thisByte = Serial.read(); //Each char is interpretted as byte representing a keypress //The byte is the id of button pressed + ' ' (so are printable switch (thisByte) { } }
The Arduino Remote App (Sketch) loop() function
The connectivity is the same as the desktop UWP code.