Serial communication with c++

I know. Not exacly an Arduino specific topic. Nonetheless I'll try...

I've been trying to establish a serial link between my Arduino and a c++/DirectX application I'm making. I can't get the c++ code to read the serial output from Arduino properly...

My Arduino is intermittently sending out bytes of 0's and 1's like this:

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

void loop() 
{
  Serial.print(0xff, BYTE);
  Serial.print(0x00, BYTE);
}

The processing sketch below prints out 0 255 0 255 0....as it should:

import processing.serial.*;

Serial port;
int val;

void setup() 
{
  port = new Serial(this, Serial.list()[1], 115200);
}

void draw() {
  val = port.read();
  println(val);
}

However I have been trying to do the same using C++ (Visual C++ 2008 Express) based on these instruction:

And after HOURS of struggle I've been able to OPEN the COM port, detect that data is being send and OCCASIONALLY get one or two bytes of correct data:255 and 0 (= 0xff and 0x00). But most of the time I get 15 and 248 (= 0x0f and 0xf8) or other crap data.

I've been trying varios ways...Here is the simple way:

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <commdlg.h>
#include <windef.h>

#define READ_TIMEOUT 500 

void main()
{
      TCHAR* currentuser = L"COM8";
      LPCWSTR *lstr = (LPCWSTR *)(&currentuser);
    HANDLE hComm;

      DWORD dwRead;
      OVERLAPPED osReader = {0};
      
      DWORD dwRes;

      hComm = CreateFile( *lstr,  
                                    GENERIC_READ | GENERIC_WRITE, 
                                    0, 
                                    0, 
                                    OPEN_EXISTING,
                                    FILE_FLAG_OVERLAPPED,
                                    0);

      if (hComm == INVALID_HANDLE_VALUE) printf("*Error opening port\n");
      else printf("*Port opened succesfully\n");

      osReader.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

      if (osReader.hEvent == NULL)
         printf("*Error creating overlapped event; abort\n");

      BYTE inByte = 1;

      for(int i=0; i<100; i++) {
            if(!ReadFile(hComm,&inByte,1,&dwRead,&osReader)) 
                  dwRes = WaitForSingleObject(osReader.hEvent, READ_TIMEOUT);

            printf("%02x\n", inByte);
      }

      CloseHandle(osReader.hEvent);

      // Wait for user
      int a;
      scanf("%d", &a);
}

...And here is the more complicated version:

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <commdlg.h>
#include <windef.h>

#define READ_TIMEOUT 500 
#define READ_BUF_SIZE 200

void HandleASuccessfulRead(char lpBuf[], DWORD dwRead);

void main()
{
      TCHAR* currentuser = L"COM8";      // L"\\\\.\\COM8";
      LPCWSTR *lstr = (LPCWSTR *)(&currentuser);
    HANDLE hComm;

      char lpBuf[READ_BUF_SIZE];
      for(int i=0; i<READ_BUF_SIZE; i++) lpBuf[i]=0x01;

      DWORD dwRead;
      BOOL fWaitingOnRead = FALSE;
      OVERLAPPED osReader = {0};
      
      DWORD dwRes;

      hComm = CreateFile( *lstr,  
                                    GENERIC_READ | GENERIC_WRITE, 
                                    0, 
                                    0, 
                                    OPEN_EXISTING,
                                    FILE_FLAG_OVERLAPPED,
                                    0);

      if (hComm == INVALID_HANDLE_VALUE) printf("*Error opening port\n");
      else printf("*Port opened succesfully\n");

      // Create the overlapped event. Must be closed before exiting
      // to avoid a handle leak.
      osReader.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

      if (osReader.hEvent == NULL)
         printf("*Error creating overlapped event; abort\n");
      
      if (!fWaitingOnRead) {
            printf("*Issue read operation\n");
            
            if (!ReadFile(hComm, lpBuf, READ_BUF_SIZE, &dwRead, &osReader)) {
                  if (GetLastError() != ERROR_IO_PENDING)     // read not delayed?
                        printf("*Error in communications\n");
                  else {
                        fWaitingOnRead = TRUE;
                        printf("*Waiting on read\n");
                  }
            }
            else {    
              printf("*Read completed immediately\n");
              HandleASuccessfulRead(lpBuf, dwRead);
            }
      }

      if (fWaitingOnRead) {
            dwRes = WaitForSingleObject(osReader.hEvent, READ_TIMEOUT);

         switch(dwRes)
         {
              // Read completed.
              case WAIT_OBJECT_0:
                    if (!GetOverlappedResult(hComm, &osReader, &dwRead, FALSE))
                         printf("*Error in communications\n");
                    else
                         // Read completed successfully.
                         HandleASuccessfulRead(lpBuf, dwRead);

                    //  Reset flag so that another opertion can be issued.
                    fWaitingOnRead = FALSE;
                    break;

              case WAIT_TIMEOUT:
                    // Operation isn't complete yet. fWaitingOnRead flag isn't
                    // changed since I'll loop back around, and I don't want
                    // to issue another read until the first one finishes.
                    // This is a good time to do some background work.
                    break;                       

              default:
                    // Error in the WaitForSingleObject; abort.
                    // This indicates a problem with the OVERLAPPED structure's
                    // event handle.
                    break;
         }
      }

      // Wait for user
      int a;
      scanf("%d", &a);
}


