How to stop random servo movement during reset?

If this is in the wrong place, feel free to move it. Also if you need more details, just ask - I am trying to keep it to the essential parts, but might be missing something important...

I am making a project that uses a high power 1/4 scale digital servo, driven by an Arduino UNO. I am getting random movement of the servo when the Arduino is being reset, reloaded, or between the time I release the reset and I get into the Loop function of my sketch.

This is a bad thing, because it could potentially result in a moderate safety hazard under some conditions.

The basic circuit is a 110VAC in / 15VDC 3A out power brick, feeding a TI LM22679 based buck converter board with a 7.2VDC nominal output, that is connected directly to the power wires of the servo, and the +Vin and Gnd pins of the Uno (the onboard regulator of the UNO provides it's power.) The servo control pin is connected to pin D9 on the Uno.

There are a bunch of other things wired up to other pins, but none are active - just data input switches and status LED's - I can supply details, but I don't think they are part of the problem.

The servo specs say that it has a maximum full load power draw of 3A at 7.2VDC, and the buck board is supposed to be able to put out at least 4A, so there shouldn't be any power shortage.

I'm doing it this way in part because it's what I could find inhouse for parts, and partly because the servo requires the 7.2V supply in order to get it's rated speed and power output.

If I push the external reset button (ties the reset pin to ground), and especially if I hold it, the servo often moves to a random position, that does not seem to have anything to do with the code in my sketch. Sometimes it stops after an initial movement, sometimes it will move back and forth a few times. Usually after a few seconds it will settle into a single position and stay there.

If I then release the button, I will sometimes, but not always get some more random movements before the "good status" LED that my sketch turns on as the very first action of the loop function.

Similarly if I upload my sketch via the USB cable, I get more random movement. Judging by the LED's it seems to be happening between the last few lines of "AVRdude" and the start of the Setup code.

As soon as the loop section starts, the servo moves to the position specified by the sketch code as I expect it to.

How can I prevent this random movement? Ideally I'd like the servo to go to it's maximum CCW position (2300uSec pulse width) but it would be OK if it just didn't move.

Thanks,
ex-Gooserider

As soon as the loop section starts, the servo moves to the position specified by the sketch code as I expect it to.

How can I prevent this random movement? Ideally I'd like the servo to go to it's maximum CCW position (2300uSec pulse width) but it would be OK if it just didn't move.

Well it's not because of anything your are doing or not doing. Here is the problem, typical hobby servos are designed such that anytime they are powered up they need to be receiving a continuous and valid position control pulse. A servo that has DC power on but no position control pulse being received, is in a undefined state and no assumptions can or should be made as to what behavior the servo should exhibit while you either hold down reset switch, or run a program that doesn't run the servo library code which has been set-up. Once you send a valid servo.write command to the servo, the servo library code automatically keeps sending that same position output command at a standard servo update rate, around every 20-25 msec as I recall. That is why a servo will remain at the last command position sent, because the servo library software is continuously sending the same pulse width signal. Only if you execute another servo.write command of a different value will the servo move to that new position.

If it important to try and eliminate this behavior then you need to think about about possible solutions. One possible method might be to have the power wired to your servo be controlled on and off by your sketch via an output pin controlling a relay which would then route power on or off to the servo. That way your servo can be set-up in software first and then turn on the digital output to switch power to the servo. Keep in mind that a servo with no power will offer no resistance (other then fixed gear friction) to external torque forces acting on the servo control arm or wheel.

So does that help any?

Lefty

1 Like

This is a bad thing, because it could potentially result in a moderate safety hazard under some conditions.

This really needs to be addressed then.

What about removing the bootloader and flying solo with ISP programming? The problem technically will still exist but the pulses will begin within about 60mS from power up/reset and so may not be noticed.

You could also have some external hardware (a 555 timer) producing pulses that centre (or any other position) the servos until you get around to providing the real thing.


Rob

Graynomad:

This is a bad thing, because it could potentially result in a moderate safety hazard under some conditions.

This really needs to be addressed then.

What about removing the bootloader and flying solo with ISP programming? The problem technically will still exist but the pulses will begin within about 60mS from power up/reset and so may not be noticed.

That may be a solution, and easy to try to see it's effect. However keep in mind that in the case of a manual reset, the servo is still at risk as long as the person is holding down the reset switch, it's only after he/she releases the switch that the program starts to execute.

You could also have some external hardware (a 555 timer) producing pulses that centre (or any other position) the servos until you get around to providing the real thing.

Yes that is also a possibility, but would require additional possibly complex external logic to prevent interaction of the 555 pulses with the arduino pulses once the arduino pulses start to arrive? But this would also have the advantage that the servo would have full torque holding power at the default position as long as the servo is powered up.
Lefty


Rob

Correct about holding the reset button. OP, in the final product will this ever occur?

Re the 555, you could gate the master signal with an AND gate and further gate the individual servo signals with OR gates and tie all Arduino servo outputs low with resistors.

Run the 555 signal into an Arduino pin as well as to all the OR gates, the OR gate outputs drive the servos.

Sync on the master signal and when happy kill the master signal by driving the second AND gate input low (or just kill the 555) then start the Arduino servo outputs.

There may be a slight hiccup on the changeover but probably not enough to matter and it could possibly be allowed for.

This idea has the added benefit of returning all servos to a known position if the processor fails.

OP, if this really is a safety issue I think something needs to be done, there may be a better way but that's a couple of ideas.


Rob

Thanks for the replies so far...

Let me give a bit more detail on the application, and explain the safety issue... I'm a paraplegic due to spinal cord injury, and am still trying to do various maker type things. There are a lot of machines, for instance sewing machines and TIG welders, that are usually operated with a foot pedal, mostly because your hands are busy doing other stuff. Since my feet don't work any more, this is a problem. My project is to create an electric "pedal pusher" to work the pedal for me. The servo drives a metal plunger up and down in a frame to push on whatever pedal I put in the device. I'm controlling the servo with an Arduino and speech reco via an EasyVR shield.

The safety issue is that if the controlled device is turned on, and the servo pushes the pedal as part of it's random movement, the result would be accidental machine operation, with obvious risks of damage to operator, machine, workpiece, etc. depending on circumstances....

I don't see failure of the servo / arduino / other during use as a big problem, as that would be similar to the pedal failing on, and can be handled similarly if need be.

Other questions - yes, the reset button can be held down indefinitely, and given human nature, I'd expect it to be held down for at least a few seconds.

I'm not worried about the servo moving if not powered - there is almost no torque load on it when it isn't moving (unless it's pushing a pedal, in which case you have the springs in the pedal pushing back) and it is all but impossible to turn it manually.

I'm not using the servo library in my sketch, what I'm doing is the "servo without delay" approach to manually generate the pulses, as IMHO that gives a smoother action. However the same logic seems to apply - and it seems like the power relay may be the best (simplest) approach.

ex-Gooserider

power relay may be the best (simplest) approach.

Yes but it doesn't conform to MIRC* principles.


Rob

  • Make It Really Complicated

I'm not using the servo library in my sketch, what I'm doing is the "servo without delay" approach to manually generate the pulses, as IMHO that gives a smoother action.

Really? 8)

zoomkat:

I'm not using the servo library in my sketch, what I'm doing is the "servo without delay" approach to manually generate the pulses, as IMHO that gives a smoother action.

Really? 8)

Really :stuck_out_tongue:

I originally had this in the main code, but found I kept reusing the same code in several places so I made the following function -

void servoMove (int pulseWidth){
  
  // SERVO MOVE SECTION
  // pulse the servo every 20 ms (refreshTime) with current pulseWidth
  // this will hold the servo's position if unchanged, or move it if
  // changed

  if (millis() - lastPulse >= refreshTime) {
    digitalWrite(servoPin, HIGH); // start the pulse
    delayMicroseconds(pulseWidth); // pulse width
    digitalWrite(servoPin, LOW); // stop the pulse
    lastPulse = millis(); // save the time of the last pulse
  }
}

Millis is a lont unsigned int for time, as is lastPulse. RefreshTime is the length of the total signal, nominally 20ms, but I actually use a little less to ensure that I don't go to long - currently it's about 18ms. PulseWidth is the length of the servo control pulse, and is adjusted elsewhere in the code. As long as there isn't a total delay time of longer than 20ms per loop cycle, I get a nice smooth movement. In earlier projects where I tried using the servo library and a cheapy servo, I couldn't get a motion that wasn't extremely jerky. The above code wasn't perfect, but it did a lot better.


Getting back to the original topic, I talked with some other local folks, and they agreed with the random state of the servo when the code wasn't running as the problem, but instead of using a relay, they suggested using a FET or power transistor to do the same thing, on the argument that mechanical things like relays fail...

I ended up with a TIP112 NPN Darlington pair with a 1N4148 diode across the C-E pair in the ground lead of the servo power connection. The base is wired to one of the Arduino pins, with a 10K pull down resistor to keep it turned off unless the pin is actively high. Just got it wired up this evening, will need to do the code to control it tomorrow, and see if it works...

ex-Gooserider

I've added the code to work with the servo enable/disable circuit I described in the previous post (a TIP112 NPN Darlington pair with a 1N4148 diode across the C-E pair in the ground lead of the servo power connection. The base is wired to one of the Arduino pins, with a 10K pull down resistor to keep it turned off unless the pin is actively high.)

The code to make it work was pretty simple - assigned a pin as servoEnable, and set it high as the last step in setup and a couple of other places.

The results are mixed - I'm definitely not getting the random movements during reset, etc., but I'm getting some strange operation during the main program that wasn't present.

I've attached my code below, it's my entire sketch, except that I've trimmed out some of the cases that I've assigned to modes, but not actually written any code for. I haven't trimmed it further, as I am not sure where the problem might be, so I wanted to show it all and not risk trimming out something critical.

Most of the problems I've had in the "Knob" mode - which is mostly a development mode that lets me move the servo up and down with a pot.

I can reproduce the symptoms in the "Exercise" mode if I shorten the delays and / or increase the turn rate enough. "Exercise" mode is just that - it continuously drives the servo up and down, no input needed or accepted.

Serial mode allows keyboard control via the USB port - again I can reproduce the problem if I crank the turn rate way up, but it is more stable than the other two. I suspect that this is mostly because of the delays added by the serial port and the very small change increments in the code.

Using the pot, it would move the servo predictably / properly if I turned the knob very slowly. If I turn the knob quickly (the faster I turn it, the worse the problems) I get erratic movement of the servo, including up and down oscillations. In addition, the run and error LED's would flash - even though the ONLY places in the code that change their states are the run-stop and error functions, which shouldn't be getting called.

I've had several eyeballs look at the code, and nobody has seen any issues with it.

At the suggestion of one of the reviewers, I've experimented with commenting and uncommenting out the lines in the error and stop action functions that change the LED states, and found that playing with the lines that do a digital write of the "wrong" state doesn't affect the erratic servo operation, but DOES change the LED behaviour. Further testing showed that the problem appears to be in the stop action function. Altering the error function statements didn't change the LED behaviour, but the stop action function statements did.

If I comment out the lines that write a low to the Run LED or a High to the Error LED I can prevent the respective LED from changing state when it shouldn't. Thus it appears that the stop action function is getting called momentarily for some reason.... :~ but NOTHING in the code should be calling it.

I've also tried commenting out the call to the stop action function - this seems to not only prevent the flashing, but ALSO gets rid of the erratic servo operation! If I have the call commented out, it doesn't make any difference which way I have the stop action switch.

Can anyone suggest reasons as to WHY this is happening?

I'm wondering if it could be a wiring issue? FWIW, my wiring is as follows -

AC into a 15vdc, 3A out brick. That feeds a "buck board" that chops it down to 7.5V and up to 5A out.

The servo power + lead is tied to the + out of the buck board, the - power lead goes to the collector of the enable Xstor, the emitter goes to the - lead of the buck board. The servo signal lead goes to the servo control pin (D9) on the Arduino. My reasoning is to keep the relatively high 3A servo power current away from the Arduino, and to supply the voltage that the servo requires for it's fastest performance.

The Arduino is connected across the buck board as well (7.5V should be well within the capability of the onboard regulator)

The +V for the knob is taken from the 5V out pin on the Arduino. all the ground leads for the LED's and other switches are gathered at the run-stop switch terminal, and from there go to the Arduino ground pin. This "should" be totally OK, but it seems the stop action switch might not like it?

At any rate, sorry this has been so long... Here is my (also very long) sketch... - OOPS, getting an error that my message is to long, will put code in next message...

ex-Gooserider

Here's the code... This still hits the length limits, so I'm splitting it across a couple of messages - here is everything up to the end of setup.

// PEDAL OPERATOR MAIN
// Master file for foot pedal operator - consolidated development

// Connections:
// D0 - EZVR commo (per docs)
// D1 - EZVR commo (per docs)
// D2 - D5 - profile switch inputs (common to ground)
// D6 - debug switch - ground
// D7 - RUN LED - via resistor to ground
// D8 - error LED - via resistor to ground
// D9 - SERVO CONTROL (servo power 7.5v supply and Ground via 
        // enable circuit xstor)
// D10 - Stop Action switch - ground
// D11 - Up Limit switch - ground
// D12 - D13 - EZVR commo (per docs)
// A0 - Knob wiper, 1K pot, ends to 5V and ground
// A1 - Servo enable (used as digital output - to enable xstor base)
// A2 - A5 - unused

// ////////////////////////////////////////////////////////////////////////////////////////

// PROFILE VARIABLES
// "Profile switch" read BCD switch once during setup
// BCD Switch inputs attached to pins D2-5, common to Ground
// (use internal pullups) On = low = set

// Define data bit pins
const int profile_BIT1 = 2;
const int profile_BIT2 = 3;
const int profile_BIT4 = 4;
const int profile_BIT8 = 5;

// Define profile variable, set to 0
int profile = 0;
// END PROFILE VARIABLES

// DEBUG SWITCH VARIABLE
const int debugPin = 6;
int debug = 1;

// LED indicator variables
const int runledPin =  7;    // the number of the RUN LED pin
int runledState = LOW;       // Run LED state
const int errorledPin =  8;  // the number of the ERROR LED pin
int errorledState = LOW;     // Error LED state
int errorCode = 0;           // Error has happened if !0, what is wrong

// SERVO VARIABLES
/** Adjust these values for your servo and setup, if necessary **/
const int servoPin = 9; // control pin for servo motor
int minPulse = 700;     // minimum servo position
int maxPulse = 2300;    // maximum servo position
int turnRate = 3;       // servo turn rate increment (larger value, faster rate)
unsigned int refreshTime = 18; // time (ms) between pulses (50Hz)

/** The Arduino will calculate these values for you **/
int centerServo;   // center servo position
int pulseWidth;    // servo pulse width
int moveServo;     // raw user input
unsigned long lastPulse = 0; // recorded time (ms) of the last pulse
int exDir = LOW; // exercise direction, used only in exercise mode
// END SERVO VARIABLES


// Stop Action Variables
const int stopactPin = 10;
int stopact = 1;

// UP Limit veriables
const int upLimitPin = 11;
int upLimit = 1;


// KNOB VARIABLES
const int potpin = A0; // analog pin used to connect the potentiometer
int potval;            // variable to read the value from the analog pin
// END KNOB VARIABLES

// SERVO ENABLE VARIABLES
const int srvenblPin = A1; // analog pin used for enable signal
                           // (all digital pins used already)
int srvenblState = LOW;    // State of enable pin
// END SERVO ENABLE VARIABLES


/////////////// END VARIABLES 


/// START SETUP

void setup() {
  // DEBUG SWITCH SETUP - NOTE switch ON = 0
  pinMode(debugPin, INPUT_PULLUP);
  debug = digitalRead(debugPin);

  // initialize serial communication at 9600 bits per second if 
  // debug is on
  if (debug == 0) {
    Serial.begin(9600);
    delay(1000);
  }

  // UpLimit switch setup
  pinMode (upLimitPin, INPUT_PULLUP);
  upLimit = digitalRead (upLimitPin);

  // SERVO SETUP
  pinMode(servoPin, OUTPUT); // Set servo pin as an output pin
  centerServo = maxPulse - ((maxPulse - minPulse) / 2);
  pulseWidth = maxPulse; // Give the servo a starting point (or it floats) 
  // set pulseWidth = maxPulse - needed for safe state (full up)
  // END SERVO SETUP


  // LED pin setups        
  pinMode (runledPin, OUTPUT);   // RUN LED
  pinMode (errorledPin, OUTPUT); // ERROR LED

  // Stop Action Setups
  pinMode (stopactPin, INPUT_PULLUP);
  stopact = digitalRead (stopactPin);


  // PROFILE SWITCH SETUP
  // make the profile switch's pins inputs, with internal pullups on:
  pinMode(profile_BIT1, INPUT_PULLUP);
  pinMode(profile_BIT2, INPUT_PULLUP);
  pinMode(profile_BIT4, INPUT_PULLUP);
  pinMode(profile_BIT8, INPUT_PULLUP);

  // read the input pins: NOTE - CLOSED switch (binary 1) returns "0"
  int val_profile_BIT1 = digitalRead(profile_BIT1);
  int val_profile_BIT2 = digitalRead(profile_BIT2);
  int val_profile_BIT4 = digitalRead(profile_BIT4);
  int val_profile_BIT8 = digitalRead(profile_BIT8);

  // turn bit values into single profile value by adding decimal value
  // of each bit to profile variable
  if (val_profile_BIT1 == 0) {
    profile = profile + 1;
  }
  if (val_profile_BIT2 == 0) {
    profile = profile + 2;
  }
  if (val_profile_BIT4 == 0) {
    profile = profile + 4;
  }
  if (val_profile_BIT8 == 0) {
    profile = profile + 8;
  }

  /* // DEBUG - prints out the state of the switch bits and profile value
   if (debug == 0) {
   delay (1000);
   Serial.print("[profile_BIT1: ");
   Serial.print(val_profile_BIT1);
   Serial.print("] ");
   Serial.print("[profile_BIT2: ");
   Serial.print(val_profile_BIT2);
   Serial.print("] ");
   Serial.print("[profile_BIT4: ");
   Serial.print(val_profile_BIT4);
   Serial.print("] ");
   Serial.print("[profile_BIT8: ");
   Serial.print(val_profile_BIT8);
   Serial.print("] ");
   Serial.print("[profile: ");
   Serial.print(profile);
   Serial.println("] ");
   Serial.print("Pulse Width: ");
   Serial.print(pulseWidth);
   Serial.println(" us"); // microseconds
   Serial.print ("errorCode = ");
   Serial.println (errorCode);
   }
   */
  // END PROFILE SWITCH SETUP

  // PROFILE SETUP - put all case specific code in the appropriate space
  // below

  switch (profile) {

 // non-developed cases trimmed

  case 4:
    if (0 == debug) {
      delay (1000);
      Serial.println("KNOB setup");
    }
// Check the knob position - if not max up, throw error, and move
// to max up position - makes startup safer.
    potval = analogRead(potpin); // reads the value of the
    // potentiometer (value between 0 and 1023)
    potval = map(potval, 0, 1023, 700, 2300); // scale it to use
    // with the servo (value between 700 and 2300 uSec)

    if (potval <= 2200) {
      pulseWidth = 2300;

      /*     if (0 == debug) {
       delay (1000);
       Serial.println("KNOB wrong value");
       }*/
       
      // ENABLE SERVO
      pinMode(srvenblPin, OUTPUT);
      srvenblState = HIGH;
      digitalWrite(srvenblPin, srvenblState);

      /*      if (0 == debug) {
       // delay (1000);
       Serial.print("Servo enabled? ");
       Serial.println (srvenblState);
       }*/
      delay (200); // experimentally found minimum to get servo to move before
      // error code flash starts
      
      servoMove (pulseWidth);

      /*      if (0 == debug) {
        // delay (1000);
        Serial.println("servo moved?");
      }*/
      
      // Define error state, go directly to error code, 
      // Do NOT pass loop
      // Do NOT collect $200 ;-}
      errorCode = 4;
      errorFunction (errorCode);
    }

    // do something when var equals 4
    break;

  case 5:
    if (debug == 1) {
      Serial.begin(9600);
      delay (1000);
      // if debug NOT on, starts serial port. If serial port gets turned
      // on twice causes hang.
    }
    Serial.println("SERIAL CONTROL ");
    // SERIAL CONTROL PROFILE INSTRUCTIONS
    Serial.println("      Arduino Serial Servo Control");
    Serial.println("Press < for DOWN, > for UP, spacebar to center");
    Serial.println();
    // This mode doesn't cause servo motion at start, so no
       // 'safety code' needed
    // END SERIAL CONTROL PROFILE INSTRUCTIONS
    break;

  case 6:
    if (debug == 0) {
      //delay (1000);
      Serial.println("EXERCISE SETUP");
    }
    pulseWidth = (2300);
    // servoMove (pulseWidth); this can't move servo as isn't 
    // enabled yet?
    break;

  default:
    if (0 == debug) {
      // delay (1000);
      Serial.println("NOT DEFINED! ");
    }
    // if nothing else matches, do the default
    // default is optional
  } // end switch

  // ENABLE SERVO (normally)
  pinMode(srvenblPin, OUTPUT);
  srvenblState = HIGH;
  digitalWrite(srvenblPin, srvenblState);
  // allow for xstor rise and servo to come up?
  delay (200);
  // execute servo move to startup pulseWidth
  servoMove (pulseWidth);

  if (0 == debug) {
    Serial.println ("end of setup");
    //Serial.println(debug);
    // delay (10000);
  }


} // SETUP END ///////////////////////////////////////////////////////////////

The rest of the code:

void loop() {
  upLimit = digitalRead (upLimitPin);
  /*  Not implemented yet - still trying to figure out how...
  if ( 0 == upLimit) {
   maxPulse = pulseWidth;
   }
   */

  /*
 if (debug == 0) { 
   // delay (10);
   Serial.print("beginning of loop debug=");
   Serial.println(debug); 
   Serial.print("[profile: ");
   Serial.print(profile);
   Serial.println("] ");
   Serial.print("[maxPulse: ");
   Serial.print(maxPulse);
   Serial.println(" uS] ");
   Serial.print("Pulse Width: ");
   Serial.print(pulseWidth);
   Serial.println(" us"); // microseconds
   Serial.print ("UP Limit = ");
   Serial.println (upLimit); 
   Serial.print ("errorCode = ");
   Serial.println (errorCode);}
   */

  // Turn on RUN LED
  runledState = HIGH;
  digitalWrite(runledPin, runledState);
  
  // PROFILE LOOP SECTION - put all case specific code in the appropriate
  // space below
  switch (profile) {

// Non developed cases trimmed

  case 4:
    if (debug == 0) {
      //delay (10);
      //Serial.println("KNOB loop ");
    }
    // KNOB CONTROL SECTION

    potval = analogRead(potpin); // reads the value of the
    // potentiometer (value between 0 and 1023)
    potval = map(potval, 0, 1023, 700, maxPulse); // scale it to use it with
    // the servo (value between 700 and 2300 uSec or maxPulse, if less)
    pulseWidth = potval;
    

    // END KNOB CONTROL SECTION
    break;

  case 5:
    if (debug == 0) {
      Serial.println("SERIAL CONTROL loop ");
    }
    // SERIAL CONTROL SECTION
    // wait for serial input

    if (Serial.available() > 0)
      // read the incoming byte, if any:
      moveServo = Serial.read();

    // ASCII '<' is 44, ASCII '>' is 46 (comma and period, really)
    if (moveServo == 44) {
      pulseWidth = pulseWidth - turnRate;
      Serial.print("Pulse Width: ");
      Serial.println(pulseWidth);
    }
    if (moveServo == 46) {
      pulseWidth = pulseWidth + turnRate;
      Serial.print("Pulse Width: ");
      Serial.println(pulseWidth);
    }
    if (moveServo == 32) {
      pulseWidth = centerServo;
      Serial.print("Pulse Width: ");
      Serial.println(pulseWidth);
    }

    // stop servo pulse at min and max
    if (pulseWidth > maxPulse) {
      pulseWidth = maxPulse;
      Serial.print("Pulse Width: ");
      Serial.println(pulseWidth);
    }
    if (pulseWidth < minPulse) {
      pulseWidth = minPulse;
      Serial.print("Pulse Width: ");
      Serial.println(pulseWidth);
    }
    moveServo = (0);
    //delay (100);
    // END SERIAL CONTROL SECTION
    break;

  case 6:

    if (debug == 0) {
      //delay (10);
      Serial.println("EXERCISE LOOP");
    }
    /* adjust turnRate and delay to get smooth
     action
     */

    turnRate = (8); 
    if (pulseWidth >= 2250){
      exDir = HIGH;
    }
    if (pulseWidth <= 750) {
      exDir = LOW;
    }
    if (HIGH == exDir){
      pulseWidth = pulseWidth - turnRate;
      delay (10);
    }
    if (LOW == exDir){
      pulseWidth = pulseWidth + turnRate;
      delay (10);
    }    
    break;
       
 
  default:
    if (debug == 0) {
      // delay (10);
      Serial.println("DEFAULT CASE - NOT DEFINED! loop ");
    }
    errorCode = (1);
    // if nothing else matches, do the default
    // default is optional
  } // end switch

  /*
  // DEBUG SECTION
   if (debug == 0) {
   // print pulseWidth back to the Serial Monitor 
   // delay (10);
   Serial.print("Pulse Width: ");
   Serial.print(pulseWidth);
   Serial.println(" us"); // microseconds
   Serial.print ("errorCode = ");
   Serial.println (errorCode);
   } // END DEBUG SECTION
   */

  // Check for errror code not 0, if so go to error function 
  if (0 != errorCode) {
    errorFunction (errorCode);
  }  

  // Check state of Stop Action switch, if on, go to stop action
  // function 
  stopact = digitalRead (stopactPin);

  if (0 == stopact){
    stopactFunction ();
  }


  // If all OK, move the servo to pulseWidth
  servoMove (pulseWidth);

} // END LOOP function /////////////////////////////////////

void servoMove (int pulseWidth){

  // SERVO MOVE SECTION
  // pulse the servo every 20 ms (refreshTime) with current pulseWidth
  // this will hold the servo's position if unchanged, or move it if
  // changed
  
  // stash current millis so value won't change as function runs
  unsigned long storeTime = millis();
  // check to see if it has been long enough since last pulse
  if (storeTime - lastPulse >= refreshTime) {
    digitalWrite(servoPin, HIGH); // start the pulse
    delayMicroseconds(pulseWidth); // continue for pulse width
    digitalWrite(servoPin, LOW); // stop the pulse
    lastPulse = storeTime; // save the time of the last pulse
  }
}
 // END SERO MOVE Function ///////////////////////////////

void errorFunction (int errorCode) {

  if (debug == 0) {
    // print pulseWidth back to the Serial Monitor 
    delay (10);
    Serial.println ("In errror function!");
    Serial.print("Pulse Width: ");
    Serial.print(pulseWidth);
    Serial.println(" us"); // microseconds
    Serial.print ("errorCode = ");
    Serial.println (errorCode);
  }

  // put mechanicals in "safe state"
  pulseWidth = 2300;
  servoMove (pulseWidth);


  // Out of loop, turn off run LED
  runledState = LOW;
  digitalWrite(runledPin, runledState);

  while (true) {  // Will NEVER be true, thus will not return!
    // Something is wrong, turn on error LED (test code)
    // errorledState = HIGH;
    // digitalWrite(errorledPin, errorledState);

    // put mechanicals in "safe state" (if not already there)
    pulseWidth = 2300;
    servoMove (pulseWidth);

    // BLINK THE ERROR LED errorCode times

    int blinkCount=0; // set blinkCount to 0
    while (blinkCount < (errorCode)){ // do following errorCode
      // times; causes flash of errorCode

        errorledState = HIGH; // Turn errorled on for 1/2 second
      digitalWrite(errorledPin, errorledState);
      delay (500);

      errorledState = LOW; // Turn errorled off for 1/2 second
      digitalWrite(errorledPin, errorledState);
      delay (500);

      blinkCount++; // increment blink count
    } // End blink section - when blinking done go to delay

    delay (2000); // leave errorled off for two seconds

  } // end while loop, since doesn't return, repeat 

} // END errorFunction //////////////////////////////


void stopactFunction () {

  if (debug == 0) {
    delay (10);
    Serial.print("beginning of stopactFunction debug=");
    Serial.println(debug); 
    Serial.print("[stopact: ");
    Serial.print(stopact);
    Serial.println("] ");
    Serial.print("Pulse Width: ");
    Serial.print(pulseWidth);
    Serial.println(" us"); // microseconds
    Serial.print ("errorCode = ");
    Serial.println (errorCode);
  }
  // Out of loop, turn off run LED
  runledState = LOW;
  digitalWrite(runledPin, runledState);

  while (0 == stopact) {  //run until 1 == stopact

      // put mechanicals in "safe state"
    pulseWidth = maxPulse;
    servoMove (pulseWidth);

    /* Turn error and run LED's on and off at fairly fast rate (
     railroad signal...) as signifier that you are in stop action mode
     delay doesn't hurt anything since not doing anything critical
     */
    errorledState = HIGH;
    digitalWrite(errorledPin, errorledState);
    runledState = LOW;
    digitalWrite (runledPin, runledState);

    delay (200);

    errorledState = LOW;
    digitalWrite(errorledPin, errorledState);
    runledState = HIGH;
    digitalWrite (runledPin, runledState);

    delay (200);

    // check state of stop action switch
    stopact = digitalRead (stopactPin);


  }
  // If stop action turned off, break out of while loop and return
  // to main loop code 
  return;

} // END stopactFunction ////////////////////////////////

I'm having trouble following the code and I spent a while looking for where maxPulse gets changed only to find it's a constant. That's why constants are usually in upper case.

This worries me

    digitalWrite(servoPin, HIGH); // start the pulse
    delayMicroseconds(pulseWidth); // continue for pulse width
    digitalWrite(servoPin, LOW); // stop the pulse

Any interrupts will affect the timing of the pulse, by how much I don't know but it would manifest itself more at the 700uS end.

delayMicroseconds() does not need interrupts so you could try disabling them for the duration of the pulse and see what helps.


Rob

Right now "maxPulse" is being treated as a constant, in that it doesn't change in the current state of the code, but that will be changing once I figure out just how to do it...

I have a switch in the pushing unit that changes state when the pusher comes OFF the top of the pedal travel. (upLimit, set by pin D11) My plan is to look at that switch and change 'maxPulse' to the value of pulseWidth when it changes. The effect will be to limit the range of the servo to only as much as needed to operate the pedal. (The current design has about 4.5" of travel, most pedals use less than that)

I know what interrupts are in the PC world, but I wasn't all that aware that the Arduino had them - far as I know I don't use them at all, anywhere in the code. Is there a good place to read up on how to use them and / or disable them?

I'm still puzzled by the question of how / why the "stopAction" function is apparently getting called during normal servo operation - the pin that triggers it is tied to a toggle switch that doesn't, or at least shouldn't be, changing state at any time other than if I flip it... However it does look like it is being called judging by the symptoms. I have the pin set as "input pullup" which should keep it at a constant 1 if I don't close the switch, do I need to add another external pullup resistor? If so, how big?

ex-Gooserider

far as I know I don't use them at all, anywhere in the code.

Serial comms and the millis() timer use interrupts so the Arduino is using them even if you aren't. Millis() is standard and given that your code is riddled with Serial.prints I'd say there's a good chance they are being fired as well.

Is there a good place to read up on how to use them and / or disable them?

There are the Arduino reference pages, although I wouldn't necessarily call them "good" :slight_smile: Look up noInterrupts() and Interrupts(). As for how to use interrupts I don't know what you would read but there must be some good docs around somewhere. Try starting here

Meanwhile you can test the theory with

   noInterrupts();
    digitalWrite(servoPin, HIGH); // start the pulse
    delayMicroseconds(pulseWidth); // continue for pulse width
    digitalWrite(servoPin, LOW); // stop the pulse
    Interrupts();

However it does look like it is being called judging by the symptoms.

Easy to tell, make it print something or enable debug. Is it being called or not? It should be simple to find out.

I have the pin set as "input pullup" which should keep it at a constant 1 if I don't close the switch, do I need to add another external pullup resistor?

An external resistor should not be required, but won't do any harm, if nothing else it removes one more variable.


Rob

I think I've found the problem - and it doesn't appear to be code related.

I added the interrupt disable code as suggested, and it didn't help, (doesn't seem to hurt either so I'm leaving it in...)

So I then started adding in some serial print statements to see if I could find where things were going wrong. To get some repeatability, I played with the delay and turnRate variables in the exercise mode to get it to exhibit the bad behaviour in a severe way. (and kept having to play with them as the serial code slowed things down severely...

I ended up with the following in the relevant part of the code -

  // Check state of Stop Action switch, if on, go to stop action
  // function 
  stopact = digitalRead (stopactPin);
 
    // test only - remove later!
    Serial.print("1 =");
    Serial.println(stopact);


  if (0 == stopact){
    
      // test only - remove later!
    Serial.print("2=");
    Serial.println(stopact);

    stopactFunction ();
  }
  // test only - remove later!
    Serial.print("3=");
    Serial.println(stopact);



  // If all OK, move the servo to pulseWidth
 servoMove (pulseWidth);

} // END LOOP function /////////////////////////////////////

void servoMove (int pulseWidth){

  // SERVO MOVE SECTION
  // pulse the servo every 20 ms (refreshTime) with current pulseWidth
  // this will hold the servo's position if unchanged, or move it if
  // changed
  
  // stash current millis so value won't change as function runs
  unsigned long storeTime = millis();
  // check to see if it has been long enough since last pulse
  if (storeTime - lastPulse >= refreshTime) {
    noInterrupts ();
    digitalWrite(servoPin, HIGH); // start the pulse
    delayMicroseconds(pulseWidth); // pulse width
    digitalWrite(servoPin, LOW); // stop the pulse
    interrupts ();
    
    lastPulse = storeTime; // save the time of the last pulse
  }
  // test only - remove later!
    Serial.println(stopact);
  
  
}
 // END SERO MOVE Function ///////////////////////////////

<snip>

void stopactFunction () {
    
  // test only - remove later!
    Serial.print("fu=");
    Serial.println(stopact);
   
 <snip>

If the switch was doing right, I should see a print out of the first and third lines, and the servo move check, but not the second (which should only show if the if test fails) or the stopactFunction test.

What I got was (heavily trimmed)

....
1 =1
3=1
1
1 =0
2=0
fu=0
0
3=1
1
1 =1
3=1
1
.....

with intermittent rounds of the value going to zero. This tells me that for some reason the value for the stopAction switch was going to zero for very short periods intermittently, just enough to get into the function, which would return after one pass through. (because I only see one call to the servo move function) After that the value would go back to high, like it should have been all along, and I'd see normal operation for a few cycles, etc...

After this, I put a scope on the stopAction pin, and watched. It "should" have shown a constant 5VDC, since I have the Arduino set to use the internal pullups, however it was throwing intermittent drops to 0V of about 200uS duration!

I've gotten two suggestions so far on what to do to try and fix this - one is to add a 3.3K external pullup, and the other is to tie all the unused analog pins to ground - haven't done either yet, but thought a progress report on stuff thus far would help...

ex-Gooserider