Windows 10 IOT serial — работает на RP2, но чтение серийных данных не завершено

Итак, я экспериментировал с серийным образцом IOT и RP2 с тех пор, как в октябрьском выпуске были открыты серийные контакты. Я использую серийный образец Windows iot. Есть несколько проблем, которые я не могу понять.

  1. Прочитанные данные кажутся неполными. У меня есть Arduino, выводящая аналоговый ввод через последовательный порт на скорости 9600 бод. В универсальном приложении Windows плохо читается одна полная строка, а затем одна неполная строка. Например: если arduiono выводит значение «2,25», приложение будет читать одну строку 2,25, а затем следующую «0,25» (без чего-либо перед точкой). Я пробовал задержки на стороне приложения и Arduino, но безуспешно.

  2. Иногда при попытке подключения выдается исключение форматирования. Иногда можно отключить и снова подключиться, и все будет работать нормально. Другие я должен перезагрузить arduino.

Я думаю, что это что-то в примере кода, потому что у меня те же проблемы, если я запускаю с ноутбука или raspberry pi.

Вот С# для программы. я немного модифицировал.

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

Вот скетч Ардуино:

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

}

И здесь есть ссылка на короткое видео, показывающее проблему. И вы бы знали, что он подключился в первый раз для видео, хахаха.

Заранее спасибо за всю помощь и предложения.


person BigSquirmy    schedule 26.10.2015    source источник
comment
Похоже, вы не читаете достаточно рано во второй раз. Поставить точку останова на ReadAsync и сократить время ожидания?   -  person GGleGrand    schedule 28.10.2015
comment
Я попробую сегодня вечером и дам вам знать, как это происходит. Спасибо   -  person BigSquirmy    schedule 28.10.2015


Ответы (1)


Я немного упростил код. Я считаю, что токены отмены в исходном коде могли быть частью проблемы. Теперь он подключается и читает каждый раз :) Вот код, если кто хочет попробовать.

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

а ксамл...

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

person BigSquirmy    schedule 29.10.2015