Reset on open com port by dll for reading

Hi everybody. This is my first post here, so I think a few words about what brought me here would be adequate, but you can also skip this and go right to the actual problem description.

Years ago I made a driver PCB for a 3D router that connects to the old 25 pin printer port. It offered 8 GPIO pins, nine if you used the status bit. I controlled 3 4-phase stepper motors: the lower 4 bits opened one of the 4 phase-outputs of all motors (in parallel), the next 3 bits were used to open the connection to ground of one of the motors. So I could sequentially / selectively control 3 motors by 7 GPIOs. It worked perfectly. Just as info: the data pins went through a diode, then through a 3K resistor, it was designed not to draw more than 2.5mA per pin.

But as you know, parallel ports became rare. and I wanted to use my driver board with a current windows laptop. So I bought an arduino with the intention to use it as a flexible port substitute. All I needed was to forward 7 bits to 7 pins, with say at least about 1kHz speed.

I then noticed that there aren't so many simple solutions out there, to communicate over the serial port with the arduino BY CODE. I mean, I write an app and my app should be able to directly communicate with the arduino. I tried a lot of stuff, spent many days, read many articles,including from here, installed a lot of things. (That I evaded CPP all my life didn't help either)

Finally I found a CPP code snippet for COM port comm, but as I'm a bloody CPP beginner (for life), I thought those randomly sprinkled characters were part of the code, while they were what appears to be a forum-script bug that adds some random chars to a posted code (imagine that). However, I manage to get it compiled with MinGW and working (one shot send receive test), and then even mastered to turn it into a dll that I can use with my beloved Blitz3D compiler (Which I use since decades, explaining why I never went full CPP)

Anyway, long story short, the actual problem is:

To test my dll, I use a loop that sends packets that cause the arduino to do a motor job, then receives a status packet by the arduino. I was debugging this already for days when I realized my code was working, but it is the Arduino that is going into setup() again. I nailed it down to the moment windows is starting to receive the answer. I noticed many articles about undesired autoreboot and read them.

I tried to connect with DTR off, but the Arduino then ignores the call. I can also rule out all other reasons, like power drop etc.
I've seen this:

that basically says this is not a bug, but a feature, so I was beginning to think my efforts were all in vain.

Sure the arduino will remain a cool thing on my bench, I successfully made other things, like driving LED displays etc. But as a substitute for a parallel port I may have to look for other ways.

Still, I'd be really happy if I could make this work. Somebody said something about suppressing the autoboot in DTR context. but that trace ended in some 404s. So if anybody has a suggestion about how to solve this particular problem, I'd be very glad. Again: all I want is to forward 7 bits from windows to my PCB. (Without to use my monitor for optical coupling at 60Hz lol). And yes, it needs to be bidirectional, so I can make a checksum based data integrity protocol that re-orders corrupted packets.

Arduino Uno R3 compatible Velleman, Win10 home. As stated, a max of 2.5mA draw per GPIO (pin 2 to 8). But a power issue was ruled out definitely.

Thank you!

In addition, as some may ask for it, a bit of code, the section where it happens. Please not, this is not my code, I just made it working. I was shocked there is GOTO in CPP, tbh.

So the reset happens when the dll starts to receive potential ata from the Arduino:

#include <Windows.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>


#define BBDECL extern "C" __declspec(dllexport)
#define BBCALL _stdcall


//--------------------------------------------------------- orig com code


