Serial Communication between Arduino and Visual Studio (+QT)

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;
		}
		}

Error Code:
Program:
C:\Users\Julian\Desktop\QT\6.15.2\msvc2019_64\bin\Qt5Cord.dll
Module 5.15.2
File:
C:\Users\Julian\Desktop\QT\6.15.2\msvc2019_64\include\QtCore\qlist.h
Line: 579

ASSERT failure in QList::operator[]: "index out of range", file
C:\Users\Julian\Desktop\QT\6.15.2\msvc2019_64\include\QtCore\qlist.h
Line: 579

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

  1. wait for the starting character, e.g. <, initialse array (may be char[] or a String)
  2. loop reading a character adding it to array until newline found
  3. when newline found parse the message in the array

is that what you are doing?

1 Like

that sounds like a good idea. I'm not quite sure how to translate that into code though. I'll try, thanks in advance!

See Serial input basics - updated

1 Like

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?

what language are you using on the PC? C++ or C#

I'm using C++

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.

This is the initialization of the function:

//set up response
	serialCom->allowDirectResponseMessage();
	connect(serialCom, SIGNAL(signalDirectResponseMessage(QString)), this, SLOT(slotResponse(QString)));

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

ah true, I will. Thank you for your help so far!

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()

  1. characters are read in a loop
  2. the characters are added to the message (a String^)
  3. 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

Thanks a lot for your time and effort! Will check out your code and see if I can modify mine

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
Image1

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.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.