Using a Parallex 360° with Arduino

Hi all

For a project where we need to simulate a moving head, we have to use a Parallex 360° High Speed. We need to find a way to control the position of the motor. The idea is that we fill in a certain angle and that the motor changes to that position and stays there until we give it another one. Sadly, both of us don't have a lot of experience with programming.

With the datasheet (https://www.mouser.com/pdfdocs/900-00360-Feedback-360-HS-Servo-v12.pdf) and some examples we found on the internet (including this Forum), we managed to have something that looks good, but doesn't work. (We work with an Arduino Uno)

#include <Servo.h>                            

Servo nek; //Name the Servo
int pinFeedback=9;
int pinControl=10;
int angle, targetAngle;
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;
  int errorAngle, output, offset;             // Control system variables         

void setup() {
 pinMode(pinFeedback, INPUT);
 pinMode(pinControl,OUTPUT);
 unsigned long tHigh;
 unsigned long tLow;

}

void loop() {
  //feedback
   tHigh=pulseIn(pinFeedback,HIGH); 
   tLow=pulseIn(pinFeedback,LOW); 
   //dutycycle berekenen
   dc=(dutyScale*tHigh)/(tHigh+tLow);
  theta = (unitsFC - 1) - ((dc - dcMin) * unitsFC) / (dcMax - dcMin + 1);
  thetaP = theta;
//while(1){
  int tCycle = 0;                           // Clear cycle time
    while(1)                                  // Keep checking
    {
      tHigh = pulseIn(pinFeedback, HIGH);       // Measure time high
      tLow = pulseIn(pinFeedback, LOW);        // 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
  }
//}    

    //Controle van servobewegingen
     
  
   // servo_speed(pinControl, 0);                 
    
    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
   delay(20);                                // Repeat after 20 ms
   
}  

}

Is there someone here who has an idea how to implement this correctly? Or by any chance has a code that already works and that we could use?

It is part of a project we have at university, in which we have to calculate where a sound source is located based on scattering of sound waves on a sphere. To be able to do this, the 'head' (sphere) should be able to turn. Both of us are Engineering Physics students, so our programming skill is only minimal (and apparently not good enough for this task). It would be stupid that our project doesn't work because of an issue that isn't really something we can work on.

Thanks in advance!

Sincerely,
Matmax

our project doesn't work because of an issue that isn't really something we can work on.

Of course you can work on your programming skills. That is what Arduino is for! Beginners on this forum do it all the time, and some eventually become experts.

Your problem is that you started an advanced programming project without going through the basic steps of learning how the Arduino works: blink an LED, read a voltage, control a standard servo, etc.

We recommend going through the provided example programs, and spend some time on line studying and applying tutorials. In a couple of days you will be in a much better position to tackle this project.

Note: DO NOT attempt to power servos or motors with the 5V Arduino output. It won't work, and you can damage the Arduino.

Well that code doesn't even compile. Do you have a version that does at least compile and do something?

If you do get it working you will need to provide more detail than just "doesn't work". What does it do? What should it be doing differently? Most of us won't have access to that particular servo so we can't really do the testing for you.

Steve

Hi Steve

Thanks for our answer!

To be honest, we tried to rewrite a code we found on the Parallax website, which is in c, in the Arduino environment.

This is the code we used.

/*
  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
  }    
}

We are still trying to make something out of it, because there is no point in giving up.

Greetings,
Matmax

I managed to get this working to read the position and the results were pretty accurate.

https://forum.arduino.cc/index.php?topic=524993.msg3701956#msg3701956

I have been trying something very similar to get the Parallex 360 feedback servo to work for a motorized blinds project. The problem that I am running into with the code provided by Parallex is that it is written in Propeller C, and "cog_run" within the main function is not recognized by Arduino.

cog_run(feedback360, 128); // Run feedback360 in a cog
cog_run(control360, 128); // Run control360 in a cog

First of all, what is cog_run anyway?
Second, is there an equivalent of cog_run in Arduino?

I see that aldeba has modified the feedback360 function to return a value of "theta"(in my case I would be using the "angle" variable seen in MatMax's post from the original Parallex code instead since my rotations can be =>360 degrees, and "theta" is only valid from 0-359).

Last question is how would you call and structure the theta/angle variables (which is in the feedback360 function) with the servo.write() function so that theta/angle is continuously being read and updated as the servo moves? Can I just run the feedback360 function as seen below and the angle variable will get updated every time it loops? Or do I have to modify the feedback360 function so that it returns an "angle" value similar to what aldeba did?

This is part of what I have so far in my blinds going up function:

void blindsup() {
myservo.attach(13); //servo attached to control pin 13
while (angle < up) { //up defined as angle in which the blinds are up
myservo.write(0); //my comments: move the servo in one direction
feedback360(); //run the function feedback360()
Serial.println(angle); //
}
myservo.write(90);
myservo.detach();
}

Any help would be greatly appreciated!!!

It looks like a cog is an independent core in a multi-core system. The Arduino Due (and a few other Arduinos) has a scheduler built in which will probably do a similar job.

It would be better to implement it without the scheduler though. You probably don't want or need a Due.

So there's no Arduino ready code for the servo?

The pdf was interesting reading, it should contain everything one needs to know for coding it from scratch.

If I got it right, if you connect it like normal servo, you get a continuous servo, where you control its rotation speed and direction. To make it rotate to a given angle, you need your own function:

The function gets the desired angle, then:

  • read the present angle
  • decide whether to rotate clocwise or counterclockwise
  • start rotating, continuously reading the angle
  • stop when the desired angle is reached, perhaps slowing down when near

It's strange that the feedback signal is an analog PWM. You have to actually measure the length of the pulse. I guess that's done with an interrupt handler. An interrupt is launched when a pin change occurs. If it goes high, the current µs is read. If it goes low, the last read µs is subtracted from the current µs time and saved to a global variable. No need to calculate the actual angle in the interrupt routine. Only when you need to know the angle, you calculate it from the global variable.

MatMax:
Hi all

For a project where we need to simulate a moving head, we have to use a Parallex 360° High Speed. We need to find a way to control the position of the motor. The idea is that we fill in a certain angle and that the motor changes to that position and stays there until we give it another one. Sadly, both of us don't have a lot of experience with programming.

With the datasheet (https://www.mouser.com/pdfdocs/900-00360-Feedback-360-HS-Servo-v12.pdf) and some examples we found on the internet (including this Forum), we managed to have something that looks good, but doesn't work. (We work with an Arduino Uno)

#include <Servo.h>                            

Servo nek; //Name the Servo
int pinFeedback=9;
int pinControl=10;
int angle, targetAngle;
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;
 int errorAngle, output, offset;             // Control system variables

void setup() {
pinMode(pinFeedback, INPUT);
pinMode(pinControl,OUTPUT);
unsigned long tHigh;
unsigned long tLow;

}

void loop() {
 //feedback
  tHigh=pulseIn(pinFeedback,HIGH);
  tLow=pulseIn(pinFeedback,LOW);
  //dutycycle berekenen
  dc=(dutyScale*tHigh)/(tHigh+tLow);
 theta = (unitsFC - 1) - ((dc - dcMin) * unitsFC) / (dcMax - dcMin + 1);
 thetaP = theta;
//while(1){
 int tCycle = 0;                           // Clear cycle time
   while(1)                                  // Keep checking
   {
     tHigh = pulseIn(pinFeedback, HIGH);       // Measure time high
     tLow = pulseIn(pinFeedback, LOW);        // 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
 }
//}

//Controle van servobewegingen
   
 
  // servo_speed(pinControl, 0);                
   
   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
  delay(20);                                // Repeat after 20 ms
 
}

}





Is there someone here who has an idea how to implement this correctly? Or by any chance has a code that already works and that we could use?

It is part of a project we have at university, in which we have to calculate where a sound source is located based on scattering of sound waves on a sphere. To be able to do this, the 'head' (sphere) should be able to turn. Both of us are Engineering Physics students, so our programming skill is only minimal (and apparently not good enough for this task). It would be stupid that our project doesn't work because of an issue that isn't really something we can work on.

Thanks in advance!

Sincerely,
Matmax

Matmax

You have a basic programming mistake ; The Variables Declaration !

Here I see that dc is not and integer ;

dc=(dutyScale*tHigh)/(tHigh+tLow);

But you have chosen the dc as int ;

int dc,...

You need to declare proper type for your dc variable, for example float could work here.

Hadi

Hi, could anyone help me please. I have Nodemcu v1 and parallax feedback 360 servo. I'm sorry but my programming skils are very limited and probably someone will have code that I'm looking for. I want to do the motorised blinds. All I want is that it stops where I point it to low/high. I have mqtt sketch working that enable mqtt control over clockwize and anticlockwise movement aswell as stop. The whole idea is when I send command do mcu for it to go one direction and stop in 1 position and when I send another command for it to opposite direction and stop in previous position.

Could any of you talented guys/gals help me by any chance?

Thank You.

Hello,

you have problem on your code.

errorAngle = targetAngle - angle; // Calculate error

  • 'errorAngle' does not name a type

your code looks complicated, with a if else comand without {}.

MatMax:
Hi all

For a project where we need to simulate a moving head, we have to use a Parallex 360° High Speed. We need to find a way to control the position of the motor. The idea is that we fill in a certain angle and that the motor changes to that position and stays there until we give it another one. Sadly, both of us don't have a lot of experience with programming.

With the datasheet (https://www.mouser.com/pdfdocs/900-00360-Feedback-360-HS-Servo-v12.pdf) and some examples we found on the internet (including this Forum), we managed to have something that looks good, but doesn't work. (We work with an Arduino Uno)

#include <Servo.h>                            

Servo nek; //Name the Servo
int pinFeedback=9;
int pinControl=10;
int angle, targetAngle;
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;
  int errorAngle, output, offset;            // Control system variables

void setup() {
pinMode(pinFeedback, INPUT);
pinMode(pinControl,OUTPUT);
unsigned long tHigh;
unsigned long tLow;

}

void loop() {
  //feedback
  tHigh=pulseIn(pinFeedback,HIGH);
  tLow=pulseIn(pinFeedback,LOW);
  //dutycycle berekenen
  dc=(dutyScale*tHigh)/(tHigh+tLow);
  theta = (unitsFC - 1) - ((dc - dcMin) * unitsFC) / (dcMax - dcMin + 1);
  thetaP = theta;
//while(1){
  int tCycle = 0;                          // Clear cycle time
    while(1)                                  // Keep checking
    {
      tHigh = pulseIn(pinFeedback, HIGH);      // Measure time high
      tLow = pulseIn(pinFeedback, LOW);        // 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
  }
//}

//Controle van servobewegingen
   
 
  // servo_speed(pinControl, 0);               
   
    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
  delay(20);                                // Repeat after 20 ms
 
}

}





Is there someone here who has an idea how to implement this correctly? Or by any chance has a code that already works and that we could use?

It is part of a project we have at university, in which we have to calculate where a sound source is located based on scattering of sound waves on a sphere. To be able to do this, the 'head' (sphere) should be able to turn. Both of us are Engineering Physics students, so our programming skill is only minimal (and apparently not good enough for this task). It would be stupid that our project doesn't work because of an issue that isn't really something we can work on.

Thanks in advance!

Sincerely,
Matmax

Hey Matmax,

Were you able to get it working? Need an update :slight_smile:
I am working on a similar project with the same code.

Thanks,
Sgowdar