BBDECL int BBCALL ReceivePaket( int portnr,int mybaudrate,int limitter ){

    int index = 0;
 char bigspace[10]={0};
 char* sbuf_adr = bigspace;

char xx=0;
char ttt[10]={67,79,77,53,58,0};

char portnr_s=53; 

    HANDLE hComm;  // Handle to the Serial port
    BOOL   Status; // Status
    DCB dcbSerialParams = { 0 };  // Initializing DCB structure
    COMMTIMEOUTS timeouts = { 0 };  //Initializing timeouts structure
    char SerialBuffer[6400] = { "Hello World." }; //Buffer to send and receive data
    DWORD BytesWritten = 0;          // No of bytes written to the port
    DWORD dwEventMask;     // Event mask to trigger
    char  ReadData;        //temperory Character
    DWORD NoBytesRead;     // Bytes read by ReadFile()
    unsigned char loop = 0;
//    char pszPortName[10]={"COM3:"};// orig
    char pszPortName[10]={"COM1:"};
    pszPortName[3]=48+portnr;
//    char PortNo[20] = { "COM3" }; //contain friendly name, orig
    char PortNo[20] = { "COM3:" }; //
    PortNo[3]=48+portnr;  // ascii 48... 0,1,2,3... 51=3

//    printf(PortNo, 20, "\\\\.\\%s", pszPortName);
    //Open the serial com port
    hComm = CreateFile(PortNo, //friendly name
                       GENERIC_READ | GENERIC_WRITE,      // Read/Write Access
                       0,                                 // No Sharing, ports cant be shared
                       NULL,                              // No Security
                       OPEN_EXISTING,                     // Open existing port only
                       0,                                 // Non Overlapped I/O
                       NULL);                             // Null for Comm Devices
    if (hComm == INVALID_HANDLE_VALUE)
    {
//        printf("\n Port can't be opened\n\n");
        goto Exit2;
    }

// this purge section erases 1 to 8 trash bytes in the buffer, that otherwise leads correct reception
// I tried without it, didn't fix the problem 
 PurgeComm(hComm,0x0002); // x2=purge rx abort, x8=rx flush, x1=purge tx abort, x4=flush tx, winbase.h (include Windows.h), kernel32.dll
 Sleep(1);
 PurgeComm(hComm,0x0008); // x2=purge rx abort, x8=rx flush, x1=purge tx abort, x4=flush tx, winbase.h (include Windows.h), kernel32.dll
 Sleep(1);

    //Setting the Parameters for the SerialPort
    dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
    Status = GetCommState(hComm, &dcbSerialParams); //retreives  the current settings
    if (Status == FALSE)
    {
//        printf("\nError to Get the Com state\n\n");
        goto Exit1;
    }

    dcbSerialParams.BaudRate = mybaudrate; //CBR_9600;      //BaudRate = 9600
    dcbSerialParams.ByteSize = 8;             //ByteSize = 8
    dcbSerialParams.StopBits = ONESTOPBIT;    //StopBits = 1
    dcbSerialParams.Parity = NOPARITY;      //Parity = None
    Status = SetCommState(hComm, &dcbSerialParams);
    if (Status == FALSE)
    {
//        printf("\nError to Setting DCB Structure\n\n");
        goto Exit1;
    }

    //Setting Timeouts
    timeouts.ReadIntervalTimeout = 500;
    timeouts.ReadTotalTimeoutConstant = 500;
    timeouts.ReadTotalTimeoutMultiplier = 10;
    timeouts.WriteTotalTimeoutConstant = 500;
    timeouts.WriteTotalTimeoutMultiplier = 10;
    if (SetCommTimeouts(hComm, &timeouts) == FALSE)
    {
//        printf("\nError to Setting Time outs");
        goto Exit1;
    }

// reading from -------------------------------------------------------------------------

    //Setting Receive Mask
    Status = SetCommMask(hComm, EV_RXCHAR);
    if (Status == FALSE)
    {
//        printf("\nError to in Setting CommMask\n\n");
        goto Exit1;
    }
    //Setting WaitComm() Event
    Status = WaitCommEvent(hComm, &dwEventMask, NULL); //Wait for the character to be received
    if (Status == FALSE)
    {
//        printf("\nError! in Setting WaitCommEvent()\n\n");
        goto Exit1;
    }
    //Read data and store in a buffer
    do
    {
        Status = ReadFile(hComm, &ReadData, sizeof(ReadData), &NoBytesRead, NULL);
//        SerialBuffer[loop] = ReadData;
        xx = ReadData & 255;
SerialBuffer[loop]=xx;
//printf("%c", xx);
        ++loop;
if( (loop>50) || (xx==255) ) {exit;}
    }

    while (NoBytesRead > 0); // end of do-loop

    --loop; //Get Actual length of received data
//     printf("\nNumber of bytes received = %d\n\n", loop);
    //print receive data on console
//    printf("lol \n\n");
 //   int index = 0;
//    for (index = 0; index < loop; ++index)
//    {
//        printf("%c", SerialBuffer[index]);
//    }
//    printf("\n\n");

// could open com port, but read success is certain. dll-caller gets serialbuffer pointer in return to read arduino answer paket in dlls array.
Exit1:
    CloseHandle(hComm);//Closing the Serial Port
sbuf_adr = SerialBuffer;
return (uintptr_t) sbuf_adr; // return pointer so the dll-caller can rtlMemoryMove the data. This works, but sometimes the array seems overwritten, so I rather move it from the dll to the caller to a given address. Anyway, that is suboptimal, but not the issue here

// could not open com port at all
Exit2:

    return 0;
}


Sorry for the various debugging residues, like unused variables etc, and my beginner understanding of CPP. Note, I tried the above in many combinations, including to disable many of the above steps (like only reading, but not setting the com port settings - this works for writing, but not for reading btw,). The code is working, one shot, and at some point the Arduino resets.
On the Arduino side there is nothing special, it should work I guess. Let me put the arduino code into the next comment.

I think there are two recommended methods 1) add a capacitor or 2) modify the PCB

This image shows where the "RESET EN" track should be cut.

Thank you very much. Wow this looks like the solution! I'll sure circle back and let you know whether it worked.
One thought already: Can I still upload sketches with the capacitor in place? Well I guess I'll just
add a manual switch in series with the cap. And a draining resistor, maybe 10M, so the cap can discharge when the switch is off That's then really a feature.

Ok, I tried it, but it doesn't seem to be so easy. I strictly followed the guide given by your link, a 10uF cap, negative to GND pin next to 5V, and positive to RESET pin - that's how I understood the article. Sketches cannot be uploaded now. When I upload the sketch, then run mode to init COM3 for 8N19600 dtr+rts on (as required to get it to respond at all), ten run my test app, as usual it does the first job, then resets. If I hot-plug the cap in now, which is risky, the Arduino freezes, or the communication. I made a connector with a switch, and a resistor 10M across the cap, to allow it to discharge when switched off. So I switch it on during that boot-looping. Again, Arduino freezes.

Maybe I'm doing something wrong.

As announced, the Arduino code

/*
  originally by Tom Igoe and Scott Fitzgerald
  https://www.arduino.cc/en/Tutorial/BuiltInExamples/SerialCallResponse
*/
// I guess these sensors aren't used anymore, but let them here anyway.
int firstSensor = 0;    // first analog sensor
int secondSensor = 0;   // second analog sensor
int thirdSensor = 0;    // digital sensor

int inByte = 0;         // incoming serial byte
int count=0;
byte buff1[257];

long int cur_x=0xffff;
long int cur_y=0xffff;
long int cur_z=0xffff;

int paket_n=0;

byte stepper[5]={0b00000001,0b00000010,0b00000100,0b00001000};
byte motor[4]={16,32,64};

//int bufco=0

byte parport[9]={2,3,4,5,6,7,8};
void setup() {
/*
buff_test[0]=1;
buff_test[1]=1;
buff_test[2]=48;
buff_test[3]=1;
buff_test[4]=30;
buff_test[5]=128;
*/
  
    pinMode(LED_BUILTIN, OUTPUT);

// this very characteristic LED glow effect using pseudo pwm identifies visually the rebooting. It's 
// called nowhere else.
myglow();
//delay(5000);
  // start serial port at 9600 bps:
Serial.setTimeout(3000);
Serial.begin(9600,SERIAL_8N1);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  for(int i=0;i<7;i++)
   {
    pinMode(parport[i], OUTPUT);   // define 7 bits for 3 motors
    digitalWrite(parport[i], LOW);    // set all pins low
   }
   establishContact();  // send a byte to establish contact until receiver responds
}

