I'm sending a command via C# from PC (directly via USB) to Arduino to trigger a relay switch. There is a delay of slightly less than a second for the relay to respond. That delay is too long for my needs.
Is that a normal delay between PC and Arduino and relay switch or is there anything I can change in the codes below?
Below are my C# commands followed by the Arduino sketch
C#:
public Form1()
{
InitializeComponent();
serialPort1.PortName = "COM3";
serialPort1.BaudRate = 115200;
serialPort1.Open();
}
Button events:
private void btnSolenoid1On_Click(object sender, EventArgs e)
{
serialPort1.Write("1");
}
private void btnSolenoid1Off_Click(object sender, System.EventArgs e)
{
serialPort1.Write("0");
}
If I want to loop the ON/OFF commands above, I have to add a Sleep(1000) statement between the commands. But that's too much of a delay.
ARDUINO SKETCH:
//Relay set to low from Arduino turns switch on; high turns it off
const int RELAY_1 = 22;
int intSolenoid1 = 0;
String strSolenoid1 = "";
void setup()
{
Serial.begin(115200);
pinMode(RELAY_1, OUTPUT);
//Turn relay off at startup.
digitalWrite(RELAY_1, HIGH);
}
void loop()
{
while (Serial.available()>0) { //Wait for user input
strSolenoid1 = Serial.readString();
intSolenoid1 = strSolenoid1.toInt();
switch (intSolenoid1)
{
case 0:
digitalWrite(RELAY_1, HIGH);
break;
case 1:
digitalWrite(RELAY_1, LOW);
break;
}
}
I think there is a flaw in the sketch. You want it to act on a single byte of data but you are reading a string. If there is more than one byte of data in the input buffer then all the bytes are being read in. For example, if you sent "0" then "1", then the readString could see "01", and the conversion of that to an int is 1. So essentially it would never see the off command. The opposite is also a problem. "10" would become an int 10 and fall through the case. Re- writing the sketch as #frarugi87 suggested:
while(Serial.available()>0)
{
switch(Serial.read())
{
case '0':
digitalWrite(RELAY_1, HIGH);
break;
case '1':
digitalWrite(RELAY_1, LOW);
break;
}
}
would not only run faster, but also run as you intended.
Related
I'm trying to figure out, how to control two servos separately from WinForms C# desktop application.
In C# initialization:
myport.PortName = "COM6";
myport.BaudRate = 9600;
myport.Open();
Controlled by trackBar1:
private void trackBar1_Scroll(object sender, EventArgs e)
{
if (myport.IsOpen)
{
myport.WriteLine(trackBar1.Value.ToString());
}
}
Arduino code moves servos with pin 9 and 11, one goes to left, anther to right side synchronously :
#include <Servo.h>
Servo servo1;
Servo servo2;
int val;
void setup() {
Serial.begin(9600);
servo1.attach(9);
servo2.attach(11);
}
void loop() {
val = Serial.parseInt();
if(val != 0){
servo1.write(val);
servo2.write(val);
}
}
to control direction, I can create separate function void for servo1 and servo2 with different angle value, but I can't figure out, how to get separate control for each servo from C# application. I'm not sure, what I have to add in Arduino uploaded code and C#, for example, if I want control servo2 from pin 11 with trackBar2 and servo1 from pin 9 with trackBar1:
private void trackBar2_Scroll(object sender, EventArgs e)
{
if (myport.IsOpen)
{
myport.WriteLine(trackBar2.Value.ToString());
}
}
Any advice, guide or example would be very helpful
One option would be, instead of just reading ints on your Arduino, wait for character 'A' or 'B', then read an int. If the character is 'A' move servo1, if the character is 'B' move servo2. In your .net app, Write 'A' or 'B' before sending the trackbar value
Note: Untested
// in Arduino
void loop() {
switch(Serial.read())
{
case 'A':
servo1.write(Serial.parseInt());
break;
case 'B':
servo2.write(Serial.parseInt());
break;
}
}
// in c#
myport.Write('B')
myport.WriteLine(trackBar2.Value.ToString());
I'm trying to communicate back and forth with an Arduino via serial through a C# program. I've got a basic working example, but it's not pretty and not fault-less. I've included it at the end. I have done some research online, but it's difficult to look for something when you don't know all the details and intricacies of what you're looking for.
I'm under the impression that good serial communication protocol should include a handshake, send data and wait for response, receive data and send response, data clocking, etc. My question is how best to implement this. My existing code is one-way, from C# to the arduino's serial port, and it just sends the data and assumes that it was received. It's also very simple, sending just two bytes (a 1 or 0 to turn on or off an LED, and a 0-255 to control the LED's brightness via PWM). I'd like to be able to send sensor data or confirmation that the data was received and acted on, back from the arduino to the program.
I'm also curious about syncing. Does that go hand in hand with the handshake (no pun intended)? How can I reliably send data either way and be assured that nothing will be lost due to being out of sync?
Anywho, I've rambled, sorry. Hopefully someone can shed a little learning light on this subject. Here's my current messy code:
Arduino:
#define LEDPin 9
byte SerialCmd = 0;
byte SerialVal = 0;
void setup() {
Serial.begin(9600);
pinMode(LEDPin, OUTPUT);
digitalWrite(LEDPin, LOW);
}
void loop() {
while(Serial.available() > 1) {
SerialCmd = Serial.read();
SerialVal = Serial.read();
switch(SerialCmd) {
case 0:
digitalWrite(LEDPin, LOW);
break;
case 1:
analogWrite(LEDPin, SerialVal);
break;
}
}
}
C#:
using System;
using System.Windows.Forms;
namespace ArduinoTest2
{
public partial class Form1 : Form
{
byte[] SerialData = new byte[] { 0, 0 };
public Form1()
{
InitializeComponent();
serialPort1.PortName = "COM3";
serialPort1.BaudRate = 9600;
serialPort1.Open();
}
public void SerialWrite()
{
serialPort1.Write(SerialData, 0, 2);
}
private void button1_Click(object sender, EventArgs e)
{
SerialData[0] = 1;
SerialWrite();
}
private void button2_Click(object sender, EventArgs e)
{
SerialData[0] = 0;
SerialWrite();
}
private void trackBar1_Scroll(object sender, EventArgs e)
{
SerialData[1] = Convert.ToByte(trackBar1.Value);
label1.Text = trackBar1.Value.ToString();
SerialWrite();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
serialPort1.Close();
}
}
}
I use this code to initialize a serial port in C# :
serialPort.PortName = cboCOMPort.Text;
serialPort.BaudRate = Convert.ToInt32(cboBaudRate.Text);
serialPort.Parity = Parity.None;
serialPort.DataBits = 8;
serialPort.StopBits = StopBits.One;
serialPort.Handshake = Handshake.None;
serialPort.WriteTimeout = 500;
serialPort.ReadTimeout = 500;
serialPort.DataReceived += new SerialDataReceivedEventHandler(SerialDataReceived);
serialPort.Open();
then use this code to read data from serial port :
private void SerialDataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort serialPort = (SerialPort)sender;
strDataReceived = serialPort.ReadExisting();
ShowSerialOutput(strDataReceived);
}
and write :
private void SerialDataSend(string strCommand)
{
serialPort.WriteLine(strCommand);
}
My problem : when a device (example a switch) connects to the serial port and executes a command I have written, my program has to wait for the switch finishes executing this command before writing a new command to the serial port. How to check if the switch is finished? (when finished, the switch will send some message contain the keywords like 'completed', 'finished',...). I have tried to use this code but not work :
while(true)
{
if(serialPort.ReadLine().Contains("completed"))
serialDataSend(nextCommand);
}
Sorry for not clearly explaining my problem.
**Example my problem : **
I connect to the switch using serial port and use my program to read/write data. I want to copy a large file from server to the switch using this command : cp tftp://10.0.0.1/file.tgz /var/tmp/file.tgz. I use my program to write this command to the serial port and the switch executes this command. The file is very large so the program need to wait the file copied completely before sending the next command. When finished, the switch show message "Completed". That is my problem : how to check the copy process completed to write the new command.
I will try to approach your problem. Probably will need to rephrase as more information might come from you :)
As I understand it you have more than 1 command that you are sending to the device. Something like a command-list.
As you already have the event for data reception you could also use it to verify whether the completed keyword has been send and set a flag.
EDIT: The flag you can use for the while-wait-loop as you already do.
Your program will wait there until your device confirms the completion and jump out of the loop. Then you need to reset the flag for the next waiting loop.
public class DeviceCommunication
{
bool FlagToProceed = false;
private void MainJob()
{
// send command 1...
SerialDataSend("cp tftp://10.0.0.1/file.tgz /var/tmp/file.tgz.");
// wait
while(!FlagToProceed)
{}
// reset the flag
FlagToProceed = false;
// send command 2 ...
SerialDataSend("WhatEverComesNext");
// wait
while(!FlagToProceed)
{}
// reset the flag
FlagToProceed = false;
}
private void SerialDataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort serialPort = (SerialPort)sender;
strDataReceived = serialPort.ReadExisting();
ShowSerialOutput(strDataReceived);
// check whether affirmation has been received and open the gates
if(strDataReceived.Contains("completed"))
{
FlagToProceed = true;
}
}
private void SerialDataSend(string strCommand)
{
serialPort.WriteLine(strCommand);
}
}
This waiting technique is not a very good one! It is just the closest to your posted code. If you want to use it, I would suggest to have a timer running and break out of the while - loop if timeout is reached. Otherwise you might keep stuck in the while loop if the device decides not to confirm with "complete" or data just gets lost during transmission.
Another possibility to solve your problem could be to apply an asynch / await approach.
EDIT: be ware of case sensitivity! "Complete" & "complete" is not the same for the Contains method!
You need to use a state machine and delegates to achieve what you are trying to do. See the code below, I recommend doing all this in a separate thread other then Main. You keep track of the state you're in, and when you get a response you parse it with the correct callback function and if it is what you are expecting you move onto the next send command state.
private delegate void CallbackFunction(String Response); //our generic Delegate
private CallbackFunction CallbackResponse; //instantiate our delegate
private StateMachine currentState = ATRHBPCalStateMachine.Waiting;
SerialPort sp; //our serial port
private enum StateMachine
{
Waiting,
SendCmd1,
Cmd1Response,
SendCmd2,
Cmd2Response,
Error
}
private void do_State_Machine()
{
switch (StateMachine)
{
case StateMachine.Waiting:
//do nothing
break;
case StateMachine.SendCmd1:
CallbackResponse = Cmd1Response; //set our delegate to the first response
sp.Write("Send first command1"); //send our command through the serial port
currentState = StateMachine.Cmd1Response; //change to cmd1 response state
break;
case StateMachine.Cmd1Response:
//waiting for a response....you can put a timeout here
break;
case StateMachine.SendCmd2:
CallbackResponse = Cmd2Response; //set our delegate to the second response
sp.Write("Send command2"); //send our command through the serial port
currentState = StateMachine.Cmd2Response; //change to cmd1 response state
break;
case StateMachine.Cmd2Response:
//waiting for a response....you can put a timeout here
break;
case StateMachine.Error:
//error occurred do something
break;
}
}
private void Cmd1Response(string s)
{
//Parse the string, make sure its what you expect
//if it is, then set the next state to run the next command
if(s.contains("expected"))
{
currentState = StateMachine.SendCmd2;
}
else
{
currentState = StateMachine.Error;
}
}
private void Cmd2Response(string s)
{
//Parse the string, make sure its what you expect
//if it is, then set the next state to run the next command
if(s.contains("expected"))
{
currentState = StateMachine.Waiting;
backgroundWorker1.CancelAsync();
}
else
{
currentState = StateMachine.Error;
}
}
//In my case, I build a string builder until I get a carriage return or a colon character. This tells me
//I got all the characters I want for the response. Now we call my delegate which calls the correct response
//function. The datareceived event can fire mid response, so you need someway to know when you have the whole
//message.
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string CurrentLine = "";
string Data = serialPortSensor.ReadExisting();
Data.Replace("\n", "");
foreach (char c in Data)
{
if (c == '\r' || c == ':')
{
sb.Append(c);
CurrentLine = sb.ToString();
sb.Clear();
CallbackResponse(CurrentLine); //calls our correct response function depending on the current delegate assigned
}
else
{
sb.Append(c);
}
}
}
I would put this in a background worker, and when you press a button or something you can set the current state to SendCmd1.
Button press
private void buttonStart_Click(object sender, EventArgs e)
{
if(!backgroundWorker1.IsBusy)
{
currentState = StateMachine.SendCmd1;
backgroundWorker1.RunWorkerAsync();
}
}
Background worker do work event
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
if (backgroundWorker1.CancellationPending)
break;
do_State_Machine();
Thread.Sleep(100);
}
}
I am sending a character from visual studio C# to the serial port and I want arduino to read the character and do some work. But, its not reading it correctly. I have tested the arduino program using the serial monitor and it is working perfectly.Here's my C# code.
private void startBit_Click(object sender, EventArgs e)
{
char start = '!';
byte[] buffer = new byte[] { Convert.ToByte(start) };
timerRealTimeData.Enabled = true;
serialPort1.PortName = "COM29";
serialPort1.BaudRate = 9600;
serialPort1.DtrEnable = true;
serialPort1.Open();
serialPort1.Write(buffer, 0, 1);
if (serialPort1.IsOpen)
{
Start.Enabled = false;
Stop.Enabled = true;
}
}
Here's the arduino code for reading the serial port
char incomingByte;
void setup()
{
Serial.begin(9600);
pinMode(13,OUTPUT);
}
void loop()
{
if(Serial.available() > 0) {
incomingByte = Serial.read();
if(incomingByte=='!')
{
digitalWrite(13,HIGH);
delay(2000);
digitalWrite(13,LOW);
}
}
}
I am stuck on this for a long time.Any help is appreciated
Try to read the equivalent Ascii (33) using an int instead of a char. Also change the line
if(Serial.available() > 0)
To
if(Serial.available())
Some reference for you. Here
First of all, create a function where you open the serial port, and a function where you close the serial port. Your click handler startBit_Click opens the serial port, which means that if the handler is called again, the port is already open and an exception will be generated. (unless you have other code not shown that closes the serial port?)
Second, I am guessing you are connected to the arduino through the USB port?
If so, DtrEnable should be false (default), as you have no XON/XOFF software handshaking.
You should also examine the buffer in the debug window, set a breakpoint after the buffer is assigned the character and check that the character you send is indeed what you think it is.
I developed an application that sends data for an Arduino by the serial port, but I can't understand how I can receive it on the Arduino. I send a string by the serial port for the Arduino and the Arduino receives it, but it does not work in my code (on the Arduino, I receive a byte at a time).
Update: it's working ;)
The code in C# that sends data:
using System;
using System.Windows.Forms;
using System.Threading;
using System.IO;
using System.IO.Ports;
pulic class senddata() {
private void Form1_Load(object sender, System.EventArgs e)
{
//Define a serial port.
serialPort1.PortName = textBox2.Text;
serialPort1.BaudRate = 9600;
serialPort1.Open();
}
private void button1_Click(object sender, System.EventArgs e)
{
serialPort1.Write("10"); //This is a string. The 1 is a command. 0 is interpeter.
}
}
The Arduino code:
I have Update the Code
#include <Servo.h>
Servo servo;
String incomingString;
int pos;
void setup()
{
servo.attach(9);
Serial.begin(9600);
incomingString = "";
}
void loop()
{
if(Serial.available())
{
// Read a byte from the serial buffer.
char incomingByte = (char)Serial.read();
incomingString += incomingByte;
// Checks for null termination of the string.
if (incomingByte == '0') { //When 0 execute the code, the last byte is 0.
if (incomingString == "10") { //The string is 1 and the last byte 0... because incomingString += incomingByte.
servo.write(90);
}
incomingString = "";
}
}
}
Some things which make my eyebrow raise:
serialPort1.Write("1");
This will write exactly one byte, the 1, but no newline and no trailing NUL-Byte.
But here you are waiting for an additional NUL byte:
if (incomingByte == '\0') {
You should use WriteLine instead of Write, and wait for \n instead of \0.
This has two side effects:
First: If there is some buffering configured, then there is a certain chance, than a new line will push the buffered data to the Arduino. To be certain you have to dig through the docs at MSDN.
Second: This makes your protocol ASCII-only. This is important for easier debugging. You can then use a plain terminal program like Hyperterm or HTerm (edit) or even the Serial Monitor in the Arduino IDE itself (edit) to debug your Arduino-Code without worrying about bugs in your C# code. And when the Arduino code works you can concentrate on the C# part. Divide et impera.
Edit: Another thing I noticed after digging out my own Arduino:
incomingString += incomingByte;
....
if (incomingByte == '\n') { // modified this
if(incomingString == "1"){
This will of course not work as expected, because the string will contain "1\n" at this point. Either you compare to "1\n" or move the += line after the if.
You could alternatively try using the Firmata library - it's a much better way of having standard firmware on the Arduino and managing it from .net
I believe, Firmata 2.0+ has support for I2C and servo control.
http://firmata.org/