Regular Servo Jitter - every 57 seconds

I'm using Arduino.h, Servo.h, and HX711.h. My Servo jitters routinely about every 57 seconds. I've spent days searching and trying various possible solutions. I suspect the problem is that the PWM signal is being disrupted by something using a timer, but I don't know how to verify that, nor how to find it.

I'm using the RexQualis Mega 2560. The Servo is connected to pin 9. The only two places I use Servo.write() is once in setup() and once in loop(). I've added Serial.print() lines to reveal if Servo.write() is being called unexpectedly. It isn't.

I disconnected from the USB cable and powered the Arduino from a separate 9V 1A supply. The Servo is powered by a 5V 6A supply. The HX711 is powered by a breadboard supply. The breadboard supply also feeds a DFPlayer. The DFPlayer connects to the Mega on RX1 and TX1. No DFPlayer libraries are being used.

I've tried a completely different brand and power level of Servo.

At the top of loop() I set currentTimeStamp = millis(). From then on the code uses currentTimeStamp.

During Idle time, when nothing else is going on, currentTimeStamp is used to periodically report on various state machines. These reports happen multiple times per minute without any Servo jitter. Various buttons & switches are read during each loop. All code sections check either currentTimeStamp or a State machine variables to decide whether to execute.

I use delay() during setup, at the end of a game just prior to another idle period, and in the DFPlayer function to prevent DFPlayer commands from executing too close together. During idle time, DFPlayer is invoked about once per hour to restart the theme music. Otherwise, during idle time, delay() is never used.

  if (currentTimeStamp < (lastDFCommandTime + dfCmdDelay)) {
    delay((lastDFCommandTime + dfCmdDelay) - currentTimeStamp);  // Briefest delay possible
  }
  lastDFCommandTime = currentTimeStamp;   // record time of last command

WIRING:
Servo: pin 9
HX711-DT: pin 4
HX711-CK: pin 5
LED: pin 13
DFPlayer TX: RX1
DFPlayer RX: TX1
Relay1: pin 6
Relay2: pin 7
Buttons & Switches: pins 23-35 odd

Concering the HX711 library, I use Scale.begin() during setup(). I use read_average(), before idle time, during initialization. During idle time, this section of code runs with two library calls...
(note: weighInterval = 80)

  // --------------------------  Weigh periodically  -------------------------
  if (currentTimeStamp > weighTime) {
    // Is Load Cell data available?
    if (Scale.is_ready()) {
      // Yes.
      recordScaleWeights(Scale.read());
      weightOfObject = scaleWeight();
    } 
    weighTime += weighInterval;   // The Time of next weighing
  }

// --------- Function recordScaleWeights -------------------------------------------
// The purpose of this function is to record the most recent RAW scale readings
void recordScaleWeights(long scaleReading) {
  scaleWeights[scaleWeightIndex++] = scaleReading;
  if (scaleWeightIndex == WeightsSize) {
    WeightReady = true;    // This will stay True, after enough weighs are made
    scaleWeightIndex = 0;
  }
}

// ---------- Function scaleWeight -----------------------------------------------
// The purpose of this function is to turn the array of RAW readings into a weight.
float scaleWeight() {
  long weightMin = scaleWeights[0];  // start tracking weights in the array
  long weightMax = weightMin;
  long weightTot = weightMin;
  for (byte i = 1; i < WeightsSize; i++) {
    if (scaleWeights[i] < weightMin) { weightMin = scaleWeights[i]; }  // Track smallest
    if (scaleWeights[i] > weightMax) { weightMax = scaleWeights[i]; }  // Track largest
    weightTot += scaleWeights[i];     // Accumulate
  }
  long weight = ((weightTot - weightMin - weightMax) / (WeightsSize - 2));
  return float(0.9f + (weight - scaleTare) / scaleFactor);
}

I can tell, with my ears, that the theme music plays without disruption. The idle code...

  switch (gameStateNext) {
    case gameStateIdle:     // Idle - Play Theme Music, wait for Go-Watch
      // We get here from gameStateWeighedIdol, Yellow button, bigGreen-bigRed buttons
      
      // Are we already in Idle mode?
      if (gameState != gameStateIdle) {
        // No.  Execute the transition  (i.e. nothing to transition in this state)
        gameState = gameStateIdle;

      } else {
        // Yes.  follow through
        // bigGreen Button will advance state to gameStateGoWatch

        // Did we reach end of Theme music?
        if (songFlagDone) {
          sendDFCommand(dfCmdTrack, 0, dfThemeSong);  // Play the Theme Music
        }   // endIf - end of Theme music
      }   // endIf - in Idle mode?
      break;
    
    case gameStateGoWatch:     // Watch for Go-Trigger

I also wonder what else might disrupt the PWM signal, such as routine garbage collection.

At this time, the code is over 2100 lines. After much testing, all seems well... except for the jitter during idle time.

Any suggestions are very welcome as to what might cause jitter so regularly.

You wrote: "I use delay() during setup, at the end of a game just prior to another idle period, and in the DFPlayer function to prevent DFPlayer commands from executing too close together. During idle time, DFPlayer is invoked about once per hour to restart the theme music. Otherwise, during idle time, delay() is never used.

Code: [Select]

if (currentTimeStamp < (lastDFCommandTime + dfCmdDelay)) {
delay((lastDFCommandTime + dfCmdDelay) - currentTimeStamp); // Briefest delay possible
}
lastDFCommandTime = currentTimeStamp; // record time of last command

How do you know the result in delay((lastDFCommandTime + dfCmdDelay) - currentTimeStamp);
Is not causing the problem? Do you display the value being delayed? No you don't.

Also snippets of code make it impossible to give much help in debugging the problem.

Paul

Paul, thanks for taking the time to read my post and reply. If DFPlayer were invoked during Idle time, the theme music would start over. Therefore, it's clear that the delay() is not being used. Also, the jitter happens routinely, about every 57 seconds, which is out of sync of anything that I can observe. If however, DFPlayer were invoked, I would see a message on the monitor screen.

// -------------------- Function 'sendDFCommand' --------------------
// This function assembles a packet to send to the DFPlayer
// Parameter 'Command' is a command recognized by DFPlayer
// Parameter 'cmdHighByte5' is the high byte of a DFPlayer command parameter
// Parameter 'cmdLowByte6' is the low byte
void sendDFCommand(byte Command, byte cmdHighByte5, byte cmdLowByte6) {

  #ifdef dfDebug
    Serial.print("-%-  dfPlayer Command Issued -%- -  ");
    Serial.print(Command, HEX);
    Serial.print(", ");
    Serial.print(cmdHighByte5, HEX);
    Serial.print(", ");
    Serial.println(cmdLowByte6);
  #endif
  
  if (Command == dfCmdTrack) {
    songFlagDone = false;
    songTrackNumber = cmdLowByte6;   // less than 256 tracks
  } else if (Command == dfCmdStop) { 
    songFlagDone = true; 
  } else if (Command == dfCmdVolume) { 
    currentVolume = cmdLowByte6; 
  }
  
  // Avoid giving commands too quickly, so delay only as much as necessary.
  // Remember, there aren't any negative numbers here, so check before subtracting
  if (currentTimeStamp < (lastDFCommandTime + dfCmdDelay)) {
    delay((lastDFCommandTime + dfCmdDelay) - currentTimeStamp);  // Briefest delay possible
  }
  lastDFCommandTime = currentTimeStamp;   // record time of last command

  // Calculate the checksum - notice the '-'
  unsigned int checkSum = -(dfVersionByte + dfDataLength + Command + 
    dfInfoReq + cmdHighByte5 + cmdLowByte6);

  // Construct the command line
  byte commandBuffer[10] = { dfStartByte, dfVersionByte, dfDataLength, Command, dfInfoReq, 
    cmdHighByte5, cmdLowByte6, highByte(checkSum), lowByte(checkSum), dfEndByte };

  // Send all 10 bytes
  for (int cnt = 0; cnt < 10; cnt++) {
    // DFSerial.write(commandBuffer[cnt]);  // Used for Uno, not Mega
    Serial1.write(commandBuffer[cnt]);    // Used for Mega, not Uno
  }

Nevertheless, I will add your suggestion to Serial.print() the amount of delay when delay() is invoked.

I also wonder what else might disrupt the PWM signal, such as routine garbage collection.

There is no garbage collection in the Arduino environment as there is no dynamic memory allocation, it’s all static at compile time.

The problem is somewhere in the program you didn’t post. Attach it if it’s too big to place within code tags. Code fragments just waste your time and ours.

Its probably beating of the Servo timer interrupts with the normal millis() interrupt on timer2. If the timer2
interrupt is in-flight when the timer1 interrupt used by Servo library fires, that gets delayed a few us,
causing the timing jitter.

There's no-doubt a library somewhere to use timer1 PWM for up to two servos on pins 9 and 10, that
ought to fix it I think.
[ actually no, you have a Mega, and timer2 controls pin 9's PWM on that alas... ]

If the load on the servo is light enough (it should be) so that it won't cause the servo arm to move when the servo is depowered then a simple solution is to detach the servo after a move and attach it again before the next move.

...R

It turns out the Servo jitter is caused by the load cell HX711.h library. I've eliminated all delay() statements and other statements that might cause the Arduino to halt. Upon disabling the HX711 library, my servo is now quiet.

The average time through a loop() is about 0.13 ms. In other words, my code executes the loop about 7000 times per second. So, it doesn't make sense that the servo would jitter, regularly, every 57 seconds; even during idleState when nothing is going on and no functions are being invoked.

Perhaps, to be more accurate, the problem appears to be a conflict in the use of timers for the servo PWM signal, using 'servo.h' and the 'HX711.h' libraries.

So, I'm now on the hunt for servo and load cell libraries that do not conflict. Is there a forum you could suggest for posting a new thread, or can someone suggest a direction in this thread? Thank you all for taking the time you've given me.

1 Like

BTW, here is the code I disabled to quiet the servo:

#define disableHX711 true   // Used to debug Servo Jitter
#ifndef disableHX711  
  #include "HX711.h"
#endif

//Create a Load Cell Object
#ifndef disableHX711
  HX711 idolScale;
#endif

setup()
  // Start Load Cell Object
  #ifndef disableHX711
    idolScale.begin(idolScaleDTPin, idolScaleCKPin);
  #endif

loop()
  if (currentTimeStamp > timeToWeigh) {
    // Is Load Cell data available?
    #ifndef disableHX711
      if (idolScale.is_ready()) {
        // Yes.
        recordScaleWeights(idolScale.read());  // store in array of 6 readings
        weightOfObject = scaleWeight();       // get average of 6 readings
      } 
    #else
      recordScaleWeights(idolScaleTare);
      weightOfObject = scaleWeight();
    #endif
    timeToWeigh += weighInterval;   // The Time of next weighing (80ms)
  }

  Switch(gameState)
    case gameStateGetTare  // this code does not run during idleState
        #ifndef disableHX711
          idolScaleTare = idolScale.read_average(5);  // The first measure is inaccurate
        #endif
  
        #ifndef disableHX711
          idolScaleTare = idolScale.read_average(15);   // Use this one. (raw readings)
        #endif