Hello everyone,
I'm experiencing a problem with my Communication between my Arduino and Visual Studio. I programmed my Arduino to send a specific sign with a value to show where to display said value.( I'm using a lab setup which is supposed to show different parameter values or read out the current temperature and display it to my GUI in Visual Studio) e.g. when I want to show the current temperature I send a "<" at the beginning and theres a SlotResponse function which detects the "<" and then knows to write the value into the "temp_ui" spinBox. This all works fine, but as soon as I use a Timer in Arduino the serial communciation gets buggy. At the beginning is sends the values correctly, but at a certain point the data gets split up while tranfering and my slotResponse function cant decipher the message and gets messed up and shows an error. I'm wondering if its possible to enter a delay somewhere or to tell the slotResponse message to wait for the whole message to be transferred. Maybe the Baudrate is set wrong, but I've tried many different ones and none seem to fit right. I used a switch case to call the function I need. When I call the function the Timer gets set and so the Update_Temperature() and Display_Variables_Plant()(which sends the Index, a ";" and the temperature value) functions get called every second (ISR raises interrupt_counter3 every second). It works for a certain amount of time but then it gets desynchronized, then it gets snychronized again until an error occurs which stops the program (It says that the index is out of range). Any help would be very much appreciated. I'll add some screenshots to give you guys a better idea of the program. (Visual Studio code snippet, Arduino Code Snippet and Errorwindow)
Arduino Code Snippet:
const char MEASURE[] PROGMEM = "&"; //is the tenth in the array)
const uint16_t t1_load = 0;
const uint16_t t1_comp = 62500;
String Semi;
String Total;
int ArrayIndex = 0;
double temperature_c = 0;
void loop() {
while (interrupt_counter3 == 1){
ClearInputBuffer();
Update_Temperature();
Display_Variables_Plant();
interrupt_counter3 = 0;
}
delay(10);
case 'U':
Update_Temperature(); // Get current temperature
roomtemp = temperature_c; // Save it as RoomTemp
// Setting variables for data aquisition
interrupt_counter3 = 0; // Counter variable
ArrayIndex = 0;
analogWrite(peltierPin, c * 2.55);
TCCR1A = 0;
TCCR1B &= ~(1 << WGM13);
TCCR1B |= (1 << WGM12);
TCCR1B |= (1 << CS12);
TCCR1B &= ~(1 << CS11);
TCCR1B &= ~(1 << CS10);
TCNT1 = t1_load;
OCR1A = t1_comp;
TIMSK1 = (1 << OCIE1A);
sei(); // Set global interrupt enabled, ISR is called every 1 ms
break;
ISR(TIMER1_COMPA_vect) {
interrupt_counter3++;
}
void Update_Temperature() // Saves measured temperature to temperature_c
{
temperature_c -= 2;
return;
}
void Display_Variables_Plant()
{
ClearInputBuffer();
Semi = ";";
Semi.concat(temperature_c);
Total = ArrayIndex + Semi;
ArrayIndex++;
PrintCurve();Serial.println(Total);
return;
}
}
void ClearInputBuffer() {
Serial.read();
}
void PrintCurve() // Equivalten to Serial.print("&");
{
strcpy_P(buffer, (char *)pgm_read_word(&(string_table_serial[10]))); Serial.print(buffer);
}
Visual Studio Code Snippet:
void arduinoVirtualPIDPlugin::slotResponse(QString msg)
{
SerialMessage.clear();
SerialMessage.append(msg);
QString HelpingStr = SerialMessage;
QString HelpingStr2 = SerialMessage;
int i = 0;
while (SerialMessage.size() > 0) // Cecking Serial Response for keychars "<", "#" and "~"
{
if (SerialMessage[i] == '&') //Save Data to Vector for plotting the graph
{
HelpingStr.remove(0, 1);
bool isNum;
tempString->append(HelpingStr.split(';')[1]); //Save TemperatureValue in array
TempString->append(HelpingStr.split(';')[1]);
HelpingStr2 = HelpingStr.split(';')[1];
HelpingStr2.toDouble(&isNum);
if (isNum) {
ui.textEdit_console->append(HelpingStr); //Print Timeindex and Temperature to console
//For later conversion needed --> updateQwtPlot
updateQwtPlot(PLOT_NAME);
k++;
HelpingStr.remove(0, HelpingStr.size());
HelpingStr2.remove(0, HelpingStr2.size());
}
else {
ui.textEdit_console->append("Peltier PWM Module: the data value is not a number: " + HelpingStr2 + "\n");
HelpingStr.remove(0, HelpingStr.size());
HelpingStr2.remove(0, HelpingStr2.size());
}
SerialMessage.remove(0, SerialMessage.size());
i = 0;
}
else
{
i++;
if (i > SerialMessage.size() - 1)
{
ui.textEdit_console->append(HelpingStr);
ui.textEdit_console->append("this command cannot be read");
HelpingStr.remove(0, HelpingStr.size());
SerialMessage.remove(0, SerialMessage.size());
i = 0;
}
}
I have used the technique when transmitting messages to a PC (usually C# or Java) of starting the message with a known character (similar to your <) followed by text terminated by '\n'.
it sounds like you are getting part of the message then attempting to parse it
When I am receiving a message I
wait for the starting character, e.g. <, initialse array (may be char[] or a String)
loop reading a character adding it to array until newline found
Thank you, that link is quite helpful. Unfortunately it describes the Arduino side, but I want to receive the data in Visual Studio. Do you know if there is such a link for the Visual Studio side?
The slotResponse function is receiving the message as a whole. I'm not sure how I can change that to receiving single characters and then adding it into an array.
for serial PC communications using Visual Studio (C++, C#, VB.NET, etc) I always used the SerialPort class
you appear to be using the SerialCom class which has different functionality - I will have a look at the documentation
As the problem is with Visual Studio code not Arduino perhaps try posting on the Msdn forum
this is a Visual Studio C++ console terminal emulator (no GUI)
// Visual Studio C++ console terminal emulator usings threads
#using <System.dll>
#include <string>
using namespace System;
using namespace System::IO::Ports;
using namespace System::Threading;
public ref class PortChat
{
private:
static bool _continue;
static SerialPort^ _serialPort;
public:
static void Main()
{
StringComparer^ stringComparer = StringComparer::OrdinalIgnoreCase;
Thread^ readThread = gcnew Thread(gcnew ThreadStart(PortChat::Read));
// Create a new SerialPort object with default settings.
_serialPort = gcnew SerialPort();
// Allow the user to set the appropriate properties.
_serialPort->PortName = SetPortName(_serialPort->PortName);
_serialPort->BaudRate = SetPortBaudRate(_serialPort->BaudRate);
// _serialPort->Parity = SetPortParity(_serialPort->Parity);
// _serialPort->DataBits = SetPortDataBits(_serialPort->DataBits);
// _serialPort->StopBits = SetPortStopBits(_serialPort->StopBits);
// _serialPort->Handshake = SetPortHandshake(_serialPort->Handshake);
// Set the read/write timeouts
_serialPort->ReadTimeout = 500;
_serialPort->WriteTimeout = 500;
_serialPort->Open();
_continue = true;
readThread->Start();
while (_continue)
{
ConsoleKeyInfo message= Console::ReadKey();//Line();
_serialPort->Write(message.KeyChar.ToString());
}
readThread->Join();
_serialPort->Close();
}
// read data from serial line
static void Read()
{
while (_continue)
{
try
{
String^ message;
Char ch;
// read character from serial line add to message - terminate on '\n'
do
{
ch = _serialPort->ReadChar();
message += ch;
Console::Write(ch);
}
while (ch != '\n');
// check if message starts with <
if (message[0] == '<')Console::WriteLine( "message starting with < received = " + message);
else Console::WriteLine("message received = " + message );
}
catch (TimeoutException^) {}
}
}
static String^ SetPortName(String^ defaultPortName)
{
String^ portName;
Console::WriteLine("Available Ports:");
for each (String^ s in SerialPort::GetPortNames())
{
Console::WriteLine(" {0}", s);
}
Console::Write("COM port({0}): ", defaultPortName);
portName = Console::ReadLine();
if (portName == "")
{
portName = defaultPortName;
}
return portName;
}
static Int32 SetPortBaudRate(Int32 defaultPortBaudRate)
{
String^ baudRate;
Console::Write("Baud Rate({0}): ", defaultPortBaudRate);
baudRate = Console::ReadLine();
if (baudRate == "")
{
baudRate = defaultPortBaudRate.ToString();
}
return Int32::Parse(baudRate);
}
static Parity SetPortParity(Parity defaultPortParity)
{
String^ parity;
Console::WriteLine("Available Parity options:");
for each (String^ s in Enum::GetNames(Parity::typeid))
{
Console::WriteLine(" {0}", s);
}
Console::Write("Parity({0}):", defaultPortParity.ToString());
parity = Console::ReadLine();
if (parity == "")
{
parity = defaultPortParity.ToString();
}
return (Parity)Enum::Parse(Parity::typeid, parity);
}
static Int32 SetPortDataBits(Int32 defaultPortDataBits)
{
String^ dataBits;
Console::Write("Data Bits({0}): ", defaultPortDataBits);
dataBits = Console::ReadLine();
if (dataBits == "")
{
dataBits = defaultPortDataBits.ToString();
}
return Int32::Parse(dataBits);
}
static StopBits SetPortStopBits(StopBits defaultPortStopBits)
{
String^ stopBits;
Console::WriteLine("Available Stop Bits options:");
for each (String^ s in Enum::GetNames(StopBits::typeid))
{
Console::WriteLine(" {0}", s);
}
Console::Write("Stop Bits({0}):", defaultPortStopBits.ToString());
stopBits = Console::ReadLine();
if (stopBits == "")
{
stopBits = defaultPortStopBits.ToString();
}
return (StopBits)Enum::Parse(StopBits::typeid, stopBits);
}
static Handshake SetPortHandshake(Handshake defaultPortHandshake)
{
String^ handshake;
Console::WriteLine("Available Handshake options:");
for each (String^ s in Enum::GetNames(Handshake::typeid))
{
Console::WriteLine(" {0}", s);
}
Console::Write("Handshake({0}):", defaultPortHandshake.ToString());
handshake = Console::ReadLine();
if (handshake == "")
{
handshake = defaultPortHandshake.ToString();
}
return (Handshake)Enum::Parse(Handshake::typeid, handshake);
}
};
int main()
{
PortChat::Main();
}
in the method static void Read()
characters are read in a loop
the characters are added to the message (a String^)
if the character is newline '\n'
the message is tested for starting with a < and messages displayed
it was tested with the Arduino program
void setup() {
Serial.begin(115200);
Serial.println("< test 1");
Serial.println("# test");
Serial.println("! test");
Serial.println("< test 2");
}
void loop() {
}
when run the Visual Studio program displayed on the Console
Available Ports:
COM1
COM5
COM12
COM4
COM port(COM1): COM12
Baud Rate(9600): 115200
< test 1
message starting with < received = < test 1
# test
message received = # test
! test
message received = ! test
< test 2
message starting with < received = < test 2
I will have a go at modifing one of the GUI terminal Enulators - may take some time as the Visual Studio libraries have changed since I implemented the code
the Visual Studio C++ serial read method of a GUI based terminal emulator
// data received from serial port
String^ message; // to hold a line text
private: System::Void serialPort1_DataReceived(System::Object^ sender, System::IO::Ports::SerialDataReceivedEventArgs^ e) {
Char ch;
// read character from serial line add to message - terminate on '\n'
while ((ch = serialPort1->ReadByte()) > 0) {
String^ m;
message += ch; // add character to message
m += ch;
textBox1->AppendText(m); // add character to TextBox
if (ch == '\n') {
// newline found check if message starts with <
if (message[0] == '<')textBox1->AppendText("message starting with < received = " + message);
else textBox1->AppendText("message received = " + message);
message = ""; // clear message
}
}
}
similar in operation to the Console version except output is to a TextBox.
when run it displays
hope it give some ideas !
the majority of my PC programming is Java but if I was to use Visual Studio I would program in C#. The latest Visual Studio versions do not even provide a template for a C++ Windows Form app.