Go Down

Topic: VCL C++ Builder Serial Communication (Read 386 times) previous topic - next topic

jakebottari

Hello all,

I am building an application that allows for serial communication between a GUI interface on a PC and an arduino uno. The sending of data from the pc to the arduino seems to be working, but I cannot seem to be able to receive any data.

The basic methodology of my program is as follows:

  • After a certain action by the user, the GUI program will send a string to the arduino through the serial port (Right now the only "command" string set up is OUTPUT, which sets an output pins value)
  • The arduino will then process that string and implement it, and afterwards send a confirmation string that the command has succeeded


I can tell that the data is getting to the arduino because the TX light blinks whenever a command is sent. However the RX light rarely, if ever, turns on. I do not have LEDs or any other such indicator with me to actually see if the command is being implemented, but right now my only concern is getting data back into the GUI program on the PC.

My code implements the following class to allow for serial communication:

Header:
Code: [Select]
#ifndef SERIALPORT_H
#define SERIALPORT_H

#define ARDUINO_WAIT_TIME 2000
#define MAX_DATA_LENGTH 255

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

class SerialPort
{
private:
    HANDLE handler;
    bool connected;
    COMSTAT status;
    DWORD errors;
public:
    SerialPort(char *portName);
    ~SerialPort();

    int readSerialPort(char *buffer, unsigned int buf_size);
    bool writeSerialPort(char *buffer, unsigned int buf_size);
 bool isConnected();

};

#endif // SERIALPORT_H
[/quote]

SerialPort CPP:
[quote]#include "SerialPort.h"

SerialPort::SerialPort(char *portName)
{
    this->connected = false;

    this->handler = CreateFileA(static_cast<LPCSTR>(portName),
                                GENERIC_READ | GENERIC_WRITE,
                                0,
                                NULL,
                                OPEN_EXISTING,
                                FILE_ATTRIBUTE_NORMAL,
                                NULL);
    if (this->handler == INVALID_HANDLE_VALUE){
        if (GetLastError() == ERROR_FILE_NOT_FOUND){
            printf("ERROR: Handle was not attached. Reason: %s not available\n", portName);
        }
    else
        {
            printf("ERROR!!!");
        }
    }
    else {
        DCB dcbSerialParameters = {0};

        if (!GetCommState(this->handler, &dcbSerialParameters)) {
            printf("failed to get current serial parameters");
        }
        else {
            dcbSerialParameters.BaudRate = CBR_9600;
            dcbSerialParameters.ByteSize = 8;
            dcbSerialParameters.StopBits = ONESTOPBIT;
            dcbSerialParameters.Parity = NOPARITY;
            dcbSerialParameters.fDtrControl = DTR_CONTROL_ENABLE;

            if (!SetCommState(handler, &dcbSerialParameters))
            {
                printf("ALERT: could not set Serial port parameters\n");
            }
            else {
                this->connected = true;
                PurgeComm(this->handler, PURGE_RXCLEAR | PURGE_TXCLEAR);
                Sleep(ARDUINO_WAIT_TIME);
            }
        }
    }
}

SerialPort::~SerialPort()
{
    if (this->connected){
        this->connected = false;
        CloseHandle(this->handler);
    }
}

int SerialPort::readSerialPort(char *buffer, unsigned int buf_size)
{
    DWORD bytesRead;
    unsigned int toRead;

    ClearCommError(this->handler, &this->errors, &this->status);

    if (this->status.cbInQue > 0){
        if (this->status.cbInQue > buf_size){
            toRead = buf_size;
        }
        else toRead = this->status.cbInQue;
    }

    if (ReadFile(this->handler, buffer, toRead, &bytesRead, NULL)) return bytesRead;

    return 0;
}

bool SerialPort::writeSerialPort(char *buffer, unsigned int buf_size)
{
    DWORD bytesSend;

    if (!WriteFile(this->handler, (void*) buffer, buf_size, &bytesSend, 0)){
 ClearCommError(this->handler, &this->errors, &this->status);
        return false;
    }
    else return true;
}

bool SerialPort::isConnected()
{
 return this->connected;
}



My arduino Code:
Code: [Select]
String incomingByte;
char command[6];
int param1 = 0;
int param2 = 0;

void setup() {
    Serial.begin(9600);
}

void loop(){
  if(Serial.available() > 0)
    incomingByte = Serial.readStringUntil('\n');
    
    // Convert from String Object to string (char)
    char buf[15];
    incomingByte.toCharArray(buf, 15);
    
    //parse string command
    char* token = strtok(buf, " ");
    strcpy(command, token);
    
    token = strtok(NULL, " ");
    param1 = atoi(token);
    
    token = strtok(NULL, " ");
    param2 = atoi(token);
    

    if(*command == 'O' && *(command + 5) == 'T'){ //Look for command OUTPUT
      pinMode(param1, 1);
      digitalWrite(param1, param2);
      incomingByte = "";

      Serial.flush();
      Serial.println("SUCCESS");
    }


}



