Pages: 1 [2]   Go Down
Author Topic: VB Comms Failure During Long Jobs  (Read 3403 times)
0 Members and 1 Guest are viewing this topic.
United Kingdom
Offline Offline
Newbie
*
Karma: 0
Posts: 43
Some things are difficult, nothing's impossible
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Okay Paul, you've got my attention!

I had no idea that String class was problematic, or I would never have used it. It's quite hard to feel your way around a bug when it takes several hours to manifest.

Here's the Arduino code in FULL: I'll post the servo handler loop in the next post, as the full sketch exceeds the forum's character limit

Code:
// MMM_matrix_Plotter3 (pairs with same-named VB)
// tjn 16/10/2012
//
// Status: Interleaving X, Y1, Y2 and Z(B)
// Note: 78 Steps/mm using B servo in Full-wave mode, slack = 36

#include <Wire.h>
#include <iox.h>

// Setup pins (0 and 1 reserved for serial I/O)
byte xctrl1 = 2;                // X-Axis geartrain control 1
byte xctrl2 = 3;                // X-Axis geartrain control 2
byte yctrl1 = 4;                // South-Y geartrain control 1
byte yctrl2 = 5;                // South-Y geartrain control 2
byte y2ctrl1 = 7;               // North-Y geartrain control 1
byte y2ctrl2 = 8;               // North-Y geartrain control 2
byte xypower = 12;              // XY stepper supply
byte lpower = 13;               // Laser logic supply
byte xsensors = 15;             // X-buffer switches
byte ysensors = 14;             // Y-buffer switches
byte scaleFactor = 1;
byte offset1, offset2, space, cut, gap;
byte xStepIdx, y1StepIdx, y2StepIdx, zStepIdx;
int xSteps, y1Steps, y2Steps, zSteps, totalSteps, subSteps, bitIndex, laserTime, tMult;
int xStore, yStore, zStore, pixCount, iterations, dotCount, max1, max2, iTimeIdx = 0;
String inStr = "";              // Hold incoming data
boolean stringComplete = false; // Data complete flag
boolean xStop = false;
boolean yStop = false;
boolean xDir = false;           // TRUE = Clockwise
boolean yDir = false;
boolean zDir = false;
boolean oxDir = false;
boolean oyDir = false;
boolean ozDir = false;
boolean laserOn = false;
long MsDelay;
unsigned char twoWire[] = {
  B01,B11,B10,B00};             // 2-Wire sequence for X/Y steppers
word fullWaveB[] = {            // Full-wave Slave stepper motor sequence
  0x6000,0x2010,0x18,0x4008};
word lampState = 0x0000;
double m, mx, my1, my2, mz, x, y1, y2, z;

void setup() {
  Serial.begin(9600);         // was 38400, but froze
  inStr.reserve(40);          // easily enough for longest command (ie.x7000y7000z1404o13s0c19g6i270o14)
  pinMode(xctrl1, OUTPUT);    // X stepper pin1
  pinMode(xctrl2, OUTPUT);    // X stepper pin2
  pinMode(yctrl1, OUTPUT);    // Y1 stepper pin1
  pinMode(yctrl2, OUTPUT);    // Y1 stepper pin2
  pinMode(y2ctrl1, OUTPUT);   // Y2 stepper pin1
  pinMode(y2ctrl2, OUTPUT);   // Y2 stepper pin2
  pinMode(lpower, OUTPUT);    // Laser power
  pinMode(xypower, OUTPUT);   // XY stepper power
  pinMode(xsensors, INPUT);   // X-buffer switches
  pinMode(ysensors, INPUT);   // Y-buffer switches
  digitalWrite(xypower, LOW); // Power-down XY motors
  digitalWrite(lpower,LOW);   // Power-down laser
  Wire.begin();               // Start 2-wire communications (Arduino as master device)
  IOX.device(0x74, 16);       // 0x74 is address for Servo A (Pitch)
  IOX.write(0x0080, CFGPORT); // P07=INPUT Set ports LOW to make them OUTPUTS
  IOX.write(0x0000, INVPORT); // Set slave device invert ports to all NON-INVERT
  IOX.write(0x000, OUTPORT);  // Power-down Lamp/Fan
  Serial.println("OK?");
  Serial.flush();
  delay(100);
}

void loop() {
  if (stringComplete) {
    if(inStr.indexOf("x") >=0) xSteps = inStr.substring(inStr.indexOf("x")+1,inStr.indexOf("y")).toInt();
    else xSteps = 0;  // X Transit
    if(inStr.indexOf("y") >=0) {
      if(inStr.indexOf("z") >=0) y1Steps = inStr.substring(inStr.indexOf("y")+1,inStr.indexOf("z")).toInt();
      else y1Steps = inStr.substring(inStr.indexOf("y")+1).toInt(); // Y Transit
    }
    else y1Steps = 0;
    if(inStr.indexOf("z") >=0) {
      if(inStr.indexOf("l") >=0) zSteps = inStr.substring(inStr.indexOf("z")+1,inStr.indexOf("l")).toInt();
      else zSteps = inStr.substring(inStr.indexOf("z")+1).toInt(); // Y Transit
    }
    else zSteps = 0;
    if(inStr.indexOf("l") >=0) laserTime = inStr.substring(inStr.indexOf("l")+1).toInt();
    else laserTime = 0;  // Laser On/Off
    if(inStr.indexOf("o") >=0){
      laserTime = inStr.substring(inStr.indexOf("l")+1,inStr.indexOf("o")).toInt();      // Laser On Time
      offset1 = inStr.substring(inStr.indexOf("o")+1,inStr.indexOf("s")).toInt();        // Offset1
      space = inStr.substring(inStr.indexOf("s")+1,inStr.indexOf("c")).toInt();          // Space
      cut = inStr.substring(inStr.indexOf("c")+1,inStr.indexOf("g")).toInt();            // Cut
      gap = inStr.substring(inStr.indexOf("g")+1,inStr.indexOf("i")).toInt();            // Gap
      iterations = inStr.substring(inStr.indexOf("i")+1,inStr.lastIndexOf("o")).toInt(); // Iterations
      offset2 = inStr.substring(inStr.lastIndexOf("o")+1).toInt();                       // Offset2
    }
    else {
      offset1 = 0;
      space = 0;
      cut = 0;
      gap = 0;
      iterations = 0;
      offset2 = 0;
    }

    if (xSteps < 0) xDir = true;
    if (xSteps > 0) xDir = false;
    if (xSteps == 0) xDir = oxDir;
    if (y1Steps < 0) yDir = true;
    if (y1Steps > 0) yDir = false;
    if (y1Steps == 0) yDir = oyDir;
    if (zSteps < 0) zDir = false;
    if (zSteps > 0) zDir = true;
    if (zSteps == 0) zDir = ozDir;
    if (xStop = true && xDir != oxDir) xStop = false;
    if (yStop = true && yDir != oyDir) yStop = false;
    xSteps = abs(xSteps);
    y1Steps = abs(y1Steps);
    zSteps = abs(zSteps);
    y2Steps = y1Steps;

    if (zDir != ozDir) { // Vertical Slack Handler
      zSteps += 37;      // Z-Slack value (from laser deflection test)
    }

    totalSteps = max(xSteps, y1Steps);
    subSteps = min(xSteps, y1Steps);
    m = (double)subSteps/(double)totalSteps;

    if (m > 0.7) { // vector splitter/dog-legger to avoid bad harmonics between X&Y
      digitalWrite(lpower,LOW);
      if (xSteps > y1Steps) {
        xStore = xSteps;
        yStore = y1Steps;
        xSteps = xStore - y1Steps;
        y1Steps = 0;
        y2Steps = 0;
        digitalWrite(xypower, HIGH);
        xyzServos();
        xSteps = yStore;
        y1Steps = yStore;
        y2Steps = y1Steps;
      }
      if (xSteps < y1Steps) {
        xStore = xSteps;
        yStore = y1Steps;
        y1Steps = yStore - xSteps;
        xSteps = 0;
        digitalWrite(xypower, HIGH);
        xyzServos();
        xSteps = xStore;
        y1Steps = xStore;
        y2Steps = y1Steps;
      }
      // else not used (no adjustment needed when X & Y are equal!  
    }

    if (xSteps != 0 || y1Steps != 0 || zSteps != 0) {
      digitalWrite(xypower, HIGH);
      xyzServos(); // rem-out while testing
    }
    else {
      if (laserTime == 1) {
        digitalWrite(lpower, HIGH); // Laser ON
        IOX.write(0x0200, OUTPORT); // Lamp & Fan ON
      }
      else{
        digitalWrite(lpower,LOW);   // Laser OFF
        IOX.write(0x000, OUTPORT);  // Lamp & Fan OFF
      }
    }

    if (xSteps  == 0 && y1Steps == 0) { // switch OFF motors on end vector
      //digitalWrite(xypower, LOW); // Machine loses registration on power-down!
      if (laserTime == 0) IOX.write(0x0000, OUTPORT); // Turn lamp & fan OFF
    }

    if (xStop == true) Serial.println("X-buffer Hit");
    if (yStop == true) Serial.println("Y-buffer Hit");
    Serial.println("OK"); // Tell VB Arduino's ready to receive next command
    Serial.flush();
    inStr = "";           // Clear input string
    stringComplete = false;
    oxDir = xDir;
    oyDir = yDir;
    ozDir = zDir;
  }
  //else{
    //Serial.println("OK!"); // Tell VB Arduino's ready to receive next command from vb    
    //Serial.flush();
    //delay(1000);
  //}
}

