Await a value change in c# - c#

I'm currently working on a project that involve a stepper motor using c# forms and arduino.
I need to know in c# when the motor stops his movement. To do that, I programmed arduino to send a string trough Serial when the movement is finished. Then the string is interpreted by c# so that the app can do other things after that.
I now want to have the same result as the example below, but using async await.
I stored the information that the motor stopped in:
movimentFinished = true;
I solved the problem in a terrible way using: while(movimentFinished = false){await Task.Delay(1)}
The difference between a while loop and an await is going to effect other stuff on my WinApp. The while blocks all the application until movimentFinished == true;
The logic that I want is simple:
MoveMotor();
await "value change"
//do stuff after it finishes
Any help/tip to do that?
Thank you for reading
//this funcion will receive an array from arduino as a reply
private void risposta(object sender, SerialDataReceivedEventArgs e)
{
riceved = port.ReadLine();
rispos = riceved;
this.Invoke(new EventHandler(Reply));
}
//this function will run every time a reply is sent from arduino through serial
private void Reply(object sender, EventArgs e)
{
//string risposValore = rispos.Substring(2, rispos.Length - 2);
char[] pcr = rispos.ToCharArray();
rispos = rispos.Trim();
if (pcr[0] == 'M' && pcr[1] == 'F' && pcr[2] == '1')
{
movementFinished = true;
}
}
//this funcion will send a serial command to arduino to run the motor
public void MoveTheMotor(int position)
{
int command = 1000;
int movement = command + (position)
string com1 = movement.ToString();
port.Write(com1 + "\n");
movementFinished = false;
}
private async void Demo()
{
MoveTheMotor(800);
//this while blocks everything until the value is true
while (movementFinished == false) { await Task.Delay(1); }
//do something after the motor finished
}

Instead of using a boolean, you need a signal. In the blocking world, this would be a ManualResetEventSlim or similar. In the asynchronous world, this would be some form of TaskCompletionSource. As such:
private TaskCompletionSource movementFinished;
private void Reply(object sender, EventArgs e)
{
...
if (pcr[0] == 'M' && pcr[1] == 'F' && pcr[2] == '1')
{
movementFinished.TrySetResult();
}
}
public void MoveTheMotor(int position)
{
...
movementFinished = new TaskCompletionSource();
}
private async void Demo()
{
MoveTheMotor(800);
await movementFinished.Task;
//do something after the motor finished
}

Related

Saving a bitmap from a variable gives "invalid parameter"

I need to save an image to disk that came from a web-cam between 5 and 10 seconds ago from when the "save" command comes in via serial port.
To get there, I have the webcam going into a pictureBox.Image (using opencv4), and then 2 Bitmap variables. Every 5 seconds a timer ticks, and Stored_bitmap_2 = Stored_bitmap_1, then Stored_bitmap_1 = (bitmap) pictureBox.Image.
When the right serial command comes in, I try to
Stored_image_2.Save("C:\Users\GreenWorld\Desktop\test.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
and I get a run-time error of invalid parameter.
When I do the same thing in a stand-alone project with some buttons (inside the button-click event handler), it works every time.
When I do this inside the serialPort_DataReceived handler, I get all kinds of cross-thread errors. So, I moved the save attempt to its own subroutine, and that fixed that but now this.
I am by no means a professional programmer, I'm an engineer with a simple problem and I can usually write a little simplistic code to fix my immediate issue. Please go easy on me in the explanation :-)
Sample code:
using OpenCvSharp;
using OpenCvSharp.Extensions;
namespace Weld_picture
{
public partial class Form1 : Form
{
// Create class-level accessible variables
int Welding_camera_ID = 1;
VideoCapture capture;
Mat frame;
Bitmap image;
private Thread camera;
bool isCameraRunning = false;
string Serial_command = "";
Bitmap Stored_image_1;
Bitmap Stored_image_2;
bool Image_saved = false;
string Station_ID = "GWM-PWS-01";
string File_name = "";
private void CaptureCamera() // from someone else's sample code
{
camera = new Thread(new ThreadStart(CaptureCameraCallback));
camera.Start();
}
private void CaptureCameraCallback() // from someone else's sample code
{
frame = new Mat();
capture = new VideoCapture(Welding_camera_ID);
capture.Open(Welding_camera_ID);
if (capture.IsOpened())
{
while (isCameraRunning)
{
capture.Read(frame);
image = BitmapConverter.ToBitmap(frame);
if (pictureBox1.Image != null)
{
pictureBox1.Image.Dispose();
}
pictureBox1.Image = image;
}
}
}
public Form1()
{
InitializeComponent();
SerialPort1.Open();
}
private void SerialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string dummy;
char CR = (char)0x0D;
while (SerialPort1.BytesToRead > 0)
{
Serial_command += (char)SerialPort1.ReadByte();
}
while (Serial_command.IndexOf(CR) > 0)
{
dummy = Serial_command.Substring(0, Serial_command.IndexOf(CR));
Serial_command = Serial_command.Substring(Serial_command.IndexOf(CR) + 1, (Serial_command.Length - (Serial_command.IndexOf(CR) + 1)));
Serial_command.Trim();
dummy.Trim();
proc_Process_serial_data(dummy);
}
}
//*************************************************************************************************************************************
/* the first timer is a 5-second interval. It's the "memory" function so that if/when the save-to-disk is triggered I can store the last-time-shutter-open image */
//*************************************************************************************************************************************
private void Timer_picture_interval_Tick(object sender, EventArgs e)
{
checkBox1.Checked = !checkBox1.Checked;
Timer_picture_interval.Stop();
Stored_image_2 = Stored_image_1;
Stored_image_1 = (Bitmap) pictureBox1.Image;
Timer_picture_interval.Start();
}
//*************************************************************************************************************************************
// the second timer is a 30-second interval. It's the way to turn capture off if the PLC/camera box somehow goes off-line
//*************************************************************************************************************************************
private void Timer_camera_powerdown_Tick(object sender, EventArgs e)
{
if (isCameraRunning)
capture.Release();
isCameraRunning = false;
Timer_picture_interval.Stop();
}
//*************************************************************************************************************************************
private void proc_Process_serial_data(string Serial_string)
{
if (Serial_string.IndexOf("Still here") > 0)
{
if (!isCameraRunning)
CaptureCamera();
isCameraRunning = true;
}
if (Serial_string.IndexOf("Sun's up") > 0)
{
Timer_picture_interval.Start();
Timer_camera_powerdown.Start();
}
if (Serial_string.IndexOf("It's dark") > 0)
{
if ((Stored_image_2 != null) && (!Image_saved)) // in case there's 2 subsequent requests to save the same thing (weld stutter)
{
File_name = "C:\\Users\\GreenWorld\\Desktop\\" + Station_ID + " D" + DateTime.Now.ToString("yyyy_MM_dd THH_mm_ss") + ".jpg";
Stored_image_2.Image.Save("C:\\Users\\GreenWorld\\Desktop\\test.bmp" , System.Drawing.Imaging.ImageFormat.Bmp );
Image_saved = true;
Timer_picture_interval.Stop();
}
Timer_camera_powerdown.Start();
}
}
}
}
You likely be able to fix the cros-threading error by simply calling your proc_Process_serial_data() method within an Invoke() call.
Change:
proc_Process_serial_data(dummy);
To:
this.Invoke((MethodInvoker)delegate ()
{
proc_Process_serial_data(dummy);
});
Also, these two lines aren't actually doing anything:
Serial_command.Trim();
dummy.Trim();
To Trim() the strings, you have capture the returned strings and re-assign them to the original variables:
Serial_command = Serial_command.Trim();
dummy = dummy.Trim();

struggling to add a follow up function with my speech recognition app c#

