Reputation: 23
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();
}
}
Upvotes: 0
Views: 57
Reputation: 1943
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;
Upvotes: 1