I will not upload the entirity of my GUI program for simplicity sake, but this is my implementation of reading the arduino, done through a timer on a 500ms interval. Again, this implementation might be my problem.

Code: [Select]

arduino->readSerialPort(output,MAX_DATA_LENGTH);
 if(output[0] = 'S'){

 std::string sTemp = std::string(output);
 UnicodeString st = UnicodeString(AnsiString(sTemp.c_str()));
 consoleEdit->Lines->Add(commandEdit->Text);

 Timer1->Enabled = false;


"output" is a char array with size of 255
the jumble in the middle is converting the char array to a wchar UnicodeString so VCL can be happy.

Any help is appreciated

Paul_KD7HB

What is missing in your description is how the two devices are connected. Are you using USB or are you using a real PC serial port? If a real serial port, do you have an RS-232 to TTL converter so the Arduino serial can see the data without being killed?

Paul

Robin2

I don't know anything about VCL but this simple Python - Arduino demo
may give you some ideas.

I suggest you get simple communication working between your PC and the Arduino before worrying about the GUI stuff. Perhaps write an equivalent of my Python code and use it with my Arduino code.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

PaulS

Why do you have butchered quote tags in the middle of your code?
The art of getting good answers lies in asking good questions.

jakebottari

The butchered quotes are from me not finding the code button till after i put my code in quotes lol. But yes im using the standard usb serial communication. I have simple communication working in a very dumbed down c++ console application, but the same methods does not work for VCL for some reason.

Paul_KD7HB

The butchered quotes are from me not finding the code button till after i put my code in quotes lol. But yes im using the standard usb serial communication. I have simple communication working in a very dumbed down c++ console application, but the same methods does not work for VCL for some reason.
Are you remembering to allow time for the Arduino reset after your PC application opens the serial port?

Paul

jakebottari

Yes I allow it to fully reset before trying to send/receive any data

Paul_KD7HB

#7
Apr 25, 2019, 06:12 pm Last Edit: Apr 25, 2019, 06:13 pm by Paul_KD7HB
Yes I allow it to fully reset before trying to send/receive any data
You never answered about having a RS-232 to TTL converter between the PC and the Arduino.

Paul

Oh, sorry, you are using the USB. Forgot.

Paul_KD7HB

By hiding all the communications in library functions, It's almost impossible to debug the communications.

But, I see you did not implement the "time out" link, so perhaps the Arduino code is still waiting to see the  "\n". If you implement the timeout and define one Arduino pin as output and set it to LOW. Then in the timeout code, set that pin to high, you will know if that is the problem or not. Check the pin with a DVM for being non-zero.

Paul


jakebottari

#9
Apr 27, 2019, 05:48 pm Last Edit: Apr 27, 2019, 05:49 pm by jakebottari
By hiding all the communications in library functions, It's almost impossible to debug the communications.

But, I see you did not implement the "time out" link, so perhaps the Arduino code is still waiting to see the  "\n". If you implement the timeout and define one Arduino pin as output and set it to LOW. Then in the timeout code, set that pin to high, you will know if that is the problem or not. Check the pin with a DVM for being non-zero.
I'm not sure what the timeout link is, however you may be misunderstanding my wording. The arduino can recieve data just fine, with the "Serial.readStringUntil('\n');" command. The problem lies in sending serial data back to the computer.

The following console application code works, and is using the same "SerialPort" library.
Code: [Select]
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "SerialPort.h"

using namespace std;

//String for getting the output from arduino
char output[MAX_DATA_LENGTH];

/*Portname*/
char *port_name = "\\\\.\\COM7";

//String for incoming data
char incomingData[MAX_DATA_LENGTH];

int main()
{
  SerialPort arduino(port_name);
  if (arduino.isConnected()) cout << "Connection Established" << endl;
  else cout << "ERROR, check port name";

  while (arduino.isConnected()){
    cout << "Write something: \n";
    std::string input_string;
    //Getting input

    getline(cin, input_string);
    char *c_string = new char[input_string.size() + 1];
    std::copy(input_string.begin(), input_string.end(), c_string);
    c_string[input_string.size()] = '\n';

    //Writing string to arduino
    arduino.writeSerialPort(c_string, MAX_DATA_LENGTH);
    //Getting reply from arduino
    arduino.readSerialPort(output, MAX_DATA_LENGTH);
    //printing the output
    puts(output);

    //freeing c_string memory
    delete[] c_string;
  }
}

PaulS

Code: [Select]
    char *c_string = new char[input_string.size() + 1];
    std::copy(input_string.begin(), input_string.end(), c_string);
    c_string[input_string.size()] = '\n';

    //Writing string to arduino
    arduino.writeSerialPort(c_string, MAX_DATA_LENGTH);
    //Getting reply from arduino

After stomping on the NULL, so that c_string is NOT a string, pass the char array to a function that expects a string. That that works at all is a miracle.

Code: [Select]
if(output[0] = 'S'){
Assigning a value to a variable, or array element, in an if statement is rarely the right thing to do.
The art of getting good answers lies in asking good questions.

Go Up