so for example i say "play next song" and my app does so, and i want a function that starts listening again for 3 seconds only for the words "one more" so it can skip one more time then listen again for 3 seconds, if the 3 seconds pass i want the loop statement to close.
heres what i've tried":
private async void Default_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
{
string speech = e.Result.Text;
switch (speech)
{
case "play next":
Speak.SpeakAsync("playing next");
keybd_event(VK_MEDIA_NEXT_TRACK, 0, KEYEVENTF_EXTENTEDKEY, IntPtr.Zero); //this line is just my "play next" function.
_next_song.RecognizeAsync(RecognizeMode.Multiple); //this line enables a different voice recognition function.
break;
}
//void to continue next song again
private async void _next_song_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
{
string speech = e.Result.Text;
_recognizer.RecognizeAsyncStop(); //this is my main recognizer that contains all the commands
while (true)
{
if (speech == "one more")
{
Speak.SpeakAsync("sure");
keybd_event(VK_MEDIA_NEXT_TRACK, 0, KEYEVENTF_EXTENTEDKEY, IntPtr.Zero);
return;
}
await Task.Delay(3000);
break;
}
_next_song.RecognizeAsyncCancel();
_recognizer.RecognizeAsync(RecognizeMode.Multiple);
}
the thing is, my code works, just not the way i want it to, when i say "play next" it plays next song and waits for me to say "one more" to skip one more time and starts listening again, it just doesn't break the chain when 3 seconds pass, only if it hears something else, then 3 seconds later, it goes back to the main _recognizer recognition function.
anyway i could achieve a followup command without using a different SpeechRecognition Module ?
would really appreciate any help!
i propose a solution if i have understood your problem, so i think you dont need to use two recognizers: i just use an int to trap the phase and thread to do the loop..
//directives needed for thread
using System.Threading;
using System.Threading.Tasks;
//--------------------------
public int phase;
public bool onemore;
public bool running;
private Thread thread;
public readonly AutoResetEvent _signalGo = new AutoResetEvent(false);
//To put in the constructor of the class
thread = new Thread(new ThreadStart(loop));
private void speechRecognizer_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
{
string speech = e.Result.Text;
lblDemo.Content = speech;
switch (speech)
{
case "play next":
if (phase != 0) return;
if (!running)
thread.Start();
else
_signalGo.Set();
Speak.SpeakAsync("playing next");
keybd_event(VK_MEDIA_NEXT_TRACK, 0, KEYEVENTF_EXTENTEDKEY, IntPtr.Zero);
break;
case "one more":
if (phase != 1) return;
onemore = true;
Speak.SpeakAsync("sure");
break;
}
}
public async void loop()
{
running = true;
while (running)
{
phase = 1;
await Task.Delay(3000);
//if sou say onemore=true, enter in loop
while(onemore)
{
onemore = false;
//if you say again onemore before end of 3s, so reloop
keybd_event(VK_MEDIA_NEXT_TRACK, 0, KEYEVENTF_EXTENTEDKEY, IntPtr.Zero);
await Task.Delay(3000);
}
phase = 0;
if (!running) break;
_signalGo.WaitOne();//wait _signalGo.set() to continue
if (!running) break;
}
}
//Dont forget to dispose the resources at the end of program
// for eaxmple in the destructor
// if your class is named toto
//destructor is ~toto
public class toto
{
//constructor
public toto()
{
}
//destructor
~toto()
{
_signalGo.Set();
running = false;
_signalGo.Dispose();
speechRecognizer.Dispose();
}
}

prolonged impulse for GPIO input C# UWP

A digital acoustic sensor is connected to my raspberry.
Everytime it detects sound, it sets the input HIGH.
But it only appears to be active for a couple of milliseconds.
How can i prolong the signal within my program that it stays up for 500ms?
It is a functionality which I know from PLC contollers.
Diagram
Start: sensor input
Q: prolonged signal
,
Here my approach with the answer of Michael:
But still, it doesn't hold for Task.Delay. It goes off directly.
public MainPage()
{
this.InitializeComponent();
GPIOinit();
Serial();
}
private void GPIOinit()
{
const int PIN_AKUSTIK = 19;
GpioController gpio = GpioController.GetDefault(); //GPIO-Controller mit Default belegen
pin_akustik = gpio.OpenPin(PIN_AKUSTIK);
}
public async void Serial()
{
//serial configuration
while (true)
{
//frequency of constant while loop 300ms
Sensor_Akustik();
}
}
public void Sensor_Akustik()
{
pin_akustik.ValueChanged += Pin_ValueChanged;
pin_akustik.SetDriveMode(GpioPinDriveMode.Input);
status_akustik = pin_akustik.Read().ToString();
textblock_DebugAkustik_live.Text = status_akustik;
Messenger.Default.Send<string, Page_Akustik>(status_akustik);
}
private void Pin_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args)
{
if (args.Edge == GpioPinEdge.RisingEdge)
{
sender.Write(GpioPinValue.High);
Task.Delay(3000).Wait();
}
}
You might refer to following code. You can try to update the latched output value for the pin if the pin is configured as an input.
private void InitGPIO()
{
var gpio = GpioController.GetDefault();
// Show an error if there is no GPIO controller
if (gpio == null)
{
pin = null;
GpioStatus.Text = "There is no GPIO controller on this device.";
return;
}
pin = gpio.OpenPin(LED_PIN);
pin.ValueChanged += Pin_ValueChanged;
pin.SetDriveMode(GpioPinDriveMode.Input);
}
private void Pin_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args)
{
if(args.Edge == GpioPinEdge.RisingEdge)
{
sender.Write(GpioPinValue.High);
Task.Delay(500).Wait();
}
}

Playback of Audio file on key press is delayed

I made a small program that plays back sounds when you press keys.
It uses a global keyboard hook to capture key presses and play back wav files using NAudio.
However playback lags on some computers and plays a few seconds after the key has been pressed. Could this be an HDD/SSD or CPU speed issue or is it a programming issue? What can be done to solve it?
Tried on 4 computers, 2 lagged, 2 did not.
My SSD/i7 - did not lag.
My HDD/Core2Duo - did not lag.
Friend's SSD/i7 - lagged.
Friend's HDD/i7 - lagged.
Program
Info
https://github.com/MattMcManis/Ink
Source
https://github.com/MattMcManis/Ink/tree/master/source/Ink
Download
https://github.com/MattMcManis/Ink/releases
App.xaml.cs
Start the Keyboard Listener.
// Application Startup
//
private void Application_Startup(object sender, StartupEventArgs e)
{
th = new Thread(() => RunKeyListener());
th.IsBackground = true;
th.Start();
th.Join();
}
// Keyboard Listener
//
private void RunKeyListener()
{
KListener.KeyDown += new RawKeyEventHandler(KListener_KeyDown);
}
// Key Down
//
void KListener_KeyDown(object sender, RawKeyEventArgs args)
{
Sound.KeyPressed(args);
}
MainWindow.xaml.cs
KeyboardListener Class is in here.
https://gist.github.com/Ciantic/471698
Sound.cs
private static string wavKeyChar = "Sounds\\character.wav";
private static string wavKeyNum = "Sounds\\number.wav";
public static WaveFileReader wav = null;
public static WaveOutEvent output = null;
// Key Pressed
//
public static void KeyPressed(RawKeyEventArgs args)
{
// Letters
if (args.Key >= Key.A && args.Key <= Key.Z)
{
PlaySound(wavKeyChar);
}
// Numbers
else if (args.Key >= Key.D0 && args.Key <= Key.D9)
{
PlaySound(wavKeyNum);
}
}
// Play Sound
//
public static void PlaySound(string sound)
{
wav = new WaveFileReader(sound);
output = new WaveOutEvent();
output.NumberOfBuffers = 3;
output.DesiredLatency = 500;
output.Init(wav);
output.Play();
}
Try to show a MessageBox or something, to understand if the delayed event is the sound itself or the keypress event.
If the MessageBox shows before the sound is played then it's not a problem of keyboard hook library.

How to pause for loop when pause button is clicked?

When I run the program and try to click the pause button, nothing happens. I am not sure how I can get this to work exactly. I have a bool variable called pause and pause is set to false. Once the pause button is clicked it should set that variable to true. Then the loop checks for that and should display a message to the user. Any help is greatly appreciated!
namespace Practice2
{
public partial class Form1 : Form
{
photocopier printer = new photocopier(500, 2500);
bool pause = false;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void btnStart_Click(object sender, EventArgs e)
{
if (checkText(txtNumberCopies.Text) == true)
{
int numberCopies = Convert.ToInt32(txtNumberCopies.Text);
int toner = Convert.ToInt32(lblTonerAmount.Text);
int paperCapacity = Convert.ToInt32(lblPaperAmount.Text);
if (toner <= 625 && paperCapacity <= 125)
{
txtMessage.Text = "Printer is low on Toner and Paper!";
}
else if (toner <= 625){
txtMessage.Text = "Printer Toner is low!";
}
else if (paperCapacity <= 125)
{
txtMessage.Text = "Printer Paper is low!";
}
else
{
txtMessage.Text = "Printing...";
txtMessage.Refresh();
for (int i = numberCopies; i != 0; i--)
{
int paper = Convert.ToInt32(lblPaperAmount.Text);
paper--;
if (paper == 480 || paper == 380 || paper == 400 || paper == 200)
{
MessageBox.Show("There is a paper Jam! Please remove the Jam and then hit the ok button to continue!", "Important Message", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
if (pause == true)
{
MessageBox.Show("Press the ok button when ready to continue", "Important Message", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
lblPaperAmount.Text = Convert.ToString(Convert.ToInt32(lblPaperAmount.Text) - 1);
lblTonerAmount.Text = Convert.ToString(Convert.ToInt32(lblTonerAmount.Text) - 1);
Thread.Sleep(1000);
}
txtMessage.Text = "Job is completed!";
}
}
}
private void btnAddPaper_Click(object sender, EventArgs e)
{
int paperAmount = Convert.ToInt32(lblPaperAmount.Text);
if (checkText(txtAddPaper.Text) == true && paperAmount <= 500)
{
lblPaperAmount.Text = Convert.ToString(paperAmount + Convert.ToInt32(txtAddPaper.Text));
}
else
{
txtMessage.Text = "Printer paper is at capacity!";
}
}
private bool checkText(string textBox)
{
if (textBox.Equals("") || textBox == null)
{
txtMessage.Text = "Please enter a value in the text box!";
return false;
}
return true;
}
private void btnReplaceToner_Click(object sender, EventArgs e)
{
lblTonerAmount.Text = Convert.ToString(printer.Toner);
}
private void btnPauseCancel_Click(object sender, EventArgs e)
{
pause = true;
}
}
}
The problem is that you're doing the work on the UI thread, so the UI thread is busy and can't process messages (e.g. button click). You need to do the work on a worker thread instead, using BackgroundWorker or Task.Run for instance.
A for loop is on the UI Thread so while the for loop is running you can't do anything with the UI. I suggest that you use a System.Windows.Forms.Timer to do the job. You set the interval to 1 and that will run pretty quickly, but not as quickly as a for loop, though. But interval = 1 is enough for you.
Let me show you:
Timer timer = new Timer () {Interval=1};
to create a new timer object.
enter
timer.Tick +=
in the constructer and press TAB twice and that should generate an event handler. Write the stuff you want to do in the event handler.
Call timer.Stop to pause the timer and timer.Start to start the timer.

Categories

Resources