Write to Serial Port

Greetings!

I am still learning the overall structure of C++, plus I am at a point where in order to maintain my interest in it, I need some help over a hurdle at which I'm stuck.

I am working on what I feel should be a simple program to write a string, byte, etc. to a designated parallel port. Perhaps the most frustrating issue is that the entirety of the code is copied verbatim from notes I've gathered. Not pieced together, but blatantly copied.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include "pch.h"
#include<iostream>
#include<string>
#include<atlstr.h>


using namespace System;
bool WriteComPort(CString PortSpecifier, CString data);

int main(array<System::String ^> ^args)
{
    bool ErrorState = WriteComPort("////.//COM11", "A");
    std::cout << ErrorState << "\n";
    return 0;
}

bool WriteComPort(CString PortSpecifier, CString data) {
    
    DCB dcb;
    DWORD byteswritten;
    HANDLE hPort = CreateFile(
        PortSpecifier,
        GENERIC_WRITE,
        0,
        NULL,
        OPEN_EXISTING,
        0,
        NULL
    );

    if (!GetCommState(hPort, &dcb)) {
        std::cout << "!GetCommState" << "\n";
        return false;
    }
    

    dcb.BaudRate = CBR_9600;
    dcb.ByteSize = 8;
    dcb.Parity = NOPARITY;
    dcb.StopBits = ONESTOPBIT;

    if (!SetCommState(hPort, &dcb)) {
        std::cout << "!SetCommState" << "\n";
        return false;
    }

    bool retVal = WriteFile(hPort, data, 1, &byteswritten, NULL);
    CloseHandle(hPort);
    return retVal;
}


I am using this code to Write a CString to COM11 with an Arduino Mega2560 on the other end. Its code is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

void setup() {
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
Serial.begin(9600);
}

void loop() {

char serData;


  if (Serial.available()) {
  serData = Serial.read();
 
  if (serData == 'A') {
  digitalWrite(LED_BUILTIN, HIGH);
  delay(2000);
}

  else {
    digitalWrite(LED_BUILTIN, LOW);
  }

}

}


The arduino code doesn't seem to have a problem, as it works fine with the serial monitor.

Like I said, I'm new to C++, so my understanding of determining whether the program is working is writing some code, clicking "debug" in Visual Studio 2019, and checking the output. Since I don't really see anywhere that gives me a clear error message as to what the problem is, I inserted a couple of "cout" lines to hopefully tell me where the code was failing. It seems not to make it past GetCommState, as I get a "!GetCommState" from the console.

I get zero lights on the Arduino, including the Rx and Tx lights.

Please help.
Thanks.
int main(array<System::String ^> ^args) That isn't C++.

Maybe the serial driver wants a newline character? Try adding that to the end of your string.
It's MS's C++/cli.
Hello,

I tink you want to read about the "try-catch"-statement and then add some.

I I would do it it would look something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#ifndef AdruinoCommunication_H
#define AdruinoCommunication_H

#include<iostream>
#include<sstream>
#include<fstream>
#include<windows.h>
#include<string>
#include<thread>

class AdruinoCommunication
{
    public:

        AdruinoCommunication(std::string comport);
        virtual ~AdruinoCommunication();

    private:
        std::thread getData;
        std::string mComport;
        bool keepGoing, sendData, receiveData;
        void communicationThread();
};
#endif // AdruinoCommunication_H

AdruinoCommunication::AdruinoCommunication(std::string comport)
{
    mComport = comport;
    keepGoing = true;
    sendData = true;
    receiveData = true;

    if (mComport.find("COM")!=std::string::npos)
    {
        getData = std::thread(&AdruinoCommunication::communicationThread, this);              // start a thread for retrieval of pump data over RS232.
    }
}

AdruinoCommunication::~AdruinoCommunication()
{
    keepGoing = false;                                                            // finish the current polling cycle and then stop the communication
    getData.join();
}

void AdruinoCommunication::communicationThread()
{
    while ( keepGoing )
    {
        std::cout << "AdruinoCommunication::communicationThread Thread (re)started. Opening the com-port.\n";
        try
        {
            HANDLE 		    file;
            COMMTIMEOUTS 	timeouts;
            DWORD 		    read, written;
            DCB 		    port;

            {   // Convert std:string to std::wstring. This may/should not always be required, but I once had issues because I used CreateFile and the compiler did not
                // agree about the type of my arguments, so now I always just make it explicit.
                std::wstring wComport = L"\\\\.\\";     // required to avoid problems with "port>COM9"-bug. https://support.microsoft.com/en-us/topic/howto-specify-serial-ports-larger-than-com9-db9078a5-b7b6-bf00-240f-f749ebfd913e
                for(unsigned int i = 0; i < mComport.length(); ++i)
                {
                    wComport += wchar_t( mComport[i] );
                }

                file = CreateFileW( wComport.c_str(),   //port_name, e.g. COM1
                    GENERIC_READ | GENERIC_WRITE,       // open for both reading and writing
                    FILE_SHARE_READ | FILE_SHARE_WRITE, // used to be 0  // share the file
                    nullptr,                            // default security
                    OPEN_EXISTING,                      // open an existing file
                    0,                                  // the type of file
                    nullptr);                           // no attribute template

                if ( INVALID_HANDLE_VALUE == file)
                {
                    std::cout << "Invalid handle to com-port :" << mComport << "/n";
                    return;
                }

                // get the current DCB, and adjust a few bits to our liking.
                memset(&port, 0, sizeof(port));
                port.DCBlength = sizeof(port);
                if ( !GetCommState(file, &port))
                {
                    std::cout << "Could not get the state of com-port :" << mComport << "\n";
                }
                if (!BuildCommDCB("baud=9600 parity=n data=8 stop=1", &port))
                {
                    std::cout << "Could not set parameters to :" << mComport << "\n";
                }
                if (!SetCommState(file, &port))
                {
                    std::cout << "Could not set state to com-port :" << mComport << "\n";
                }
                // set short timeouts on the comm port.
                timeouts.ReadIntervalTimeout = 1;
                timeouts.ReadTotalTimeoutMultiplier = 1;
                timeouts.ReadTotalTimeoutConstant = 1;
                timeouts.WriteTotalTimeoutMultiplier = 1;
                timeouts.WriteTotalTimeoutConstant = 1;
                if (!SetCommTimeouts(file, &timeouts))
                {
                    std::cout << "Failed to set the timeouts to com-port :" << mComport << "\n";
                }
                if (!EscapeCommFunction(file, CLRDTR))
                {
                    std::cout << "Could not escape com-port :" << mComport << "\n";
                }
                std::this_thread::sleep_for (std::chrono::milliseconds(200));
                if (!EscapeCommFunction(file, SETDTR))
                {
                    std::cout << "Could not escape the function of com-port :" << mComport << "\n";
                }
            }
            
            std::string commandoList[] = {"A", "B", "C", "D"};
            // keep looping to send the commands and/or poll the data
            while ( keepGoing ) 	// A loop in a loop is not unintentional.
                                    // In case of an exception/error, the comport will try to reset due to the outerloop.
                                    // And if all goes well it will keep working due to the innerloop.
            {
                std::string commandoList[] = {"A", "B", "C", "D", "E"};
                for (unsigned int index=0; index < (sizeof(commandoList)/sizeof(commandoList[0])) ; index++)
                {
                    if (sendData)
                    {   // send the request to the pump
                        std::stringstream ss;
                        // you might not need the stringstream in this case. When I was usingthis I had to start and end each message with a hexadecimal value and fixed address.
                        ss << commandoList[index].c_str() ;     // specifies the desired parameter

                        WriteFile(file, ss.str().c_str(), strlen(ss.str().c_str()), &written, nullptr); // send the data
                    }
                } // repeat the inner loop
                std::this_thread::sleep_for (std::chrono::milliseconds(500));
            }
        }   /// If we get below this point, something went wrong. I try to record the problem and recover.
        catch (std::system_error const& e)
        {
            std::ofstream exceptionLogger;
            exceptionLogger.open("exceptions.txt", std::ios::app);
            exceptionLogger << "std::system_error in AdruinoCommunication::communicationThread. code:" << e.code().value() << " message=" << e.what() << "\n";
            exceptionLogger.flush();
            exceptionLogger.close();
        }
        catch (std::exception& ex)
        {
            std::ofstream exceptionLogger;
            exceptionLogger.open("exceptions.txt", std::ios::app);
            exceptionLogger << "std::exception in AdruinoCommunication::communicationThread. Message:" << ex.what() << "\n";
            exceptionLogger.flush();
            exceptionLogger.close();
        }
        catch (...)
        {
            std::ofstream exceptionLogger;
            exceptionLogger.open("exceptions.txt", std::ios::app);
            exceptionLogger << "Something unexpected was thrown in AdruinoCommunication::communicationThread./n"; 
            exceptionLogger.flush();
            exceptionLogger.close();
        }
    }
}

int main()
{
    std::string input;

    std::cout<<"\tStarting serial communication.\n";
    {
        std::cout<<"\tPlease specify the target port (e.g. COM1)\n";
        std::cin>>input;
        std::cin.clear();

        AdruinoCommunication arduinoCommunication(input);
        std::cout<<"\tEnter 'stop' to terminate\n"; // or anything else
        std::cin>>input;
        std::cin.clear();
    }   // because we leave the scope, arduinoCommunication is destroyed.
    std::cout<<"\tTerminating\n";
    return 0;
}
Last edited on
@Nico I don't see anything in your try block that would throw exceptions, aside from the stuff in std for which an exception would signal an irrecoverable error in this case.

Nothing throws system_error in particular.
Last edited on
Topic archived. No new replies allowed.