Unable to read from 2 serial devices in UWP application

3

Hoping someone here will be able to help me out. I'm creating a c# UWP app to run on a Raspberry Pi2 / Windows IOT core. I need to read the input from 2 serial devices (triggered by an external button / PLC) in order to calculate some values. I am able to read one of the devices at a time, but so far have been unable to get a value for both. I based my serial code off an example, and I'm trying to call it from the main page. No matter what I have changed so far, the first device returns a value, and the second does not (the awaited task in the serial handler immediately returns null, rather than waiting for a value to come through the serial port).

EDIT: After doing some additional troubleshooting, I have found the following - when I attempt to stop debugging, Visual Studio freezes (no UI interaction possible, but debug monitoring still updating), and the only way to stop this is to unplug the dock my laptop (and the serial to USB cables), at which point VS behaves normally again. Also, I seem to get the error "The requested resource is in use. (Exception from HRESULT: 0x800700AA)" on the second iteration of the while(true) loop on the serial port that does not work.

The code that calls the serial port is:

private async Task listenForDeviceInput()
    {
        string weight;
        string area;
        //var areaTask =  cognosCamera.Listen();
        //var  weightTask =  weighTable.Listen();
        Task <string>  areaTask =  cognosCamera.Listen();
        Task <string> weightTask =  weighTable.Listen();
        await Task.WhenAll(areaTask, weightTask);
        weight = await weightTask;
        area = await areaTask;


        weight = weight.TrimEnds("\r");
        area = area.TrimEnds("\r");
        AddNewHide(weight, area);
        saveHide();
        listenForDeviceInput(); //removed the await here to see what happens

    }

And the serial handling is here:

 // Copyright (c) Microsoft. All rights reserved.

using System;
using System.Collections.ObjectModel;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.Devices.Enumeration;
using Windows.Devices.SerialCommunication;
using Windows.Storage.Streams;
using System.Threading;
using System.Threading.Tasks;

namespace SerialDeviceHandler
{
    public sealed partial class SerialPort
    {
        /// <summary>
        /// Private variables
        /// </summary>
        private SerialDevice serialPort = null;
        DataWriter dataWriteObject = null;
        DataReader dataReaderObject = null;

        private ObservableCollection<DeviceInformation> listOfDevices;
        private CancellationTokenSource ReadCancellationTokenSource;



        /// <summary>
        /// ListAvailablePorts
        /// - Use SerialDevice.GetDeviceSelector to enumerate all serial devices
        /// - Attaches the DeviceInformation to the ListBox source so that DeviceIds are displayed
        /// </summary>


        /// <summary>
        /// comPortInput_Click: Action to take when 'Connect' button is clicked
        /// - Get the selected device index and use Id to create the SerialDevice object
        /// - Configure default settings for the serial port
        /// - Create the ReadCancellationTokenSource token
        /// - Start listening on the serial port input
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public async Task OpenSerialPort(string entry)
        {


            try
            {
                serialPort = await SerialDevice.FromIdAsync(entry);
                if (serialPort == null) return;



                // Configure serial settings
                serialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
                serialPort.ReadTimeout = TimeSpan.FromMilliseconds(1000);
                serialPort.BaudRate = 9600;
                serialPort.Parity = SerialParity.None;
                serialPort.StopBits = SerialStopBitCount.One;
                serialPort.DataBits = 8;
                serialPort.Handshake = SerialHandshake.None;



                // Create cancellation token object to close I/O operations when closing the device
                ReadCancellationTokenSource = new CancellationTokenSource();



                //Listen();
            }
            catch (Exception ex)
            {
                //status.Text = ex.Message;
                //comPortInput.IsEnabled = true;
                //sendTextButton.IsEnabled = false;
            }
        }



        /// <summary>
        /// WriteAsync: Task that asynchronously writes data from the input text box 'sendText' to the OutputStream 
        /// </summary>
        /// <returns></returns>


        /// <summary>
        /// - Create a DataReader object
        /// - Create an async task to read from the SerialDevice InputStream
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public  async Task<String> Listen()
        {
            string result;
            try
            {
                if (serialPort != null)
                {
                    dataReaderObject = new DataReader(serialPort.InputStream);

                    // keep reading the serial input
                    while (true)
                    {
                        result = await ReadAsync(ReadCancellationTokenSource.Token);
                        if(result != "Nothing" || result == null)
                        {
                            return result;
                        }
                    }
                }
                return "Failed";
            }
            catch (TaskCanceledException tce)
            {
                //status.Text = "Reading task was cancelled, closing device and cleaning up";
                CloseDevice();
                return "Task Cancelled";
            }
            catch (Exception ex)
            {
                //status.Text = ex.Message;
                return "Task Errored";
            }
            finally
            {
                // Cleanup once complete
                if (dataReaderObject != null)
                {
                    dataReaderObject.DetachStream();
                    dataReaderObject = null;

                }
            }
        }

        /// <summary>
        /// ReadAsync: Task that waits on data and reads asynchronously from the serial device InputStream
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        private async Task<string> 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;

            using (var childCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
            {
                // Create a task object to wait for data on the serialPort.InputStream
                loadAsyncTask = dataReaderObject.LoadAsync(ReadBufferLength).AsTask(childCancellationTokenSource.Token);

                // Launch the task and wait
                UInt32 bytesRead = await loadAsyncTask;
                if (bytesRead > 0)
                {
                    return dataReaderObject.ReadString(bytesRead);
                    //status.Text = "bytes read successfully!";
                }
                return "Nothing";
            }
        }

        /// <summary>
        /// CancelReadTask:
        /// - Uses the ReadCancellationTokenSource to cancel read operations
        /// </summary>
        public void CancelReadTask()
        {
            if (ReadCancellationTokenSource != null)
            {
                if (!ReadCancellationTokenSource.IsCancellationRequested)
                {
                    ReadCancellationTokenSource.Cancel();
                }
            }
        }

        /// <summary>
        /// CloseDevice:
        /// - Disposes SerialDevice object
        /// - Clears the enumerated device Id list
        /// </summary>
        private void CloseDevice()
        {
            if (serialPort != null)
            {
                serialPort.Dispose();
            }
            serialPort = null;

            //comPortInput.IsEnabled = true;
            //sendTextButton.IsEnabled = false;
            //rcvdText.Text = "";
            listOfDevices.Clear();
        }

        /// <summary>
        /// closeDevice_Click: Action to take when 'Disconnect and Refresh List' is clicked on
        /// - Cancel all read operations
        /// - Close and dispose the SerialDevice object
        /// - Enumerate connected devices
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void closeDevice_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                //status.Text = "";
                CancelReadTask();
                CloseDevice();
                //ListAvailablePorts();
            }
            catch (Exception ex)
            {
                //status.Text = ex.Message;
            }
        }
    }

As far as I can tell, the 2 serial port objects seem to be created correctly (based on the fact that I can get a value from each one if I comment out the initialisation of the other). I initialise the 2 serial objects as:

private async System.Threading.Tasks.Task InitializeSerialDevicesAsync()
    {
        weighTable = new SerialPort();
        cognosCamera = new SerialPort();
        await weighTable.OpenSerialPort(scaleComPort);
        await cognosCamera.OpenSerialPort(cameraComPort);

    }

I know I am probably just doing something stupid along the way, but any help would be greatly appreciated. At this point I am just trying to get it working on a normal PC before I have to start dealing with driver issues etc. on the Pi.

c#
uwp
raspberry-pi
windows-10-iot-core
asked on Stack Overflow Jan 15, 2018 by L Robbins • edited Jan 16, 2018 by Rita Han

1 Answer

0

OK - so I’ve solved the problem, but don’t really understand why. I switched the USB to serial cables to a different brand (From prolific to FTDI) and now the application functions correctly on both the laptop and the pi.

answered on Stack Overflow Jan 16, 2018 by L Robbins

User contributions licensed under CC BY-SA 3.0