void HandleASuccessfulRead(char lpBuf[], DWORD dwRead) {
      printf("*Handle succesfull read\n");

      byte b;
      
      for(int i=0; i<READ_BUF_SIZE; i++) {
            b = lpBuf[i];
            printf("%02x ", b);
      }
}

I was hoping that someone here could give me a hint?!

A few general things...

  • Call GetLastError if ReadFile returns false (GetLastError should be called and the value reported whenever an API call fails)
  • Some serial drivers will not function correctly without calling GetCommState followed by SetCommState
  • Start with a program that does NOT use overlapped I/O. Once the program works, then add overlapped I/O.
  • You need to call GetOverlappedResult when using overlapped I/O to determine the final status (data arrived, error occurred)

Hi Coding Badly (based on your comment not a very appropriate name) :slight_smile:

I started off from scratch again with the serial communication. There are wrappers/API's that make it simpler but 1st I couldn't get any of them to compile. So I was doing it the hard way (see above).

BUT...after a few hours struggle installing something called Platform SDK I got the wrapper below to compile and ~work (after rewriting an example line by line).

Now I'm getting quite consistent data AND most of it looks like it should. But there are still a lot of work to do to get it all to run smoothly.

And then I have to implement it into the DirectX app. But I consider those minor details compared to what I've been through.

Anyway thanks for you advice...

On second thought..you sound like you know what you're talking about so another question:

I'm sending this repeatedly from Arduino:

Serial.print(0xFF, BYTE);
Serial.print(0x00, BYTE);

But I'm recieving this: FFFFFFFF00FFFFFFFF00...etc...

Here is the test code:

#define STRICT
#include <tchar.h>
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include "Serial.h"

