Strange problem with the Servo library

Although this is not actually a robotic problem as such, I believe that the folks here will be doing much the same as I am attempting ? so please excuse me if I am wrong in that asssumption.

I am developing a points control system for a model railway, and as a part of thast, I want to automate the situation where setting just one point that forms a crossover will automatically set the matching point accordingly.

I have got all the coding sorted out - no problems thee I am sure, I have debugged it right down to the n'th degree. The switching of the "master" point works as expected, as do all the other point switching operations.

My code does a check on a data array that reports the matching point, if any, and then resets the relevant pointer to address the "slave" point. I then make an identical call to servo.write for the "slave" point, but this simply does not move the servo (which works just fine anywhere else.) even though A CHECK WITH servo.read() suggests it has worked !

I have tried adding a delay(2000) in case it was happening too fast, no difference.

I have tested the servo from other output pins defined in the system, and also tried different pins as the output for the "slave" servo. Everything works when I switch just one point, but doing the second one seems to be ignored by the servo library.

I am using a Mega2560, and have tried various output pins for the "slave" with the same result. I am also using serial comms, so am aware that pin 10 must be left as an Output or the comms will not work, although I am using a different pin for the CS. I have therefore set pin 10 as an output in Setup()

It has driven me nuts for around 2 days now, so I would really appreciate any suggestions as to how to get this "slave" to work as expected ?

Here is a sample of the serial monitor output, the comments printed should be fairly self explanatory, point 6 is the "master" and point 7 the "Slave"

we now have array pointer *pptArray for 6
pptArray[1] = 6
pptArray[2] = 9
pptArray[3] = 0
pptArray[4] = 65
pptArray[5] = 120
pptArray[6] = 7
pptArray[7] = 6
Point 6 set to mode BRANCH, angle 65
Relevant memory array has been updated successfully
Checking if this point [6] is part of a crossover ... 
YES, this point [6] is part of a crossover with point [7]... 
Setting paired point to match ... 
Entering fillPointStructure()
List of data from pptServo, a pointer to ptServo
*pptServo  -  7
*pptServo  -  15
*pptServo  -  1
*pptServo  -  65
*pptServo  -  120
*pptServo  -  6
*pptServo  -  0
We are now going to change the setting for crossover point [7] using pin 15... 
Setting point [7] on pin [15] to 65... 
presult2 for point 7 is 65
Entering updateArray()
we now have array pointer *pptArray for 7
pptArray[1] = 7
pptArray[2] = 15
pptArray[3] = 0
pptArray[4] = 65
pptArray[5] = 120
pptArray[6] = 6
pptArray[7] = 0
Matching crossover Point 7 has been set using pin 15 
SUCCESS - command [1] completed as requested

Many thanks
Ian

Hi Ian, cool project!
Mind posting your code?

That got a bit complicated but I think you're saying that whenever you write to a servo using your "slave" mechanism to sort out the servo/value then the servo doesn't move. That sounds like a coding problem.

Are you sure all servo objects are "attach"ed to the correct pins? If the write seems to have completed but nothing happens my guess is the routing is mixed up so you're writing to something but not to what you think.

No way to make a better guess without seeing the code.

Steve

Hi guys

Thanks for your rapid response

I am quite happy to post my code, but not sure if it wouild help you greatly without all the .h files etc

As a quick example, here is the code sttarting with preparing to change point 6, and thgyen checking ther "related flag" and doing the same for point 7. I hope it makes sense

All the array stuff and pointer allocation is pretty simple in truth. I have 7 arrays, one for each point I want to control right now, For ease of reading, I use one single structure that represents a point/servo control object, and thatr is filled by the relevant calls, always by passing the required point number ( which equals the array concerned)

The debug messages prove that all is well there, anbd the structure pointer I use does contain the correct data for the slave point before it tries to set it.

Here ya go.....

if (pointCommand == 1) {
    //*********************************************//
      // We use the actual pointServoDevice array as this is the only place that we can actually use it from
//      #ifdef _DATAPRINTOUT_
//         sprintf(gbuff, "ABOUT TO SET POINT [%d] PHYSICALLY - pptServo->mainAngle = %d, pptServo->branchAngle = %d", currentPoint, pptServo->mainAngle, pptServo->branchAngle);
//         Serial.println(gbuff);
         sprintf(gbuff, "point %d attached ? = [%s]", currentPoint, pointServoDevice[currentPoint].attached() ? "TRUE" : "FALSE");
         Serial.println(gbuff);
//      #endif
      // Set the specified point to the position specified
      pointServoDevice[currentPoint].write(pointMode == 1 ? pptServo->mainAngle : pptServo->branchAngle);
      int presult = pointServoDevice[currentPoint].read();
         sprintf(gbuff, "presult for point 6 is %d", presult);
         Serial.println(gbuff);

      if (pointMode == 1) 
        pptServo->currMode = 1;
      else 
        pptServo->currMode = 0;
      pptServo->activePoint = currentPoint;

      updateArray(currentPoint);   // Save setting of point data from struct into correct array element

//      #ifdef _DATAPRINTOUT_
         sprintf(gbuff, "Point %d set to mode %s, angle %d", currentPoint, pointMode == 0 ? "BRANCH" : "MAIN", pointMode == 1 ? pptServo->mainAngle : pptServo->branchAngle);
         Serial.println(gbuff);
         Serial.println("Relevant memory array has been updated successfully");
//      #endif

      #ifdef _USERELAY_
         // Now set the point frog using I2C point Relay Board
         Serial.println(F("Setting Point Frog polarity... "));
         //******************************************************
         // LOTS of new code required here....
         // to handle the servo operation to handle frog polarity
         //******************************************************
      #endif

      sprintf(gbuff, "Checking if this point [%d] is part of a crossover ... ", pptServo->pointNumber);
      Serial.println(gbuff);
      //*******************************
      // Now check for a paired point !
      //*******************************
      if (pptServo->pairedPoint > 0 && pptServo->pairedPoint < 8 ) {
        /* 
         *  NB - THIS SHOULD WORK, BUT THE PAIRED POINT IS NOT SWITCHING AS EXPECTED
         */
        // Yes, this is part of a crossover, so set other point as well
        // we need to get the pointstruct updated from paired points array data
        sprintf(gbuff, "YES, this point [%d] is part of a crossover with point [%d]... ", pptServo->pointNumber, pptServo->pairedPoint);
        Serial.println(gbuff);
        Serial.println(F("Setting paired point to match ... "));
        currentPoint = pptServo->pairedPoint;
        //update the one and only structure with data for the "other" point forming the crossover
        //currentPoint now contains the point # of the 2nd point of the pair
        fillPointStructureFromArray(currentPoint);
        // Now we **should** have a new point structure for the "other" point in the crossover
        sprintf(gbuff, "We are now going to change the setting for crossover point [%d] using pin %d... ", pptServo->pointNumber, pptServo->outputPin);
        Serial.println(gbuff);
        // now we have a structure pointer to the paired point in pptServo, we can manipulate that "other" point data
        // We use the pointServoDevice array as this is the only place that we always use it from
        sprintf(gbuff, "Setting point [%d] on pin [%d] to %d... ", currentPoint, pptServo->outputPin, pointMode == 1 ? pptServo->mainAngle : pptServo->branchAngle);
        Serial.println(gbuff);

        // /********************************************************************/
        // ****IMPORTANT**** - For some reason, this is NOT WORKING as it should
        // the point is not moving for some unknown reason.
        // /***************************************************/
        delay(2000); // let point servo system settle down after setting main point
        pointServoDevice[currentPoint].write(pointMode == 1 ? pptServo->mainAngle : pptServo->branchAngle);
      int presult2 = pointServoDevice[currentPoint].read();
         sprintf(gbuff, "presult2 for point 7 is %d", presult2);
         Serial.println(gbuff);

        // these settings are the same as for the other point that forms the pair
        if (pointMode == 1) {
          pptServo->currMode = 1;
        }
        else {
          pptServo->currMode = 0;
        }
        //pptServo->activePoint = pptServo->pairedPoint; // we do not want paired point to be the active point
        
//        currentPoint = pptServo->activePoint;
        updateArray(currentPoint);   // Save setting of point data from struct into correct array element
        currentPoint = pptServo->pairedPoint;   // set active point to original, not the poaired point
//      #ifdef _DATAPRINTOUT_
           sprintf(gbuff, "Matching crossover Point %d has been set using pin %d ", pptServo->pointNumber, pptServo->outputPin);
           Serial.println(gbuff);
//      #endif
      }
      else{
         sprintf(gbuff, "No related crossover point exists for point [%d]", pptServo->pointNumber);
         Serial.println(gbuff);
      }

It is fairly well commented, and abundantly debug messaged as well, as you can see.

If you compare the code with the debug output, it may well help to work out what is going on.

Cheers
Ian

Looks o.k. so far but I can't see any servo objects being created or attached or what's in the pointServoDevice array or quite a lot of other things. I'm assuming it's not going to be anything daft that I might do like falling off the end of one of your many arrays.

Just for interest, if immediately after the failing write, you add a really basic XXX.write(some fixed number) where XXX is point7's basic servo object (i.e. what pointServoDevice[7] points to), what happens then?

Steve

Hi slipstick

Well, as I said, there is one hell of a lot of supporting code that handles all that "mechanistic" stuff. The pointServoDevice is just a Servo object, nothing more. I have 7 of these, and these are what is used to attach the servos to the relevant pins, which are also declared in an array (don't you just love arrays eh ?) Ergo pointServoDevice [x].attach(); so these are fixed up early on in the code and always available for use with Servo library commands. Each one refers to a separate point itself.

I will certainly try your suggestion out to see what happens. :slight_smile: Watch this space as they say.

Ian

Hi again Steve

I have just implemented some code to test your theory, and am now even more confused......

I setup a totally separate "command (4)" that is hard coded to make point 7, (the slave that misbehaves) to switch it from 65 degrees to 125 degrees.

To my utter astonishment, the point attached to pin 9 (point 6) moves), but the point attached to the pin defined and attached to pin 15 (point 7) doesn't move at all !!!!!!!!!!
here is my array for pin definitions, that are used directly by the servo.attach command :-

bool attachServos(){
//   Serial.println("Entering attachServos()");
   pointServoDevice[1].attach(digitalPins[1]);
   pointServoDevice[2].attach(digitalPins[2]);
   pointServoDevice[3].attach(digitalPins[3]);
   pointServoDevice[4].attach(digitalPins[4]);
   pointServoDevice[5].attach(digitalPins[5]);
   pointServoDevice[6].attach(digitalPins[6]);
   pointServoDevice[7].attach(digitalPins[7]);
   return true;
}

as  you can see, servo 7 is attached to digitalpin 15, and yet it is the point attached to digitalpin[6] that actually moves as specified.

I am totally confused by this behaviour, and cannot think of any obvious way to "check" this via the servo library functionality.

Basically, pin 15 is attached to servo 7, and yet when used, it sends the signal via pin 9, that is actually point 6.

It almost seems as though the Servo library cannot cope with more than 6 active servos at any one time ? Surely that is not the case ?

All suggestions are welcomed.....

A quick update after searching on Google, I stuffed the pinout for servo 7, the "slave servo" int A0 just for 'fun'

Sadly, it was still the servo on pin 9 that moved, so it seems that analogue or digital, PWM or not pins makes no difference. This code is very simple as you can see below, it is all HARD CODED

else if (pointCommand == 4) {
   // ONLY used for testing at 07/01/2018
   //*************************//
      NotBusy = false;
      if(!pointServoDevice[7].attached())
         Serial.println("point 7 is NOT ATTACHED");
      else
         Serial.println("point 7 IS ATTACHED");
      pointServoDevice[7].write(120);
      delay(2000);
      pointServoDevice[7].write(65);
      Serial.println("point 7 should have switched twice");
      NotBusy = true;
    }  // end of processing serail data received

so despite hard coding servo 7 to move, and no matter what pin I attach it to, it does nothing, but, and totally unrelated in this instance, point 6 pin (pin 9) does respond. How can that possibly occur ???????

Really weird !!!!!!!!!!

Your code is sufficiently complex that I'm really over my head here. Any minute now I'm giving up. But before I go...

The attach looks fine, all nicely static. I was just worried because you were carrying pin number around with point number that maybe you were trying to do some complex dynamic allocation with the servo objects.

My simple-minded next step would probably be to switch over the pin numbers for points 6 and 7 and see if 6 still works when it's on pin 15 to check that it's nothing to do with specific pins. But since you already tried point7 on several different pins that's very unlikely.

And I'm not being insulting here, just clutching at straws, but your arrays are all created with 8 elements [8] right? From what I've seen you don't use the first (zeroth) entry in any of them and arrays being zero-indexed is the sort of thing I've been known to forget and it always causes tricky to find problems.

Steve

Hi Steve

I understand your comments entirely !

The answer to your issue is NO, each point array holds ALL the relevant data, including current point in use, angles for branch and main settings, the pin that point is connected to (from the outputPins array). The only dynamic changes made to data are the currently active point, and the current setting (0/1) of each point. That is it So must data is read in from an SD card on initialisation, and that SD data is updated only when required, such as when adjusting point angles.

The pointServoDevice[x] is initialized at setup, and never changed, so when I tell pointServoDevice[x].write to move, it is using the pin number initialized in Setup

Hope that clarifies the situation, but see my previous and quite recent message re the point 7 test code.

Best
Ian

Some more info on this wierdness !

I have tried various tests, plugging different servos into the different pins, changing pin definitions etc etc.

In testing this morning, I suddenly realised something pretty odd going on.

I was doing my testing using the same serial inputs as always, and added sokme delays in the execution of the "Slave" point operation. To my total surprise, the SAME servo, that for point 6, is actually making the point 7 movement, not another point. In other words, the output is going to the SAME PIN (9 in this case, despite all debugging showing the pin allocated to move to be something totally different.

I cannot get my head around that, right now, pin 9 sends out data clearly assigned to be sent by pin 31

Any thoughts welcomed ?

Hi,
Can you just make some code to control ONE servo, use a pot on an analog pin to control it?
Then add another servo and code and see if both sweep with the pot.

Keep adding servos and adding code, see if your servos move in sequence.
Ultimately you should have 7 servos all moving in sync.
That will test if your attach and servo position commands work and that you have enough power for the servos.

A very important question! What are you using to power the servos?

Do you have a DMM?

Don't use serial commands for this test.

Tom... :slight_smile:

Hi Tom

Thanks for the feedback. I will certainly try your suggestion out. As to ppower, I do not believe that is an issue as I run a 5v bus from a DC generator so I can also see what current draws are made. Naturally, the GNDs are all connected to the arduino circuits, as they are to my RS485 comms system.

Yes, I do have a decent DMM, what do you suggest I check for ?

I should say here that I am NOT an electronics guru by any means, my major forte in this environment is software, but I am still on a steep learnng curve as far as hardware is concerned

As a final bit of info, I am currently only connecting to 3 servos, which I switch between the various specified digital output pins. My test system is also connected to one NANO Via RS485

Hi,
Monitor the 5V supply at the servo as you do your test with the DMM.

It should be fine skipping over servos as you add extra code.

Thanks.. Tom.. :slight_smile: