read a 360° feedback servo

Hey there!

After one year on learning the basics pf Arduino I finally moved on to a real project.

That said for this project I needed one 360° feedback servo and stumbled across the Parallax Feedback 360° High Speed Servo (Feedback 360 Degree - High Speed Continuous Rotation Servo : ID 3614 : $27.99 : Adafruit Industries, Unique & fun DIY electronics and kits).

  1. I ordered one of these servos ( which works flawlesly) but the only problem for me is now how to read the angles of this servo and show them via the serial monitor.

I fiddled around with an Adafruit sample code for Feedback servos Using Feedback | Analog Feedback Servos | Adafruit Learning System

I´m stucked at this code lines:

int getPos(int analogPin)
{
  return map(analogRead(analogPin), minFeedback, maxFeedback, minDegrees, maxDegrees);
}

How can I write the map to the serial monitor?

I´m open for any ideas (also completly new coding attempts).

  1. on a second step I want to move the servo with an command from the PC to an exact degree. Any ideas or hints how this could be done?

  2. and at last I want to home the servo to zero degrees. This should be possible with a simple button and function if I know and can use the Information in steps 1) and 2). Am I right?

naru1305:
Hey there!

After one year on learning the basics pf Arduino I finally moved on to a real project.

That said for this project I needed one 360° feedback servo and stumbled across the Parallax Feedback 360° High Speed Servo (Feedback 360 Degree - High Speed Continuous Rotation Servo : ID 3614 : $27.99 : Adafruit Industries, Unique & fun DIY electronics and kits).

  1. I ordered one of these servos ( which works flawlesly) but the only problem for me is now how to read the angles of this servo and show them via the serial monitor.

I fiddled around with an Adafruit sample code for Feedback servos Using Feedback | Analog Feedback Servos | Adafruit Learning System

I´m stucked at this code lines:

int getPos(int analogPin)

{
 return map(analogRead(analogPin), minFeedback, maxFeedback, minDegrees, maxDegrees);
}




How can I write the map to the serial monitor?


I´m open for any ideas (also completly new coding attempts).


2) on a second step I want to move the servo with an command from the PC to an exact degree. Any ideas or hints how this could be done?

3) and at last I want to home the servo to zero degrees. This should be possible with a simple button and function if I know and can use the Information in steps 1) and 2). Am I right?

Okay to answer the first question:
The answer is in the sketch on the below url.

The 2nd question.
You are from the serial monitor.
based on the input write the new desired angle to the servo motor.

The 3rd question.
homing will be trivial.
based on an event you write 0 as the desired angle to the servo.

I could be wrong, but I think that the tutorial you are trying to use is for a feedback servo that is NOT 360 degree capable.

Your servo IS a 360 degree servo.
So to make it move to a specific position, you sill need to calibrate the feedback values. Once you have that, you can tell the servo to start moving in a particular direction, then keep reading the feedback until it gives a value that corresponds to your desired position.

The 3rd question.
homing will be trivial.
based on an event you write 0 as the desired angle to the servo.

I do not think this is accurate. A 360 continuous rotation servo does not move to s specific angle. It moves forward, backward or stops.

vinceherman:
I could be wrong, but I think that the tutorial you are trying to use is for a feedback servo that is NOT 360 degree capable.

Your servo IS a 360 degree servo.
So to make it move to a specific position, you sill need to calibrate the feedback values. Once you have that, you can tell the servo to start moving in a particular direction, then keep reading the feedback until it gives a value that corresponds to your desired position.
I do not think this is accurate. A 360 continuous rotation servo does not move to s specific angle. It moves forward, backward or stops.

adafruit claims that the tutorial is valid with the 360 degree servo.

It is not complete clear to me how the Hall effect switch is providing the angle the servo is at.
I would have to have one to play with to understand thoroughly the hall-effect position feedback.

I deal with optical disc encoders and linear encoders in cnc machines.

I am not going to write the sketches for him.
I do not have a 360 degree servo to play with so it is up to them to learn.
I still think it is trivial to write a sketch that based on where the serve currently is in rotation with respect to the home position, that the necessary “commands” can be sent to the servo to move to the 0 degree position.

Note that the example code is for a servo which provides the feedback as a simple voltage, hence the analogRead() is all that is needed.

But the servo specification clearly says that feedback is via PWM signal.
"Feedback sensor: Hall effect
Feedback signal: PWM, 3.3V, 910 Hz, 2.7–97.1% duty cycle"

Not the same thing at all. I'm not sure exactly how that works (I've never seen a servo like that) but from the description I guess your options are to input the signal through a RC/filter circuit to convert it to a voltage or, probably more useful, to use pulsein() to read the pulse length of this signal.

Either way you can't really expect the example for a different type of feedback servo to just work.

Steve

Thank you all for your answers!

It was sure that the example sketch wont work for this kind of servo - it was just to get an idea how to get started.

But your answers gave my at least something I can start and try with.

Thank you again!

slipstick:
But the servo specification clearly says that feedback is via PWM signal.
"Feedback sensor: Hall effect
Feedback signal: PWM, 3.3V, 910 Hz, 2.7–97.1% duty cycle"
probably more useful, to use pulsein() to read the pulse length of this signal.

Thanks again Steve, that was the solution and why it did not work with the analogread.

With a simple pulsein() it works flawlessly! You get clear numbers ranging to 1051

From this point now it is easy to get it working like intended :slight_smile:

Hi Naru (and everyone) im a having a similar problem with reading the PWM values correctly and determining angular position. I've followed the datasheet parallax has given to a T. But my problem is that for some inexplicable reason, i am missing ~40 degrees?

Naru have you had a similar issue?

It must be something to do with the hall effect sensor, as on the serial plotter, you can see an immediate change of PWM value.

Screenshot by Lightshot

The image shown here is me twiddling ever so gently around the threshold. Now the output values should be 0 to 100 (0 to 1 but magnified to 100 to show you). however its not really ever reaching 0 or 100.

im actually missing around the first and last 3% (~6% total) of my duty cycle. This should sound about right as the dutycycle minimum is 2.9% and duty cycle max is 97.1%.

//datasheet

but following their example of angular position, im missing around 40 degrees. Im fair new to this scene, im in my third year at uni and my third year project depends on getting this RIGHT.

Can any one enlightening me what im doing wrong?

i kinda need those missing 40 degrees

(can link arduino code too, but this is my first forum post ever and i dont know how to embed it properly for you guys)

I was reading this thread on the Parallax 360 servo. I am struggling to get mine working and was wondering if you could post your code

reading from the datasheet page 3 & 4 i managed to get this working

int readPos(int pwmPin)
{
 int tHigh;
 int tLow;
 int tCycle;

 float theta = 0;
 float dc = 0;
 int unitsFC = 360;
 float dcMin = 0.029;
 float dcMax = 0.971;
 float dutyScale = 1;
 while(1) {
   pulseIn(pwmPin, LOW);
   tHigh = pulseIn(pwmPin, HIGH);
   tLow =  pulseIn(pwmPin, LOW);
   tCycle = tHigh + tLow;
   if ((tCycle > 1000) && ( tCycle < 1200)) break;
 }

 dc = (dutyScale * tHigh) / tCycle;
 theta = ((dc - dcMin) * unitsFC) / (dcMax - dcMin);
 return theta;
}

I know this is an old thread, but I'm currently trying to do the same thing but am unsuccessful. I used the code in aldeba's post but it seems to cause a problem that interrupts the code when I use it. I've read the documentation from Parallax, and basically converted the C code there to Arduino, but I'm still having issues. Has anyone managed to get this to work well?

/*
  Feedback 360 Angle Control [Low Level Example].c
  
  This is a simplified example of how the low-level position control is done.
  These features would normally be tucked away in a library like abdrive360
  or servo360 with functions to request measured angle, set target angle, 
  and etc.  
*/

// Library inlcudes  
#include "simpletools.h"                      // For pulse_in, print, scan etc...
#include "servo.h"                            // For servo pulse control

int pinFeedback = 14;                         // P14 connected to feedback line
int pinControl = 12;                          // P12 connected to control line

volatile int angle, targetAngle;              // Global shared variables
volatile int Kp = 1;                          // Proportional constant   

void feedback360();                           // Position monitoring
void control360();                            // Position control

int main()                                    // Main function
{
  cog_run(feedback360, 128);                  // Run feedback360 in a cog
  cog_run(control360, 128);                   // Run control360 in a cog
  pause(100);                                 // Wait 1/10 s, might not need

  while(1)                                    // Main loop
  {
    print("Enter angle: ");                   // Prompt user for angle
    scan("%d", &targetAngle);                 // Get entered angle
    print("\r");                              // Next line
    while(abs(targetAngle - angle) > 4)       // Display until close to finish
    {
      print("targetAngle = %d, angle = %d\r", // Display target & measured
             targetAngle, angle);             
      pause(50);                              // ...every 50 ms
    }      
  }    
}


void feedback360()                            // Cog keeps angle variable updated
{
  int unitsFC = 360;                          // Units in a full circle
  int dutyScale = 1000;                       // Scale duty cycle to 1/1000ths
  int dcMin = 29;                             // Minimum duty cycle
  int dcMax = 971;                            // Maximum duty cycle
  int q2min = unitsFC/4;                      // For checking if in 1st quadrant
  int q3max = q2min * 3;                      // For checking if in 4th quadrant
  int turns = 0;                              // For tracking turns
  // dc is duty cycle, theta is 0 to 359 angle, thetaP is theta from previous
  // loop repetition, tHigh and tLow are the high and low signal times for 
  // duty cycle calculations.
  int dc, theta, thetaP, tHigh, tLow;         

  // Measure feedback signal high/low times.
  tLow = pulse_in(pinFeedback, 0);            // Measure low time 
  tHigh = pulse_in(pinFeedback, 1);           // Measure high time

  // Calcualte initial duty cycle and angle.
  dc = (dutyScale * tHigh) / (tHigh + tLow);
  theta = (unitsFC - 1) - ((dc - dcMin) * unitsFC) / (dcMax - dcMin + 1);
  thetaP = theta;

  while(1)                                    // Main loop for this cog
  {
    // Measure high and low times, making sure to only take valid cycle
    // times (a high and a low on opposite sides of the 0/359 boundary
    // will not be valid.
    int tCycle = 0;                           // Clear cycle time
    while(1)                                  // Keep checking
    {
      tHigh = pulse_in(pinFeedback, 1);       // Measure time high
      tLow = pulse_in(pinFeedback, 0);        // Measure time low
      tCycle = tHigh + tLow;
      if((tCycle > 1000) && (tCycle < 1200))  // If cycle time valid 
        break;                                // break from loop
    }      
    dc = (dutyScale * tHigh) / tCycle;        // Calculate duty cycle
    
    // This gives a theta increasing int the
    // counterclockwise direction.
    theta = (unitsFC - 1) -                   // Calculate angle
            ((dc - dcMin) * unitsFC) 
            / (dcMax - dcMin + 1);

    if(theta < 0)                             // Keep theta valid
      theta = 0; 
    else if(theta > (unitsFC - 1)) 
      theta = unitsFC - 1;

    // If transition from quadrant 4 to  
    // quadrant 1, increase turns count. 
    if((theta < q2min) && (thetaP > q3max))
      turns++;
    // If transition from quadrant 1 to  
    // quadrant 4, decrease turns count. 
    else if((thetaP < q2min) && (theta > q3max))
      turns --;

    // Construct the angle measurement from the turns count and
    // current theta value.
    if(turns >= 0)
      angle = (turns * unitsFC) + theta;
    else if(turns <  0)
      angle = ((turns + 1) * unitsFC) - (unitsFC - theta);

    thetaP = theta;                           // Theta previous for next rep
  }
}    


// Most rudimentary control system example, 
// just proportional.  This could be done
// in the same cog as the angle mesurement.                                            
void control360()                             // Cog for control system
{
  servo_speed(pinControl, 0);                 // Start servo control cog
  
  int errorAngle, output, offset;             // Control system variables
  
  while(1)                                    // Main loop for this cog
  {
    errorAngle = targetAngle - angle;         // Calculate error
    output = errorAngle * Kp;                 // Calculate proportional 
    if(output > 200) output = 200;            // Clamp output
    if(output < -200) output = -200;
    if(errorAngle > 0)                        // Add offset
      offset = 30;
    else if(errorAngle < 0)
      offset = -30;
    else
      offset = 0;     
    servo_speed(pinControl, output + offset); // Set output
    pause(20);                                // Repeat after 20 ms
  }    
}