Problems using servo and frequency library at same time

Hello, I am fairly new to arduino. I have managed to successfully cobble together bits of (other peoples) code on a few projects, but lack the skills to build my own from the ground up.

I have found a library that can measure RPM accurately for my project and got it working and tested.
I also have the servo library working and tested.

My problem is when I try to use the RPM to try and control the servo the RPM reads near zero until around 1500 RPM (actual), then it shoots up to around 45000.

I have tried restarting from scratch a bunch of times, but as soon as I add this line everything piles up: myservo.attach(9); (i tried different pins, no difference)

/* FreqMeasure - Example with serial output
   http://www.pjrc.com/teensy/td_libs_FreqMeasure.html

   This example code is in the public domain.
*/
// input is hardcoded to D8

#include <FreqMeasure.h>
#include <Servo.h>

Servo myservo;  // create servo object to control a servo

void setup() {
  Serial.begin(19200);
  FreqMeasure.begin();
 
  myservo.attach(9);  // attaches the servo on pin 9 to the servo object
}

double sum = 0;
int count = 0;


void loop() {
  if (FreqMeasure.available()) {
    // average several reading together
    sum = sum + FreqMeasure.read();
    count = count + 1;
    if (count > 3) {        // this is how many revs are averaged together, lower number is faster refresh rate
      float frequency = FreqMeasure.countToFrequency(sum / count);
       int RPM = frequency * 60;

      myservo.write(frequency);
      Serial.println(RPM);
 
      sum = 0;
      count = 0;
    }
  }
}

I have tried many variations of this, mapping the output, creating new variables for the servo position, changing the RPM from int to byte to unsignred int ect, no luck

Have not used the frequency library :thinking: .

float frequency = FreqMeasure.countToFrequency(sum / count);

You do know count will be 4 ?


For debugging, use Serial.print( ) throughout the sketch to print the values of variables to confirm they are what you think they are.

Welcome to the forum.

They both use Timer1. There seems to be a "ServoTimer2" library.

That makes sense.
If later I want to calculate RPM, run it thru a PID library, then to the servo I imagine I'm going to have a similar problem with that as well.

If that doesn't work would the next logical step be to have an arduino dedicated to nothing but reading RPM and sending it to another arduino that does the PID and servo portion?

Thanks for the help

If that ServoTimer2 library works okay, then the problem is solved. However, if you want to add yet another library that uses a timer, then you get stuck again. That's just how it is.

Splitting a project over multiple Arduino boards is a bad idea. Using the I2C bus to communicate between Arduino boards is even worse. Projects fail when they have a I2C bus between boards and there are motors involved.

If you know what you are doing, and you have a good feeling for a clean administration for different versions of sketches in different Arduino boards and if you stay away from the I2C bus, then it is possible. But it will make your project harder, not easier.

There are modules to control servo motors. For example this one: https://www.adafruit.com/product/1411
Then you would have just one Arduino board with one sketch that controls your project.

Thanks for the help, I was ready to give up.

Got it working with servolibrary2, I adjusted the library to run closer to 300hz since all my servos work on the higher digital frequency, even the ones not advertised as digital.

Heres the code

// input is hardcoded to D8

#include <FreqMeasure.h>
#include <ServoTimer2.h>

ServoTimer2 servo2;  // create servo object to control a servo
int val;    // variable

void setup() {
  Serial.begin(19200);
  FreqMeasure.begin();
  

  servo2.attach(9);  // attaches the servo on pin 9 to the servo object
}

double sum = 0;
int count = 0;

void loop() {
  if (FreqMeasure.available()) {
    // average several reading together
    sum = sum + FreqMeasure.read();
    count = count + 1;
    if (count > 1) {        // this is how many revs are averaged together, lower number is faster refresh rate
      float frequency = FreqMeasure.countToFrequency(sum / count);
      float RPM = frequency * 60;

      val = RPM;            // reads the value of the potentiometer (value between 0 and 1023)
      val = map(val, 0, 1023, 544, 2400);     // scale it for use with the servo 544=0 400=full (value between 544 and 2400)
      servo2.write(val);
    
      Serial.println(val);
      Serial.print(" ");
      

      sum = 0;
      count = 0;
    }
  }
}

I also got it working with QuickPID, tested up to 15000RPM and it seems good, heres that code for the next guy that is looking for something fast and accurate enough to control the rpm of a gas engine on a generator or something similar. I have searched many hours and havent found anything that could be used for that on the web.


// tach input is hardcoded to D8

#include <FreqMeasure.h>
#include <ServoTimer2.h>
#include "QuickPID.h"

ServoTimer2 servo2;  // create servo object to control a servo

int val;    // variable
//Define variables we'll be connecting to
float Setpoint = 0, Input = 0, Output = 0, Kp = .01, Ki = 0, Kd = 0; // adjust the values in blue

QuickPID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd,  /* OPTIONS */
               myPID.pMode::pOnError,                   /* pOnError, pOnMeas, pOnErrorMeas */
               myPID.dMode::dOnMeas,                    /* dOnError, dOnMeas */
               myPID.iAwMode::iAwCondition,             /* iAwCondition, iAwClamp, iAwOff */
               myPID.Action::reverse);                   /* direct, reverse */


void setup() {
  Serial.begin(19200);
  FreqMeasure.begin();

  myPID.SetOutputLimits(0, 1023);
  myPID.SetSampleTimeUs(800);     // this is sampling frequency delay in microseconds
  myPID.SetTunings(Kp, Ki, Kd);
  myPID.SetMode(myPID.Control::automatic);
  Setpoint = 6000;       // this is your rpm setpoint

  TCCR0B = TCCR0B & B11111000 | B00000001; // for PWM frequency of 62500.00 Hz

  Serial.println();
  Serial.print(F(" Setpoint: "));  Serial.println(Setpoint);
  Serial.print(F(" Input:    "));  Serial.println(Input);
  Serial.print(F(" Output:   "));  Serial.println(Output);
  Serial.print(F(" Pterm:    "));  Serial.println(myPID.GetPterm());
  Serial.print(F(" Iterm:    "));  Serial.println(myPID.GetIterm());
  Serial.print(F(" Dterm:    "));  Serial.println(myPID.GetDterm());
  Serial.print(F(" Control:  "));  Serial.println(myPID.GetMode());
  Serial.print(F(" Action:   "));  Serial.println(myPID.GetDirection());
  Serial.print(F(" Pmode:    "));  Serial.println(myPID.GetPmode());
  Serial.print(F(" Dmode:    "));  Serial.println(myPID.GetDmode());
  Serial.print(F(" AwMode:   "));  Serial.println(myPID.GetAwMode());



  servo2.attach(9);  // attaches the servo on pin 9 to the servo object
}

double sum = 0;
int count = 0;

void loop() {
  if (FreqMeasure.available()) {
    // average several reading together
    sum = sum + FreqMeasure.read();
    count = count + 1;
    if (count > 1) {        // this is how many revs are averaged together, lower number is faster refresh rate
      float frequency = FreqMeasure.countToFrequency(sum / count);
      int RPM = frequency * 60;

      val = Output;         
      val = map(val, 0, 1023, 544, 2400);     // scale it for use with the servo 544=0 400=full (value between 544 and 2400)
      servo2.write(val);

      Input = RPM;
      myPID.Compute();
      Serial.print(Input);
      Serial.print(" ");
      Serial.println(Output);
      Serial.print(" ");
      Serial.println(Setpoint);
      Serial.println(val);
      Serial.print(" ");

      sum = 0;
      count = 0;
    }
  }
}
1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.