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());
Related
I am building a program in C# to be used in one of my course at a college to demonstrate how Asynchronous connections work using RS-232 and two computers connected together. My course is not about programming, but data networks, so the connectivity is what I am looking for.
picture 1 - sample layout of GUI using Visual Studio 2015
One of the features I want to implement in my program is to show how a Master-slave, simplex connection works (i.e. the program can choose between been a master to send input from the keyboard; or slave to only receive information and print it on a textbox).
What I have already is the capability of initializing the serial port with specific characteristics (baud rate, data bits, stop bits, etc). This features are selected using combo boxes from the GUI, and assigned to the port when the user clicks a button to "open the port".
What I don't know is how to create the "slave" part of the program. My idea of what I could do is, after you choose the program to be "slave", you open the port waiting for some sort of flag or event to trigger when the input buffer has data stored.
I've been reading several forums and I can't find anything similar to what I need. I have, however, tested multiple alternatives that I believed would bring me closer to what I need with little to no result. I come to ask for an idea of what I could be doing wrong, or suggestions on how to tackle this problem. The problematic lines are bolded (or 2 stars ( * ) ):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
namespace SerialCommTester
{
public partial class frmSerialComm : Form
{
static SerialPort _PuertoSerial;
public frmSerialComm()
{
InitializeComponent();
getAvailablePorts();
}
//---------------------------------my functions--------------------------------------
void getAvailablePorts()
{
string[] ports = SerialPort.GetPortNames();
cmbPortList.Items.AddRange(ports);
}
void activatePort()
{
//Note that all the combo boxes are named somewhat accordingly to what the information they are meant to display.
if (cmbPortList.Text != "" && cmbBaudRate.Text != "" && cmbParity.Text != "" && cmbStopBits.Text != "")
{
_PuertoSerial.PortName = cmbPortList.Text;
_PuertoSerial.BaudRate = Convert.ToInt32(cmbBaudRate.Text);
_PuertoSerial.RtsEnable = true;
_PuertoSerial.DtrEnable = true;
_PuertoSerial.DataBits = Convert.ToInt32(cmbDataBits.Text);
if (cmbParity.Text == "Even") { _PuertoSerial.Parity = Parity.Even; }
else if (cmbParity.Text == "Odd") { _PuertoSerial.Parity = Parity.Odd; }
else if (cmbParity.Text == "Space") { _PuertoSerial.Parity = Parity.Space; }
else if (cmbParity.Text == "Mark") { _PuertoSerial.Parity = Parity.Mark; }
else { _PuertoSerial.Parity = Parity.None; }
if (cmbStopBits.Text =="2") { _PuertoSerial.StopBits = StopBits.Two; }
else if (cmbStopBits.Text == "1.5") { _PuertoSerial.StopBits = StopBits.OnePointFive; }
else { _PuertoSerial.StopBits = StopBits.One; }
if (cmbHandShake.Text == "Software Flow Control") { _PuertoSerial.Handshake = Handshake.XOnXOff; }
else if (cmbHandShake.Text == "Hardware Flow Control") { _PuertoSerial.Handshake = Handshake.RequestToSend; }
else { _PuertoSerial.Handshake = Handshake.None; }
_PuertoSerial.ReadTimeout = 500;
_PuertoSerial.WriteTimeout = 500;
_PuertoSerial.Open();
//in my understanding, this line of code is needed to handle data being received. Does it trigger a flag or something?
**_PuertoSerial.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);**
}
else
{
txtRecieve.Text = "Input selection missing 1 or more characteristics";
}
}
**
private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort testing = (SerialPort)sender;
txtRecieve.AppendText(testing.ReadExisting()); //txtRecieve cannot be reached within this function. It indicates the following error: "An object reference is required for the non-static field, method, or property 'frmSerialComm.txtRecieve'
}
**
void enableDisableGUI(bool[] input)
{
grpConnection.Enabled = input[0];
grpCharacteristics.Enabled = input[1];
btnOpenPort.Enabled = input[2];
btnClosePort.Enabled = input[3];
txtSend.Enabled = ((cmbControlMasterSlave.Text == "Slave") ? false : true);
}
//----------------------------C# objects / functions--------------------------------------
private void btnOpenPort_Click(object sender, EventArgs e)
{
try
{
_PuertoSerial = new SerialPort();
activatePort();
}
catch(Exception ex)
{
MessageBox.Show(ex.Message, "Message ", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
bool[] format = { false, false, false, true};
enableDisableGUI(format);
}
private void btnClosePort_Click(object sender, EventArgs e)
{
_PuertoSerial.Close();
bool[] format = { true, true, true, false};
enableDisableGUI(format);
}
private void txtSend_KeyPress(object sender, KeyPressEventArgs e)
{
_PuertoSerial.Write(e.KeyChar.ToString()); //this is how I send data through the serial port.
}
private void btnClearTxts_Click(object sender, EventArgs e)
{
txtRecieve.Clear();
txtSend.Clear();
}
} //class closes
} //program closes
I am not an experienced programmer, I just want to create something useful for my students. Any constructive criticism will be highly appreciated.
I don't have any definitive answers for you. You code looks like it should provide what you need once you get past the two possible glitches.
I think you should attach your SerialDataReceivedEventHandler BEFORE
you call _PuertoSerial.Open().
It may have no effect since event handlers can normally be enabled/disabled dynamically, but I base the advice on the following comment taken from the .Net source code for SerialPort on MSDN.
// all the magic happens in the call to the instance's .Open() method.
// Internally, the SerialStream constructor opens the file handle, sets the device control block and associated Win32 structures, and begins the event-watching cycle.
The "object reference" error might be resolved by removing the
static modifier from your DataReceivedHandler. If not, or if that
static modifier is necessary for some reason, then perhaps the
txtRecieve control has a private modifier which needs to be changed
to internal or public. You should be able to use Visual Studio in
debug mode to step into the InitializeComponent() method and see
where txtRecieve is being instantiated.
Well, I believe that I needed to read more. This is how I solved the problem (if this is not the real solution, at least is working for now):
I moved the "SerialDataReceivedEventHandler" line before the _PuertoSerial.open();
I followed the suggestions from this article:
https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k(EHInvalidOperation.WinForms.IllegalCrossThreadCall);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5.2);k(DevLang-csharp)&rd=true
So my funtions (one existings + a new one) look like this:
void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
printReceivedText(_PuertoSerial.ReadExisting());
}
private void printReceivedText(string text)
{
if (this.txtSend.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(printReceivedText);
this.Invoke(d, new object[] { text });
}
else
{
this.txtRecieve.AppendText(text);
_PuertoSerial.DiscardInBuffer();
}
}
For now seems to be working fine. The final testing will come when I connect another terminal and see the program interacting with each other.
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'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.
I am using a test card and this is the output after I swiped the card and it is ok
But when I'm trying to get the data of the swiped through prompting it to messagebox this will be the output
How can I fix this? I am expecting the output same as the first image, and it will also be the message of the messagebox
Here is my code:
private void CreditCardProcessor_Load(object sender, EventArgs e)
{
KeyPreview = true;
KeyPress += CreditCardProcessor_KeyPress;
}
private bool inputToLabel = true;
private void CreditCardProcessor_KeyPress(object sender, KeyPressEventArgs e)
{
if (inputToLabel)
{
label13.Text = label13.Text + e.KeyChar;
e.Handled = true;
}
else
{
e.Handled = false;
}
MessageBox.Show(label13.Text);
}
In short I want to run a function after swiping the card, and use its data to be use in my function. :)
You'll need to be more specific with your question. From the looks of things your card scanner is operating through the keyboard buffer. (Many card scanners operate this way) This means that every character of the strip is received as a character which is why you can capture this OnKeyPress.
If you're wondering why you're only seeing one character at a time it is exactly because you're raising a message box with each character received. If you want to know when you can call a function with the whole card info using that code what you'll need is something like:
private bool inputToLabel = true;
private StringBuilder cardData = new StringBuilder();
private void CreditCardProcessor_KeyPress(object sender, KeyPressEventArgs e)
{
if (!inputToLabel)
return;
if (e.KeyChar == '\r')
{
MessageBox.Show(cardData.ToString()); // Call your method here.
}
else
{
cardData.Append(e.KeyChar);
//label13.Text = label13.Text + e.KeyChar;
}
e.Handled = true;
}
Caveat: This is assuming that the card reader library is configured to terminate a card read with carriage return. (\r) You'll need to read up, or experiment with it for settings as to whether it can/does send a terminating character to know when the card read is complete. Failing that you can watch the output string for patterns. (I.e. when the captured string ends with "??") Though this is less optimal.
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/