void loop() {
  int i=0;
  int iii=0;

  /*
   paket data structure:
   all bytes are shifted by 32, to prevent any ascii control codes from interfering
   (32 is subtracted when the byte by is copied to buff1[])
   0:motor  (0,1,2)
   1:direction (1 or != 1)
   2: n steps 1-90
   3: n steps 2 (reserved for full rotations) 1-90
   4: ms step duration 1-90
   5: 128 (eop end of packet trigger)
   */
   
 if(Serial.available()>0)
  {

    
    int by=Serial.read();
    // case is a end of paket (like this motor job, sent by windows) byte (128)
    
    if(by==128){ // UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU
      int jj=0;
      int crc=0;
      //for(jj=0;jj<count-2;jj++){crc+=buff1[jj];} // to be implemented later
      // if crc <> buff1[6] // data corrupt

      // problem: despite buff1[2] being 48 (1 full rotation of these stepper motors)
      // execution aborts after about 1/4 rotation, so the loop is executed about 12 times.
      // Then weirdly and evidently, setup() is executed again, apparently a soft reboot (crash?)
      for(jj=0;jj<buff1[2];jj++)
      {
        //myblink();
        int pudel=0;
       if(buff1[1]==1){ cur_y++; } // direction
       else { cur_y--; }
       pudel=stepper[cur_y & 0b11] | motor[buff1[0]]; // 
       // set pins for cnc...
if((pudel & 1)==1){digitalWrite(parport[0], HIGH);}
if((pudel & 2)==2){digitalWrite(parport[1], HIGH);}
if((pudel & 4)==4){digitalWrite(parport[2], HIGH);}
if((pudel & 8)==8){digitalWrite(parport[3], HIGH);}
if((pudel & 16)==16){digitalWrite(parport[4], HIGH);}
if((pudel & 32)==32){digitalWrite(parport[5], HIGH);}
if((pudel & 64)==64){digitalWrite(parport[6], HIGH);}
      delay(buff1[4]);
      for(i=0;i<7;i++){digitalWrite(parport[i], LOW);}

       
      } // next jj
      count=0;
      paket_n++;
//            delay(200); // give windows time to prepare for receiving answer - actually turns out 
// windows must wait for the arduino to execute the motor job -  trying to read before that 
// moment will abort the job on the Arduino side / reboot. It also reboots when you wait, after one 
// job is done - whenever windows starts receiving, or asks for readavail..

      establishContact();  // code for: go into paket sending mode
    }
    else{
    buff1[count]=by-32;
    count++;
    if(count>=32) // this should never been reached, with the correct packet received
    {
      count=0;
      // process any re-syncing
    }
    }
  }
  
}

void myblink()
{
  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(100);                       // wait for a second
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
  delay(300);                       // wait for a second
}

void myglow()
{
  // a characteristic glow effect as a debugging signal (cause the leds blink all the time anyway)
  int i=0;
  int glow=100;
  for(i=0;i<100;i+=2)
  {
  digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1+(i/4));                       // wait for a second
  digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
  delay(27-(i/4));                       // wait for a second
  }
}



void establishContact() {
  char coco=65;
  int co=0;

  while ((Serial.available() <= 0) &&(co<1)) {
    if(Serial.availableForWrite()!=0)
    {
    Serial.write(buff1[coco-65]);   // that's empty now for some reason
    coco++;
    if(coco>=91)
     {
      Serial.write(255);
      delay(10);
      coco=65;
      co++;
      return;
     }

    delay(10);
    }
  }
}

Oh and btw., while we're at it, if anyone of you is trying to make a dll with the MinGW compiler (the original 32, not 64) then this batch may come in handy as well:

g++ -c -o my_comport_dll.o my_comport_dll.cpp
g++ -o my_comport_dll.dll -s -shared my_comport_dll.o -Wl,--subsystem,windows
pause

OK, so your dll is doing the wrong thing for unknown reason. There are plenty terminal programs that allow you to configure the DTR and RTS and they do work in a way that they do not reset the Arduino.

Try DTR off and RTS off.

I tried plenty of them. However, even they reset the Arduino when you start a session. Like the IDE port monitor. Start it and see Arduino rebooting. Additionally I was under the impression that the reset on open com is a real thing, esp. after reading several articles about it (see above). So in the best case this is some kind of well kept secret.

Of course I tried DTR and RTS off already a hundred times.

They don't; or at least not all of them. Attached a configuration file for coolterm. If you have coolterm installed, unzip it, edit it with a text editor to suite your needs and double click the stc file to start coolterm.

COM3_115200_8N1_DTRoff_RTSoff_logging.zip (1.3 KB)

The solution i've used many times it to cut the RESET jumper and wire in a switch between the two (now isolated) pads. When the switch is closed, it acts like designed and you can upload new programs, etc. I labelled that switch position "program."

When the switch is open, opening the com port will not reset the arduino. I labelled that switch position "run."

Thank you Guys. I may look into Coolterm, even I'm not sure whether you mean to use it as a terminal and then simply read it's log files.
blh64 - just so we can rule out misunderstandings, the "Reset jumper" you mention, what exactly are we talking about? There is a Reset pin, but there i also RESET2 on the 6 pin ICSP1 male connector that looks like a tripple of jumpers. (Additionally I have a similar "jumper-looking" connector in the corner next to the USB plug, not found on the original board). And to what are you connecting it exactly? Thanks in advance.
Such a switch solution would be really useful.

The RESET EN mentioned in post #3

Not really. It was just to counteract your statement "However, even they reset the Arduino when you start a session". RealTerm also has the option (by passing arguments on the command line if not mistaken).

I use VisualStudio with C# and it's easy to do so as well. You first configure a serial port object and next open the port.

This brings me to the following regarding your dll. All examples that I have seen for Windows / C++ / serial port start by opening the port (wityj CreateFile) and next configuring the parameters. And that would be the wrong sequence; is that what your dll is doing?

@sterretje

The order of api calls of the above-posted dll in a nutshell

    hComm = CreateFile(PortNo, //friendly name
                       GENERIC_READ | GENERIC_WRITE,      // Read/Write Access
                       0,                                 // No Sharing, ports cant be shared
                       NULL,                              // No Security
                       OPEN_EXISTING,                     // Open existing port only
                       0,                                 // Non Overlapped I/O
                       NULL);                             // Null for Comm Devices

    Status = GetCommState(hComm, &dcbSerialParams); //retreives  the current settings

    Status = SetCommState(hComm, &dcbSerialParams);

    if (SetCommTimeouts(hComm, &timeouts) == FALSE)
 
    Status = SetCommMask(hComm, EV_RXCHAR);

    Status = WaitCommEvent(hComm, &dwEventMask, NULL); //Wait for the character to be received

    Status = ReadFile(hComm, &ReadData, sizeof(ReadData), &NoBytesRead, NULL);
 
    CloseHandle(hComm);//Closing the Serial Port

So yes, CreateFile COM3 before any configuration. As I said, I found this code and had to fix a ton of errors to make it compile in the first place. Last time I fiddled with a NULL modem on a serial port was in the 90ies - I know nothing about this.
However, in regards to your suggestion to alter this order, how am I supposed to do any port-related things without the first obtain the hComm handle given by CreateFile?

Our power just went off while I was trying to figure that out. You can however create a DCB before you open the port. Which brings the question how to apply it? I don't have the answer to that (yet); one thing is maybe COMMCONFIG but then the question becomes how to apply that before opening the port.

You can search for winbase.h on the Microsoft site and see what you can use.

Note:
I have never programmed in C++ for Windows so I still have to fight that hurdle as well :wink: Only scripting and C#.

Thanks.

Alright. I did cut the track on the PCB. Now nothing works anymore. The Arduino is pretty much dead. Thanks guys. I'll try solder a bridge back in, even tho my eyes are inflamed from the previous weeks of debugging to the point of partial paralysis.

Tell you what. I'm tired of it. Have a good one.

In conclusion: please excuse my frustration, I'm sure you meant it well. These solutions may have worked for you, but none works here. Velleman WHADDA Uno R3 clone it is, slightly different.
I can now however bridge the actual RESET pin with the RESET pin of the IPCS port to undo the track cut, so it works again without SMD fiddling.

I will however take a break and try instead a raspberry pi with RISC OS that features bbc BASIC (among other compilers) to directly access the 48 GPIOs.

Programming should be about building complex applications by using few known functions, but today the standard seems rather to build simple applications with a ton of unknown and complex functions.I'd even say, CPP is made complicated intentionally. Like "Input" = "scanf" - I mean come on. You end up researching/debugging/staring at cheap monitors 95% of the time. The creative process of constructing an app in your head, then write it down and it works right away - that creative process gets lost. So I'm really looking forward to use something as simple as bbc BASIC.

Well, if you find this frustrating, you may not find Raspberry Pi much easier!

I cut my teeth with Acorn Atom and PC DOS, with hindsight those days seem simpler, but in the days before the internet I recall struggling to find necessary documentation. So progress could be slow. Anyway, nothing stands still and the need to learn new things remains. That is a curse or a blessing; while it might be nice to rest solely on my legacy knowledge, I think I would have got very bored without the need to be learning new things.

Anyway, I have wondered if something like Agon Light would suit people wanting "retro simplicity" in a modern form.

Not as cheap as an Arduino, unfortunately.