int main() {
      CSerial serial;
      LONG lLastError = ERROR_SUCCESS;      
      int i=0;

      // Attempt to open the serial port (COM8)
      lLastError = serial.Open(_T("COM8"),0,0,false);

      if (lLastError != ERROR_SUCCESS) printf("*Unable to open COM port\n");
      else printf("*COM port open\n");

      // Setup the serial port (9600,8N1, which is the default setting)
      lLastError = serial.Setup(CSerial::EBaud9600,CSerial::EData8,CSerial::EParNone,CSerial::EStop1);

      if (lLastError != ERROR_SUCCESS) printf("*Unable to set COM port settings\n");
      printf("*COM port settings set\n");

      lLastError = serial.SetMask(CSerial::EEventBreak |
                                                CSerial::EEventCTS   |
                                                CSerial::EEventDSR   |
                                                CSerial::EEventError |
                                                CSerial::EEventRing  |
                                                CSerial::EEventRLSD  |
                                                CSerial::EEventRecv);

      if (lLastError != ERROR_SUCCESS) printf("*Unable to set COM port event mask\n");
      else printf("*COM port event mask set\n");

      // Use 'non-blocking' reads, because we don't know how many bytes
      // will be received. This is normally the most convenient mode
      // (and also the default mode for reading data).
      lLastError = serial.SetupReadTimeouts(CSerial::EReadTimeoutNonblocking);

      if (lLastError != ERROR_SUCCESS) printf("*Unable to set COM-port read timeout\n");
      else printf("*COM port read timeout set\n");

      bool fContinue = true;

      // Read serial port
      do {

            // Wait for an event
            lLastError = serial.WaitEvent();
            if (lLastError != ERROR_SUCCESS) printf("*Unable to wait for a COM-port event\n");

            // Save event
            const CSerial::EEvent eEvent = serial.GetEventType();

            // Handle break event
            if (eEvent & CSerial::EEventBreak) printf("*BREAK received\n");

            // Handle CTS event
            if (eEvent & CSerial::EEventCTS) printf("*Clear to send %s\n", serial.GetCTS()?"on":"off");

            // Handle DSR event
            if (eEvent & CSerial::EEventDSR) printf("*Data set ready %s\n", serial.GetDSR()?"on":"off");

            // Handle error event
            if (eEvent & CSerial::EEventError)
            {
                  printf("*ERROR: ");
                  switch (serial.GetError())
                  {
                        case CSerial::EErrorBreak:            printf("Break condition");                  break;
                        case CSerial::EErrorFrame:            printf("Framing error");                  break;
                        case CSerial::EErrorIOE:            printf("IO device error");                  break;
                        case CSerial::EErrorMode:            printf("Unsupported mode");                  break;
                        case CSerial::EErrorOverrun:      printf("Buffer overrun");                  break;
                        case CSerial::EErrorRxOver:            printf("Input buffer overflow");      break;
                        case CSerial::EErrorParity:            printf("Input parity error");            break;
                        case CSerial::EErrorTxFull:            printf("Output buffer full");            break;
                        default:                                    printf("Unknown");                              break;
                  }
                  printf("\n");
            }

            // Handle ring event
            if (eEvent & CSerial::EEventRing) printf("*RING\n");

            // Handle RLSD/CD event
            if (eEvent & CSerial::EEventRLSD) printf("*RLSD/CD %s\n", serial.GetRLSD()?"on":"off");

            // Handle data receive event
            if (eEvent & CSerial::EEventRecv)
            {
                  // Read data, until there is nothing left
                  DWORD dwBytesRead = 0;
                  //char szBuffer[101];
                  char szBuffer[1];

                  do
                  {
                        // Read data from the COM-port
                        lLastError = serial.Read(szBuffer,sizeof(szBuffer),&dwBytesRead);
                        //lLastError = serial.Read(szBuffer,sizeof(szBuffer)-1,&dwBytesRead);
                        if (lLastError != ERROR_SUCCESS) printf("*Unable to read from COM-port\n");

                        if (dwBytesRead > 0)
                        {
                              // Finalize the data, so it is a valid string
                              //szBuffer[dwBytesRead] = '\0';

                              // Display the data
                              //printf("%s", szBuffer);
                              printf("%02x", szBuffer[0]);

                              // Check if EOF (CTRL+'[') has been specified
                              //if (strchr(szBuffer,EOF_Char)) fContinue = false;

                        }
                  }
                while (dwBytesRead == sizeof(szBuffer));
                  //while (dwBytesRead == sizeof(szBuffer)-1);

            }

            i++;
      }
      while(i<10);
      //while (fContinue);

      // Close the port again
    serial.Close();

      // Wait
      scanf("%d", &i);

      return 0;
}

Any ideas?

Your Arduino   Serial.begin([glow]115200[/glow]);  
Your Processing  port = new Serial(this, Serial.list()[1], [glow]115200[/glow]);
You report success.

Your C++      lLastError = serial.Setup(CSerial::EBaud[glow]9600[/glow],CSerial::EData8,CSerial::EParNone,CSerial::EStop1);
You report problems.

Is this a simple baud rate mismatch problem, or did you change the baud rate in your Arduino code since your original post, to match the C++ code?

I'm afraid not...I changed the BAUD rate of the Arduino some time ago...I'm constnatly try different rates...

But thanks anyway :slight_smile:

Hi Aniss1001,

based on your comment not a very appropriate name

Unfortunately, lots of experience doesn't always mean good code. :wink:

I've never used the "Serial library for C++" so I cannot say for certain that it's (relatively) bugfree and that your program adheres to its rules. The code you posted appears fine to me and I believe it includes enough checks to ensure nothing obvious is wrong.