void serialEvent() {
  while (Serial.available()) { // get new byte
    char inChar = (char)Serial.read();
    inStr += inChar;           // add to the inStr
    if (inChar == '\n') {      // Flag if char is vbcrlf
      stringComplete = true;
    }
  }
}

Any ideas/suggestions would be greatly appreciated.
Logged

United Kingdom
Offline Offline
Newbie
*
Karma: 0
Posts: 43
Some things are difficult, nothing's impossible
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Here's the servo handler, probably has no bearing on the problem, but you never know...

Code:
void xyzServos() {
  if (zSteps > 0 || (xSteps + y1Steps) < 500) tMult = 2000; // Change acceleration profile to suit Z(B)
  else tMult = 1000;            // stepper motor which stalls below 3ms
  max1 = max(xSteps, y1Steps);
  max2 = max(y1Steps, zSteps);
  totalSteps = max(max1, max2);
  mx = (double)xSteps/(double)totalSteps;
  my1 = (double)y1Steps/(double)totalSteps;
  my2 = (double)y2Steps/(double)totalSteps;
  mz = (double)zSteps/(double)totalSteps;
  x = mx;
  y1 = my1;
  y2 = my2;
  z = mz;
  laserOn = false;
  if (cut > 0 || laserTime > 0) {
    IOX.write(0x0200, OUTPORT); // Turn lamp & fan ON
    lampState = 0x0200;
  }
  if (cut == 0 && laserTime > 0) digitalWrite(lpower, HIGH);
  for (int i = 0; i < totalSteps; i++) {
    x += mx;
    if (x >= 1 && mx != 0 && xStop == false){ // X-stepper control
      x -= 1.0;
      if (xDir == true) {
        if (xStepIdx == 0) xStepIdx = 4;
        xStepIdx--;
      }
      else {
        xStepIdx++;
        if (xStepIdx > 3) xStepIdx = 0;
      }
      if(twoWire[xStepIdx] & 1<<1){
        digitalWrite(xctrl1,HIGH);
      }
      else {
        digitalWrite(xctrl1,LOW);
      }
      if(twoWire[xStepIdx] & 1<<0){
        digitalWrite(xctrl2,HIGH);
      }
      else {
        digitalWrite(xctrl2,LOW);
      }
    }
    y1 += my1;
    if (y1 >= 1 && my1 != 0 && yStop == false){ // Y1(South)-stepper control
      y1 -= 1.0;
      if (yDir == true) {
        if (y1StepIdx == 0) y1StepIdx = 4;
        y1StepIdx--;
      }
      else {
        y1StepIdx++;
        if (y1StepIdx > 3) y1StepIdx = 0;
      }
      if(twoWire[y1StepIdx] & 1<<1){
        digitalWrite(yctrl1,HIGH);
      }
      else {
        digitalWrite(yctrl1,LOW);
      }
      if(twoWire[y1StepIdx] & 1<<0){
        digitalWrite(yctrl2,HIGH);
      }
      else {
        digitalWrite(yctrl2,LOW);
      }
    }   
    y2 += my2;
    if (y2 >= 1 && my2 != 0 && yStop == false){ // Y2(North)-stepper control
      y2 -= 1.0;
      if (yDir == true) {
        if (y2StepIdx == 0) y2StepIdx = 4;
        y2StepIdx--;
      }
      else {
        y2StepIdx++;
        if (y2StepIdx > 3) y2StepIdx = 0;
      }
      if(twoWire[y2StepIdx] & 1<<1){
        digitalWrite(y2ctrl1,HIGH);
      }
      else {
        digitalWrite(y2ctrl1,LOW);
      }
      if(twoWire[y2StepIdx] & 1<<0){
        digitalWrite(y2ctrl2,HIGH);
      }
      else {
        digitalWrite(y2ctrl2,LOW);
      }
    }   
    z += mz;
    if (z >= 1 && mz != 0){ // Z(B)-stepper control
      z -= 1.0;
      if (zDir == true) {
        if (zStepIdx == 0) zStepIdx = 4;
        zStepIdx--;
      }
      else {
        zStepIdx++;
        if (zStepIdx > 3) zStepIdx = 0;
      }
      IOX.write(fullWaveB[zStepIdx] + lampState, OUTPORT);
    }
    if (cut > 0) { // Put lasing code here..
      bitIndex = (i - (offset1 + space)) % (space + cut + space + gap - 1); // - 1 makes interval agree with vb HorizSteps value!
      if (bitIndex == 0 && i < (totalSteps - offset2)) laserOn = true;
      if (bitIndex == cut) laserOn = false;
      if (laserOn == true) {
        digitalWrite(lpower, HIGH); //rem-out while testing
        delay(laserTime);
      }
      else {
        digitalWrite(lpower, LOW);
        delay(3); // fastest stable transit to next cutting point
      }
    }
    else {
      if (i < totalSteps / 2)
        iTimeIdx = i;
      else
        iTimeIdx = totalSteps - i;
      if (iTimeIdx > 180) iTimeIdx = 180;
      MsDelay = (tMult*(3-(sin((270+iTimeIdx)*PI/180)))) - 1000;
      if (laserTime == 0) delayMicroseconds(MsDelay);
      else delay(laserTime);
    }
    if (digitalRead(xsensors) == HIGH && i > 25) xStop = true;
    if (digitalRead(ysensors) == HIGH && i > 25) yStop = true;
    if (xStop == true || yStop == true) digitalWrite(lpower, LOW); // cut laser power if any limit reached
    if (xStop == true && yStop == true) i = totalSteps;            // bomb-out of loop if both limits reached
  }
  digitalWrite(lpower, LOW);
  //IOX.write(0x0000, OUTPORT); // Turn lamp & fan OFF
}
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 611
Posts: 49101
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
String inStr = "";              // Hold incoming data
  inStr.reserve(40);          // easily enough for longest command (ie.x7000y7000z1404o13s0c19g6i270o14)
