Windows 10 IOT serial - Working on RP2 but read se

2019-08-14 00:54发布

问题:

So I have been experimenting with the IOT serial sample and RP2 since the october release opened up the serial pins. Im using the windows iot serial sample. Have a few issues i cant figure out.

  1. The read data seems incomplete. I have an arduino outputting an analog input reading through the serial at 9600 baud. On the windows universial app, ill read one complete line then one incomplete line. For example: if the arduiono is outputting a value of "2.25", the app will read one line 2.25 then the next ".25"(without anything in front the period). Ive tried delays on the app side and arduino with no success.

  2. At times when trying to connect, it will throw a formatting exception. Sometimes can hit disconnect and reconnect and it will work fine. Others i have to reboot the arduino.

Im thinking its something in the code sample because i have the same issues if i run from laptop or raspberry pi.

Here is the c# for the program. i modified a little bit.

// 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 SerialSample
{    
public sealed partial class MainPage : Page
{
    /// <summary>
    /// Private variables
    /// </summary>
    private SerialDevice serialPort = null;
    DataWriter dataWriteObject = null;
    DataReader dataReaderObject = null;

    private ObservableCollection<DeviceInformation> listOfDevices;
    private CancellationTokenSource ReadCancellationTokenSource;

    public MainPage()
    {
        this.InitializeComponent();            
        comPortInput.IsEnabled = false;
        sendTextButton.IsEnabled = false;
        listOfDevices = new ObservableCollection<DeviceInformation>();
        ListAvailablePorts();
    }

    /// <summary>
    /// ListAvailablePorts
    /// - Use SerialDevice.GetDeviceSelector to enumerate all serial devices
    /// - Attaches the DeviceInformation to the ListBox source so that       DeviceIds are displayed
    /// </summary>
    private async void ListAvailablePorts()
    {
        try
        {
            string aqs = SerialDevice.GetDeviceSelector();
            var dis = await DeviceInformation.FindAllAsync(aqs);

            status.Text = "Select a device and connect";

            for (int i = 0; i < dis.Count; i++)
            {
                listOfDevices.Add(dis[i]);
            }

            DeviceListSource.Source = listOfDevices;
            comPortInput.IsEnabled = true;
            ConnectDevices.SelectedIndex = -1;
        }
        catch (Exception ex)
        {
            status.Text = ex.Message;
        }
    }

    /// <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
    /// - Add text to rcvdText textbox to invoke rcvdText_TextChanged event
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private async void comPortInput_Click(object sender, RoutedEventArgs e)
    {
        var selection = ConnectDevices.SelectedItems;

        if (selection.Count <= 0)
        {
            status.Text = "Select a device and connect";
            return;
        }

        DeviceInformation entry = (DeviceInformation)selection[0];         

        try
        {                
            serialPort = await SerialDevice.FromIdAsync(entry.Id);

            // Disable the 'Connect' button 
            comPortInput.IsEnabled = false;

            // 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;

            // Display configured settings
            status.Text = "Serial port configured successfully!\n ----- Properties ----- \n";
            status.Text += "BaudRate: " + serialPort.BaudRate.ToString() + "\n";
            status.Text += "DataBits: " + serialPort.DataBits.ToString() + "\n";
            status.Text += "Handshake: " + serialPort.Handshake.ToString() + "\n";
            status.Text += "Parity: " + serialPort.Parity.ToString() + "\n";
            status.Text += "StopBits: " + serialPort.StopBits.ToString() + "\n";                            

            // Set the RcvdText field to invoke the TextChanged callback
            // The callback launches an async Read task to wait for data
            rcvdText.Text = "Waiting for data...";

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

            // Enable 'WRITE' button to allow sending data
            sendTextButton.IsEnabled = true;
        }
        catch (Exception ex)
        {
            status.Text = ex.Message;
            comPortInput.IsEnabled = true;
            sendTextButton.IsEnabled = false;
        }
    }

    /// <summary>
    /// sendTextButton_Click: Action to take when 'WRITE' button is clicked
    /// - Create a DataWriter object with the OutputStream of the SerialDevice
    /// - Create an async task that performs the write operation
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private async void sendTextButton_Click(object sender, RoutedEventArgs e)
    {
        try
        {                
            if (serialPort != null)
            {
                // Create the DataWriter object and attach to OutputStream
                dataWriteObject = new DataWriter(serialPort.OutputStream);

                //Launch the WriteAsync task to perform the write
                await WriteAsync();
            }
            else
            {
                status.Text = "Select a device and connect";                
            }
        }
        catch (Exception ex)
        {
            status.Text = "sendTextButton_Click: " + ex.Message;
        }
        finally
        {
            // Cleanup once complete
            if (dataWriteObject != null)
            {
                dataWriteObject.DetachStream();
                dataWriteObject = null;
            }
        }
    }

    /// <summary>
    /// WriteAsync: Task that asynchronously writes data from the input text box 'sendText' to the OutputStream 
    /// </summary>
    /// <returns></returns>
    private async Task WriteAsync()
    {
        Task<UInt32> storeAsyncTask;

        if (sendText.Text.Length != 0)
        {
            // Load the text from the sendText input text box to the dataWriter object
            dataWriteObject.WriteString(sendText.Text);                

            // Launch an async task to complete the write operation
            storeAsyncTask = dataWriteObject.StoreAsync().AsTask();

            UInt32 bytesWritten = await storeAsyncTask;
            if (bytesWritten > 0)
            {                    
                status.Text = sendText.Text + '\n';
                status.Text += "Bytes written successfully!";
            }
            sendText.Text = "";
        }
        else
        {
            status.Text = "Enter the text you want to write and then click on 'WRITE'";
        }
    }

    /// <summary>
    /// rcvdText_TextChanged: Action to take when text is entered in the 'Read Data' textbox
    /// - Create a DataReader object
    /// - Create an async task to read from the SerialDevice InputStream
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private async void rcvdText_TextChanged(object sender, TextChangedEventArgs e)
    {
        try
        {
            if (serialPort != null)
            {
                dataReaderObject = new DataReader(serialPort.InputStream);
                await ReadAsync(ReadCancellationTokenSource.Token);
            }
        }
        catch (Exception ex)
        {
            if (ex.GetType().Name == "TaskCanceledException")
            {
                status.Text = "Reading task was cancelled, closing device and cleaning up";
                CloseDevice();
            }
            else
            {
                status.Text = ex.Message;
            }
        }
        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 ReadAsync(CancellationToken cancellationToken)
    {
        Task<UInt32> loadAsyncTask;

        uint ReadBufferLength = 128;

        // 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)
        {

            rcvdText.Text = dataReaderObject.ReadString(bytesRead);
            status.Text = bytesRead.ToString();
            //status.Text = "\nBytes read successfully!";
        }            
    }

    /// <summary>
    /// CancelReadTask:
    /// - Uses the ReadCancellationTokenSource to cancel read operations
    /// </summary>
    private 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;
        }          
    }        
}
}

Here is the Arduino Sketch:

void setup() 
{
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);

}

// the loop routine runs over and over again forever:
void loop() 
{
// read the input on analog pin 0:  
float sensorValue = analogRead(A3);
sensorValue = (sensorValue / 1023) * 4.30;

// print out the value you read:
Serial.print(sensorValue);
Serial.println("");  
delay(1);        // delay in between reads for stability

}

And Here is a link to a quick video showing the issue. And you would know that it connected the first time for the video hahaha.

Thanks in advance for all help and suggestions.

回答1:

I streamlined the code a good bit. I believe the cancellation tokens in the original code may have been part of the issue. Now it connects and reads every time:) Here is the code if anyone wants to try it out.

using System;
using System.Collections.Generic;
using System.IO;
using Windows.UI.Popups;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using System.Collections.ObjectModel;
using Windows.Devices.Enumeration;
using Windows.Devices.SerialCommunication;
using Windows.Storage.Streams;
using System.Threading;
using System.Threading.Tasks;



namespace Serial_Data_Read
{

public sealed partial class MainPage : Page
{

    private SerialDevice serialPort = null;

    DataReader dataReaderObject = null;

    public uint BytesReceived { get; }

    public MainPage()
    {
        this.InitializeComponent();
        ListAvailablePorts();

    }

    private async void ListAvailablePorts()
    {
        try
        {
            string aqs = SerialDevice.GetDeviceSelector();
            var dis = await DeviceInformation.FindAllAsync(aqs);

            var selectedPort = dis.First();
            serialPort = await SerialDevice.FromIdAsync(selectedPort.Id);


            serialPort.ReadTimeout = TimeSpan.FromMilliseconds(1000);
            serialPort.BaudRate = 9600;
            serialPort.Parity = SerialParity.None;
            serialPort.StopBits = SerialStopBitCount.One;
            serialPort.DataBits = 8;

            infoBox.Text = "Serial port configured successfully!\n ----- Properties ----- \n";
            infoBox.Text += "BaudRate: " + serialPort.BaudRate.ToString() + "\n";
            infoBox.Text += "DataBits: " + serialPort.DataBits.ToString() + "\n";
            infoBox.Text += "Handshake: " + serialPort.Handshake.ToString() + "\n";
            infoBox.Text += "Parity: " + serialPort.Parity.ToString() + "\n";
            infoBox.Text += "StopBits: " + serialPort.StopBits.ToString() + "\n";

            data.Text = "configuring port";

        }

        catch (Exception ex)
        {
            infoBox.Text = "OOps, Something went wrong! \n" + ex.Message ;
        }
    }

    private async void data_TextChanged(object sender, TextChangedEventArgs e)
    {
        dataReaderObject = new DataReader(serialPort.InputStream);
        try
        {
            var bytesRecieved = await dataReaderObject.LoadAsync(128);

            if (bytesRecieved > 0)
            {

                data.Text = dataReaderObject.ReadString(bytesRecieved).Trim();
            }

        }
        catch (Exception ex)
        {
            data.Text = ex.Message;
            ListAvailablePorts();
        }
        finally
        {
            if (dataReaderObject != null)
            {
                dataReaderObject.DetachStream();
                dataReaderObject = null;
            }
        }
    }    
}
}

and the xaml...

<Page
x:Class="Serial_Data_Read.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Serial_Data_Read"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
   Width="800" Height="480">

<Grid Background="{ThemeResource AppBarItemDisabledForegroundThemeBrush}">
    <TextBox x:Name="infoBox" HorizontalAlignment="Left" Margin="10,288,0,0" TextWrapping="Wrap" Text="Waiting to establish a connection" VerticalAlignment="Top" Height="182" Width="382"/>
    <TextBox x:Name="data" HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" Text="Waiting for data" VerticalAlignment="Top" Height="154" Width="780"  TextChanged="data_TextChanged" />
</Grid>