First...

  • Change your Arduino program to output displayable ASCII characters (like 'A' and 'z')
  • Use Hyperterminal, the Arduino monitor, or another terminal program and ensure that "AzAzAzAz..." arrives at the computer. Let it run for a minute or two.
  • At this point, there is definately NOT a problem on the Arduino side

Second...

  • I suggest continuing to use CSerial
  • What happens if you disconnect the Arduino while the PC program is receiving data? (You may have to increase the "10" in "while(i<10)")
  • Carefully step through the CSerial code (especially WaitEvent). Ensure it works as you'd expect. When you can, inspect the return value from GetLastError (bearing in mind that it can return non-zero after an API was successful).

Good luck!

Actually I got it to work some time ago. The only thing was that the 1st readings allways show messed up data (perhaps old data from the buffer?) which confused me a bit. After that it works perfectly both with chars and numerical byte values.

BUT...I can't get it to work with the DirectX app (actually I'm using a DX based API called Dark GDK) :frowning: The moment I include the serial class to my DX app it no longer compiles and I get this message:

Serial.obj : error LNK2019: unresolved external symbol __CrtDbgReport referenced in function "public: virtual __thiscall CSerial::~CSerial(void)" (??1CSerial@@UAE@XZ)
Serial.obj : error LNK2019: unresolved external symbol __CrtDbgReportW referenced in function "public: virtual long __thiscall CSerial::Open(char const *,unsigned long,unsigned long,bool)" (?Open@CSerial@@UAEJPBDKK_N@Z)
Debug\Oscilloscope.exe : fatal error LNK1120: 2 unresolved externals

Since I have no idea what to do now I'm back to square one :frowning: Silly me that I didn't check if the serial API would compile WITH my DX app before I spent all the time getting it to work in a Win32 console app. But I was so happy that I finally found a serial that COMPILED and seemingly WORKED that I didn't think ahead...

Actually I got it to work some time ago

In the future, please follow-up when you get things working. I spent several minutes looking over your code and the CSerial code. Time I could have spent doing something else.

The only thing was that the 1st readings allways show messed up data (perhaps old data from the buffer?) which confused me a bit

If the Arduino is started first, this is very likely the result of the computer opening the serial port with a different baud rate. Between the call to Open and the call to Setup, the serial driver will be buffering data but at a baud rate determined by default settings.

unresolved external symbol __CrtDbgReport

This is part of the Visual C run-time library. I believe you can remove these routines by not defining _DEBUG. Or, linking against the debug versions of the run-time libraries should solve the problem.

In the future, please follow-up when you get things working.

Sorry. I usually do and I'll try to do it even faster in the future. But in this case I was playing with the code and when I got it working I was late and had to run out the door. Hope you understand and bare over with me...

Your advice is greatly appreciated.

This is part of the Visual C run-time library. I believe you can remove these routines by not defining _DEBUG. Or, linking against the debug versions of the run-time libraries should solve the problem.

OK?! Been trying to figure out how to do this but I'm afraid I don't quite know what to do? I tried adding #undef _DEBUG but it didn't seem to help...

BUT...As I promised I'll follow up on any progess right away...so...

I JUST tried to add the CSerial class to the compiler's directories, instead of adding them to the project (which worked in the original app). And now I can actually write #include "serial.h" without getting errors.

However I now get another error when I instantiate the class ( CSerial serial; ). The error is:

Main.obj : error LNK2019: unresolved external symbol "public: virtual __thiscall CSerial::~CSerial(void)" (??1CSerial@@UAE@XZ) referenced in function "void __cdecl DarkGDK(void)" (?DarkGDK@@YAXXZ)
Main.obj : error LNK2019: unresolved external symbol "public: __thiscall CSerial::CSerial(void)" (??0CSerial@@QAE@XZ) referenced in function "void __cdecl DarkGDK(void)" (?DarkGDK@@YAXXZ)
Debug\Oscilloscope.exe : fatal error LNK1120: 2 unresolved externals

The CSerial constructor seems to be unknown..Strange since it accepted the #include "serial.h" ???

Which compiler / development tool are you using?

I tried adding #undef _DEBUG but it didn't seem to help

Did you place the "#undef _DEBUG" as the very first line in your source code?

Look for a project option like "Release Build" and change it to "Debug Build". Or, look in the project settings for the pre-compiler options. You should see a place where _DEBUG is defined. If you change the project settings, be sure to perform a "build all" or "complete build" or "rebuild".

However I now get another error when I instantiate the class ( CSerial serial; ).

You need to include the CPP file (Serial.cpp) in the project. The header file (Serial.h) only has the definitions; none of the code.

Hope you understand and bare over with me

I understand and will do my best to "bare over".

Your advice is greatly appreciated

You are welcome.

Which compiler / development tool are you using?

As mentioned I'm using Visual C++ 2008 Express Edition. And I can't help thinking that somehow all my troubles are due to the fact that MS wants me to buy the paid version... :-?

Did you place the "#undef _DEBUG" as the very first line in your source code?

Not sure but I did now and no change. But as I said it's no longer the CrtDbgReport issue that is the problem. That seems to have been solved when I added the "serial.h" to the VC++ directories (under Include files).

You need to include the CPP file (Serial.cpp) in the project. The header file (Serial.h) only has the definitions; none of the code.

I'm aware of that. The serial.cpp file has also been added to the VC++ directories (under Source files). I also tried adding it directly to the project again but still no luck.

Thanks again for the advice..

The only suggestion I can think of is to locate and delete "Serial.obj" then try to build.

Well, it may be a bit late, but In HandleASuccessfulRead, you aren't printing what you received, you are printing the entire buffer.

change it to:

void HandleASuccessfulRead(char lpBuf[], DWORD dwRead) {
      printf("*Handle succesfull read\n");

      byte b;

      for(int i=0; i<dwRead; i++) {
            b = lpBuf[i];
            printf("%02x ", b);
      }
}

I also agree with Coding Badly. Go with Reads first, then overlapped.

Also, in the Serial sample, you did this:

printf("%02x", szBuffer[0]);

I am not sure that the printf is being passed an int - but instead just a byte. When you specify %x, it looks for an int sized member in varargs, not just a byte. Try this and see if it works:

printf("%02x", (int)szBuffer[0]);

The only suggestion I can think of is to locate and delete "Serial.obj" then try to build.

I tried that earlier but with no luck.

BUT...I just got it working 5 minutes ago :slight_smile:

Based on your advice I started playing around with the project/debug/release-settings, and suddenly it worked.

I added the serial.cpp directly to the project while the serial.h is added to the VC++ directories.

In project->properties->configuration properties->debugging->configuration I chose configuration Active(release) and in the toolbar (next to the start debugging-button) I chose release. And now it works.

Off course I'll no longer be able to use the Visual C++ debugging, but I can live with that.

Thanks A LOT for all your advice. I don't know if I could have done it without you :wink:

To Spinlock:

Ah..I just noticed your comment now. And yeah it's a bit late, since I just got the other solution working finally, but thanks a lot anyway... :slight_smile:

I am not sure that the printf is being passed an int - but instead just a byte. When you specify %x, it looks for an int sized member in varargs, not just a byte.

Hmm..not sure either but usually works for me when I wanna print out bytes. But I'll keep this in mind and do experimentation later when I get time...

Just thought I'd follow up on the entire ordeal..it may be of use to others..

The Arduino test code I'm now using is sending out bytes of values: 0 1 2 3 4 .... 253 254 255 0 1 2 3 ... etc.

Here is the Arduino code:

byte val=0;

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

void loop() 
{
  Serial.print(val, BYTE);
  val++;
}

As I mentioned the first bytes received are sort of random. Probably because it takes some time for the Arduino to reset when I establish the connection (as Coding Badly mentioned). Or because they are left overs in the buffer somehow?!

Here is a typical readout:

044 045 046 047 048 049 050 051 052 255
000 001 002 003 004 005 006 007 008 009
010 011 012 013 014 015 016 017 018 019
020 021 022 023 024 025 026 027 028 029
030 031 032 033 034 035 036 037 038 039
040 041 042 043 044 045 046 047 048 049
050 051 052 053 054 055 056 057 058 059
060 061 062 063 064 065 066 067 068 069
070 071 072 073 074 075 076 077 078 079
080 081 082 083 084 085 086 087 088 089
090 091 092 093 094 095 096 097 098 099
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 181 182 183 184 185 186 187 188 189
190 191 192 193 194 195 196 197 198 199
200 201 202 203 204 205 206 207 208 209
210 211 212 213 214 215 216 217 218 219
220 221 222 223 224 225 226 227 228 229
230 231 232 233 234 235 236 237 238 239
240 241 242 243 244 245 246 247 248 249
250 251 252 253 254 255 000 001 002 003
004 005 006 007 008 009 010 011 012 013
014 015 016 017 018 019 020 021 022 023
024 025 026 027 028 029 030 031 032 033
034 035 036 037 038 039 040 041 042 043
044 045 046 047 048 049 050 051 052 053
054 055 056 057 058 059 060 061 062 063
064 065 066 067 068 069 070 071 072 073
074 075 076 077 078 079 080 081 082 083
084 085 086 087 088 089 090 091 092 093
094 095 096 097 098 099 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 181 182 183
184 185 186 187 188 189 190 191 192 193
194 195 196 197 198 199 200 201 202 203
204 205 206 207 208 209 210 211 212 213
214 215 216 217 218 219 220 221 222 223
224 225 226 227 228 229 230 231 232 233
234 235 236 237 238 239 240 241 242 243
244 245 246 247 248 249 250 251 252 253
254 255 000 001 002 003 004 005 006 007
008 009 010 011 012 013 014 015 016 017
018 019 020 021 022 023 024 025 026 027
028 029 030 031 032 033 034 035 036 037
038 039 040 041 042 043 044 045 046 047
048 049 050 051 052 053 054 055 056 057
058 059 060 061 062 063 064 065 066 067
068 069 070 071 072 073 074 075 076 077
078 079 080 081 082 083 084 085 086 087
088 089 090 091 092 093 094 095 096 097
098 099 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 181 182 183 184 185 186 187
188 189 190 191 192 193 194 195 196 197
198 199 200 201 202 203 204 205 206 207
208 209 210 211 212 213 214 215 216 217
218 219 220 221 222 223 224 225 226 227
228 229 230 231 232 233 234 235 236 237
238 239 240 241 242 243 244 245 246 247
248 249 250 251 252 253 254 255 000 001
002 003 004 005 006 007 008 009 010 011
012 013 014 015 016 017 018 019 020 021
022 023 024 025 026 027 028 029 030 031
032 033 034 035 036 037 038 039 040 041
042 043 044 045 046 047 048 049 050 051
052 053 054 055 056 057 058 059 060 061
062 063 064 065 066 067 068 069 070 071
072 073 074 075 076 077 078 079 080 081
082 083 084 085 086 087 088 089 090 091
092 093 094 095 096 097 098 099 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 181
182 183 184 185 186 187 188 189 190 191
192 193 194 195 196 197 198 199 200 201
202 203 204 205 206 207 208 209 210 211
212 213 214 215 216 217 218 219 220 221

I'm currently useing a read-buffer of 10 bytes, since that seems to be the most stable. If I use more it doesn't allways fill up the buffer. Off course I'll try to play with the baudrate, timeout etc. later on.

As you can see the first 10 values (usually BUT not allways the case) are strange and then it starts counting from 0 to 255 as it should. I tried filling up a data buffer which only started recording when the first 0 was encountered (this has been out commented in the following code). It worked fine but it's perhaps a better solution simply waiting a while before starting to read. Otherwise I have to reserve certain values as SYNC BYTES?!

Here is the test C++ code:

//#define STRICT
#include <tchar.h>
#include <windows.h>
#include <stdio.h>
//#include <string.h>
#include <time.h>

#include "Serial.h"

#define BUF_SIZE 10
//#define DATA_BUF 1000
#define REPEAT 100

int main() {
      CSerial serial;
      LONG lLastError = ERROR_SUCCESS;      
      DWORD dwBytesRead = 0;
      byte szBuffer[BUF_SIZE];

      unsigned int i=0;
      //byte bData[DATA_BUF];
      //unsigned int iData = 0;
      //for(i=0; i<DATA_BUF; i++) bData[i]=0;
      //i=0;
      //bool record=false;

      lLastError = serial.Open(_T("COM8"),0,0,false);
      if (lLastError != ERROR_SUCCESS) printf("*Unable to open COM port\n");

      lLastError = serial.Setup(CSerial::EBaud9600, CSerial::EData8, CSerial::EParNone, CSerial::EStop1);
      if (lLastError != ERROR_SUCCESS) printf("*Unable to set COM port settings\n");

      lLastError = serial.SetMask(CSerial::EEventBreak |
                                                CSerial::EEventCTS   |
                                                CSerial::EEventDSR   |
                                                CSerial::EEventError |
                                                CSerial::EEventRing  |
                                                CSerial::EEventRLSD  |
                                                CSerial::EEventRecv);
      if (lLastError != ERROR_SUCCESS) printf("*Unable to set COM port event mask\n");

      lLastError = serial.SetupReadTimeouts(CSerial::EReadTimeoutNonblocking);
      if (lLastError != ERROR_SUCCESS) printf("*Unable to set COM-port read timeout\n");

      do {
      
            lLastError = serial.WaitEvent();
            if (lLastError != ERROR_SUCCESS) printf("*Unable to wait for a COM-port event\n");

            const CSerial::EEvent eEvent = serial.GetEventType();
            if (eEvent & CSerial::EEventBreak) printf("*BREAK received\n");
            if (eEvent & CSerial::EEventCTS) printf("*Clear to send %s\n", serial.GetCTS()?"on":"off");
            if (eEvent & CSerial::EEventDSR) printf("*Data set ready %s\n", serial.GetDSR()?"on":"off");

            if (eEvent & CSerial::EEventError)
            {
                  switch (serial.GetError())
                  {
                        case CSerial::EErrorBreak:            printf("*Break condition\n");                  break;
                        case CSerial::EErrorFrame:            printf("*Framing error\n");                        break;
                        case CSerial::EErrorIOE:            printf("*IO device error\n");                  break;
                        case CSerial::EErrorMode:            printf("*Unsupported mode\n");                  break;
                        case CSerial::EErrorOverrun:      printf("*Buffer overrun\n");                  break;
                        case CSerial::EErrorRxOver:            printf("*Input buffer overflow\n");            break;
                        case CSerial::EErrorParity:            printf("*Input parity error\n");            break;
                        case CSerial::EErrorTxFull:            printf("*Output buffer full\n");            break;
                        default:                                    printf("*Unknown\n");                              break;
                  }
            }

            if (eEvent & CSerial::EEventRing) printf("*RING\n");
            if (eEvent & CSerial::EEventRLSD) printf("*RLSD/CD %s\n", serial.GetRLSD()?"on":"off");

            //do {
                  if (eEvent & CSerial::EEventRecv) {

                        lLastError = serial.Read(szBuffer,sizeof(szBuffer),&dwBytesRead);
                        if (lLastError != ERROR_SUCCESS) printf("*Unable to read from COM-port\n");

                        if (dwBytesRead > 0)
                        {
                              //printf("*Bytes read: %d\n", dwBytesRead);
                              for(int ii=0; ii<dwBytesRead; ii++) {
                                    printf("%03d ", szBuffer[ii]);      

                                    //if(szBuffer[ii]==0) record=true;
                                    //if(record) {
                                    //      bData[iData] = szBuffer[ii];
                                    //      iData++;
                                    //}
                              }
                              printf("\n");
                        }
                  }

                  i++;
            //}while(i<10);

      } while(i<REPEAT); //BUF_SIZE);

    serial.Close();

      //printf("\nresult:\n");
      //for(i=0; i<DATA_BUF; i++) printf("%03d ", bData[i]);

      scanf("%d", &i);

      return 0;
}

Now that the serial class compiles WITH my DX app I can start integrating the above code into it (=the FUN part). Here is a screenshot of my DX app (currently using random values):

The idea is to send values from Arduino analog's in and display them graphically in different ways. But much work lays ahead :slight_smile:

...A short follow up on the start up syncronization issue...

I could just use bytes and then make sure that all values sent are mapped to 0-254. The value 255 (11111111) is then reserved as a sync byte indicating that what follows is valid data.

If I wanna send int values (10 bit resolution) I'd have to use 2 sync bytes. For instance the value 1023 is: 00000011 11111111, thus it contains the reserved sync byte. But if I use two sync bytes (11111111 11111111) I should be good to go...

Any reason why this wouldn't work flawlessly?

Check out the 1st version...

www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1254899716