If you KNOW that maximum size, then a static char array is perfectly suited.

Code:
char inStr[40];
byte index = 0;

Code:
  Serial.begin(9600);         // was 38400, but froze
  Serial.println("OK?");
  Serial.flush();
  delay(100);
If you are using 1.0 or later, the flush() function blocks until the 5 bytes have been sent. Then, you sit on your thumbs for 10 times as long as it took to shift those 5 bytes out. Was blocking really useful?

If you are using 0023 or earlier, the flush function dumps random amounts of unread data. Is that useful?

99% of the time, Serial.flush() is used incorrectly. You code appears to not be a 1%-er.

Code:
void serialEvent() {
  while (Serial.available()) { // get new byte
    char inChar = (char)Serial.read();
    inStr += inChar;           // add to the inStr
    if (inChar == '\n') {      // Flag if char is vbcrlf
      stringComplete = true;
    }
  }
}
should be
Code:
void serialEvent()
{
  while (Serial.available())
  {
    char inChar = Serial.read();
    inStr[index++] = inChar;           // add to the inStr
    inStr[index] = '\0'; // NULL terminate the array
    if (inChar == '\n')
    {      // Flag if char is vbcrlf
      stringComplete = true;
    }
  }
}

That leaves only determining, in loop, what is in inStr. The strcspn() function will find a character, like 'x', in a string. It returns the position of the character in the string. The strchr() function is similar, except that it returns a pointer to the character. You can increment that pointer to point to the next character.

Pass that pointer to strncpy() to extract a substring into another char array. Null terminate that array, and pass it to atoi() to get an int.

Of course, if you have any control over the sender, sending something like "x=7000,y=7000,z=1404,o=13,s=0,c=19,g=6,i=270,o=14" instead of "x7000y7000z1404o13s0c19g6i270o14" would allow you to use strtok() in a while loop, with a series of if/else if statements to extract the numeric values and associate them with the correct variables.
Logged

United Kingdom
Offline Offline
Newbie
*
Karma: 0
Posts: 43
Some things are difficult, nothing's impossible
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Paul,

I will amend the code as you suggested and run another long test, probably reply in a day or so. In the meantime, thank you so much for taking the time to point out my mistakes!
Logged

United Kingdom
Offline Offline
Newbie
*
Karma: 0
Posts: 43
Some things are difficult, nothing's impossible
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Well,

That failed. Twice.

I did the second run at 9600 baud, but no improvement. I will give a fuller report later, bit pushed for time today.
Logged

United Kingdom
Offline Offline
Newbie
*
Karma: 0
Posts: 43
Some things are difficult, nothing's impossible
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Well, after a few more days of tests I think it's time for me to move this issue onto a VB forum.

My TX/RX camera footage doesn't really prove anything. If VB closes the port and the Arduino sends serial data, TX WILL NOT FLASH. VB continues to randomly close the port, say the device connected is not functioning, or just tell me outright that the port doesn't even exist (even though the device manager may show everything's working fine).

Ciao for now...
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 611
Posts: 49101
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
If VB closes the port and the Arduino sends serial data, TX WILL NOT FLASH. VB continues to randomly close the port, say the device connected is not functioning, or just tell me outright that the port doesn't even exist (even though the device manager may show everything's working fine).
I know that it might take a while, but what happens if you have the Serial Monitor application monitor the serial port, instead of the VB app? Does the PC continue to receive data (or for longer than the VB app does)?
Logged

United Kingdom
Offline Offline
Newbie
*
Karma: 0
Posts: 43
Some things are difficult, nothing's impossible
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

As I mentioned earlier, I had most success from Device Monitoring Studio (most of the other monitor programs would just say "port is busy" and not even attempt to connect. The DMS output mirrored exactly what the VB IDE was reporting, byte for byte.

The more I search for info on VB's serial port, the more I find a trail of irate developers who swear that the VB serial implementation is rotten to the core (at least pre-Net Framework 4.0). I have stripped the SerialDataReceivedEventHandler out as I'm sure it's overkill, I already know when a command is coming from the Arduino, I don't need a thread to watch it.

I'm currently running a skeletal VB app that just prints command strings to the Arduino. Even this froze in a debug build! Once the release build has failed later today I will be compiling in 2010 with Net Framework 4.0.
Logged

United Kingdom
Offline Offline
Newbie
*
Karma: 0
Posts: 43
Some things are difficult, nothing's impossible
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Build under .Net Framework 4.0 also failed after nearly 9 hours.

Now testing with nothing (other than the laptop) connected to the Arduino!
Logged

United Kingdom
Offline Offline
Newbie
*
Karma: 0
Posts: 43
Some things are difficult, nothing's impossible
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Motor control code running on my bare Uno (nothing but USB connected) has been running continuously since my last post. Serial seems stable, so I need to reframe the problem and start a new thread. Looks like it might've been suffering some kind of brown-out with all the motors hooked up, but I'm not sure why as all my supplies are isolated. Could I be getting ripples on the 5v line, and why would it take hours to manifest?

[RESOLVED - insofar as the comms failure is now known to not be a software issue]

EDIT...

It WAS a serial/USB issue in the laptop/OS itself. I tried another laptop (also XP SP3) and the problem VANISHED!
« Last Edit: December 12, 2012, 05:03:23 am by aibonewt » Logged

United Kingdom
Offline Offline
Newbie
*
Karma: 0
Posts: 43
Some things are difficult, nothing's impossible
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

As a very late addendum to this thread, I'd like to add that I have just tried a fairly new notebook (Acer Aspire One running Win 7) borrowed from a friend as a potential purchase, and it had exactly the same problem.

Even with updates, power-saving and USB SelectiveSleepSuspend disabled it still randomly locked the COM port!

What the hell is wrong with modern laptops!!!?

Thomas
Logged

Pages: 1 [2]   Go Up
Jump to: