简体   繁体   中英

Reading out how long a button get's pressed (serial communication C#, WPF)

I just started with serial communication in C#, so maybe it's a quite easy question, but i spent the last three days googeling, and my problem is still unsolved, so i hope you can help me.

I have a device (it's a special kind of accordeon), with a lot of buttons on it. When i press a button, it's no problem to get data. I receive a different piece of ASCII code for every single button.

My problem is: I need to know the time, how long i pressed the button. But i only get data when i press the button (not when i release it).

Is there a possibility to find that out?

Below you see the program i wrote, just for getting an overviwev how serial communication works.

My Commport Class:

class MyCommPort : INotifyPropertyChanged
{
    //Implementierung von "INotifyPropertyChanged"
    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }


    //Felder
    private SerialPort _serialPort = null;
    private SolidColorBrush fillColor1 = Brushes.LightBlue;
    private SolidColorBrush fillColor2 = Brushes.LightBlue;
    private string text = null;


    //Eigenschaften
    public SerialPort SerialPort
    {
        get { return _serialPort; }
        set { _serialPort = value; }
    }

    public SolidColorBrush FillColor1
    {
        get { return fillColor1; }
        set
        {
            fillColor1 = value;
            OnPropertyChanged("FillColor1");
        }
    }

    public SolidColorBrush FillColor2
    {
        get { return fillColor2; }
        set
        {
            fillColor2 = value;
            OnPropertyChanged("FillColor2");
        }
    }

    public string Text
    {
        get { return text; }
        set
        {
            text = value;
            OnPropertyChanged("Text");
        }
    }

    //Konstruktor
    public MyCommPort()
    {
        //Neuer Port wird angelegt
        SerialPort = new SerialPort("COM3", 115200);

        //Port wird geöffnet
        SerialPort.Open();
        //Wenn Daten am Port ankommen wird Event gefeuert
        SerialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
    }

    //Destruktor
    ~MyCommPort()
    {
        //Port wird geschlossen
        SerialPort.Close();
    }


    private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
    {
        Thread.Sleep(10);
        //Neuer Port (Kopie von serialPort) wird angelegt ==> sender ist serialPort!
        SerialPort sp = (SerialPort)sender;

        //string DataIN = null;
        string DataIN = sp.ReadExisting();

        switch (DataIN)
        {
            case "(D07084)":
                FillColor1 = Brushes.Red;
                break;

            case "(D04081)":
                FillColor2 = Brushes.Red;
                break;

            default:                   
                break;
        }

        Text = DataIN;
        DataIN = null;

        Thread.Sleep(200);

        FillColor1 = Brushes.LightBlue;
        FillColor2 = Brushes.LightBlue;

    }
} 

My View:

    <Window x:Class="USB.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:USB"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>

    <Grid Grid.Column="0">
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <Ellipse Grid.Row="0" Fill="{Binding Path=FillColor1}" />
        <Ellipse Grid.Row="1" Fill="{Binding Path=FillColor2}" />
    </Grid>

    <TextBox Grid.Column="1" Text="{Binding Text}" Height="50" Width="200"/>
</Grid>

Codebehind of the View:

public partial class MainWindow : Window
{

    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = new MyCommPort();
    }

}

The answer I'm about to write might seem quite unreliable; however, it is what you got until someone comes up with some brilliant idea.

Since you're unable to get to the moment of button release, there's a trick that could be done if (and only if) your device keeps sending the same key data as long as the is key held pressed.

You need to define an interval that is too small for a human to press a key twice, and too big for a machine not to send the pressed key data twice. In this example, I assume that your key code is int , and I'll consider the pre-defined interval as 75 (milliseconds).

    private delegate void SerialPortKeyUpHandler(int keyCode, TimeSpan timeHeld);
    private event SerialPortKeyUpHandler SerialPortKeyUp;
    CancellationTokenSource cancellationTokenSource;
    private int keyCode;
    private const int accuracy = 75; //the interval I'm talking about

    private void StartCapturing(int keyCode)
    {
        this.keyCode = keyCode;
        DateTime start = DateTime.UtcNow;
        cancellationTokenSource.CancelAfter(accuracy);
        ParameterizedThreadStart threadStart = new ParameterizedThreadStart((object cancellationTokenObj) =>
        {
            CancellationToken cancellationToken = (CancellationToken)cancellationTokenObj;
            while (!cancellationToken.IsCancellationRequested) ; //wait until we make sure no more of the same key data is being sent
            SerialPortKeyUp?.Invoke(keyCode, DateTime.UtcNow.Subtract(start));
        });
        Thread thread = new Thread(threadStart);
        thread.Start(cancellationTokenSource.Token);
    }

    private void KeyDownCallback(object sender, KeyEventArgs e)
    {
        if (cancellationTokenSource == null || cancellationTokenSource.IsCancellationRequested)
        {
            cancellationTokenSource = new CancellationTokenSource();
            StartCapturing(e.KeyValue);
        }
        else
        {
            if (e.KeyValue == keyCode)
            {
                cancellationTokenSource.CancelAfter(accuracy);
            }
            else
            {
                cancellationTokenSource.Cancel();
                cancellationTokenSource.Dispose();
                cancellationTokenSource = new CancellationTokenSource();
                StartCapturing(e.KeyValue);
            }
        }
    }
    private void KeyUpCallback(int keyCode, TimeSpan heldTime)
    {
        Debug.WriteLine($"Key {keyCode} is up. The key was held for {heldTime.TotalSeconds.ToString("#.00")} second(s).");
    }

Now you have a SerialPortKeyUp event to register; to do so, type this down somewhere

 SerialPortKeyUp += KeyUpCallback;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM