# Automating Rotary Table



## OldRon (Dec 17, 2019)

Below is the start of the code for automating my rotary table. The code calculates the number of pulses required for the Yaskawa servo motor which will be running at 3:1 reduction. The fractional part of the pulses is fed back into the process.  That basically eliminates accumulated  error.  The code below is a console program that loops from 359º to 360º and outputs the Commanded Position, Output Position, Number of Pulses and the Error in Seconds.

L O G I C:

Table gear ratio: 90:1
Degrees per Table Revolution: 4
Degree(Seconds) per Table Revolution: 14400 (((1 x 60) x 60) x 4)

Motor Pulses per Revolution: 2048
Pulses x Gear Ratio: 6144

Pulses per Degree(Second): 14400 / 6144 = 2.34375
Table Resolution: 1 Second: (2.34375 Pulses )

That logic will work with any Step & Direction motor but table resolution increment goes up as motor pulses go down. With low motor pulses positioning speed  also increase the resolution increment.

The Code:

// Get Pulses

#include <stdafx.h>
#include <iostream>

using namespace std;

void main()
{
  double degrees=360;
  double poserr=0;
  double pulses=0;

  for (degrees=359; degrees<360
  {
    pulses=(((degrees*3600)/2.34375)+poserr); // Eat the previous Error.

    if ((pulses-int(pulses))>=.5) 
    {
       poserr=(pulses-int(pulses+1)); 
       pulses=int(pulses+1);
    }
    else if (pulses-int(pulses)<.5)
    {
       poserr=(pulses-int(pulses)); 
       pulses=int(pulses);
    }

    std::cout << "Command: " << degrees;
    std::cout << " - Output: " << ((pulses/3600)*2.34375);
    std::cout << " - Pulses: " << pulses;
    std::cout << " - Error: " << ((poserr/3600)*2.34375);
    std::cout << "\n";
    std::cout << "\n";

    degrees=(degrees+.01);
  }

  std::cin.get();
}


----------



## Cogsy (Dec 18, 2019)

If you're declaring 'using namespace std;' then you don't need to reference the std:: in your input/outputs. However, best practice is not to declare namespace std so you would need to include the references to std:: each time you use them. As it stands now, you have the worst of both worlds (extra typing along with the risks and memory demands of namespace std).


----------



## OldRon (Dec 18, 2019)

Cogsy said:


> If you're declaring 'using namespace std;' then you don't need to reference the std:: in your input/outputs. However, best practice is not to declare namespace std so you would need to include the references to std:: each time you use them. As it stands now, you have the worst of both worlds (extra typing along with the risks and memory demands of namespace std).



That bit of code is my first whack at C++ so your advice is greatly appreciated. First thing this morning it dawned on me that there was no need for the second conditional statement because if the first condition failed then 'else' it had to be the default. The if and else could be eliminated if I could figure out why 'round' is an undeclared identifier. I was told to include math.h and that didn't resolve the error.

Ron


----------



## awake (Dec 18, 2019)

OldRon said:


> Degree(Seconds) per Table Revolution: 14400 (((1 x 60) x 60) x 4)
> 
> Motor Pulses per Revolution: 2048
> Pulses x Gear Ratio: 6144
> ...



In this description of the logic, I think you've got your understanding of the ratio upside down. There are 14400 seconds per revolution of the table input, and the servo + gear reduction requires 6144 pulses per revolution. 14400 sec per rev / 6144 pulses per rev = 2.34375 *seconds per pulse* - not the other way around. Likewise, it does not take 2.34375 pulses to achieve 1 second; 1 pulse gives a movement of 2.34375 seconds.

Note that in the code, however, it looks like you are applying the ratio correctly, dividing the number of seconds by 2.34375 seconds per pulse.


----------



## Cogsy (Dec 18, 2019)

OldRon said:


> First thing this morning it dawned on me that there was no need for the second conditional statement because if the first condition failed then 'else' it had to be the default. The if and else could be eliminated if I could figure out why 'round' is an undeclared identifier. I was told to include math.h and that didn't resolve the error.



You don't need the second conditional but it doesn't hurt anything. Try *#include <cmath> *and round() should work (for C++ 11 onwards).


----------



## OldRon (Dec 19, 2019)

awake

I thought that I had that nailed down. It's amazing how another person questioning your logic can so easily cast doubt over confidence. I'll have to go back to the drawing board.

Ron


----------



## awake (Dec 19, 2019)

It's the next to last line that is problematic. Look at where the units are:

552960 pulses
-----------------
1 revolution

and 

1296000 seconds
--------------------
1 revolution

Your goal is pulses per second:

pulses
----------
1 second

But what you are actually producing is 

Seconds                   pulses
----------         /     ----------
revolution              revolution

which equals

Seconds                   revolution
----------         *     ---------------
revolution              pulses

Cancel out the units in common, and what you are left with is

Seconds
----------
pulse

Which is the inverse of what you saying. You are calling that next to last line "Pulses per Degree (Second)", but actually you have calculated Seconds per Pulse. Your math is correct, and as I noted, your code is correct as well; it is your units in this description that are upside down.

To put it another way, the resolution of your control is 2.34375 seconds - that is the minimum movement you can achieve with each pulse.


----------



## OldRon (Dec 19, 2019)

awake,

/*
Rotary Table

Resolution: Degree(Seconds) * 360 = 1296000 per table (revolution)
Gear Ratio: 90:1 (4º per Input Rotation)
4º(Seconds) = 14400

Servo MOtor/Drive
Motor Pulses per Revolution: 2048
Gear Reduction: 3:1
1 Revolution @ Final Drive: 6144 motor pulses

6144 motor pulses rotated the table 14400 seconds.
1 motor pulse would rotate the table: 2.34375 seconds (14400 / 6144)
*/

Based on that I don't see an error in my logic.
( (15º * 3600) / 2.34375) = 23040 pulses

Ron


----------



## Cogsy (Dec 20, 2019)

I think it's just your descriptions that are problematic but you're using the resulting figures correctly in your code. From your logic section in post #1:
*Pulses per Degree(Second): 14400 / 6144 = 2.34375
Table Resolution: 1 Second: (2.34375 Pulses )*

Here you are calling it 2.34375 pulses per second whereas it's actually 2.34375 seconds per pulse (your description indicates pulses/seconds but your math is seconds/pulses). Similarly, your table resolution should be 2.34375 seconds (table movement for 1 motor pulse).


----------



## awake (Dec 20, 2019)

Yes, what Cogsy said. As I said above, your code is correct.

I'm sorry for making this a big deal ... It's just that I have written a lot of software, and this is the sort of thing that can bite you later, and take forever to debug. Better to get it all straight now, both in the code AND in your comments or descriptions.


----------



## Qtron (Dec 20, 2019)

...
Based on that I don't see an error in my logic.
( (15º * 3600) / 2.34375) = 23040 pulses

Ron[/QUOTE]
Hi Ron,
am interested in what U are designing re this divider.. Any chance of an overview, micro type, Display type, objectives etc?
Sorry but cant seem to find the beginning of the story!
cheers,


----------



## OldRon (Dec 21, 2019)

awake said:


> Yes, what Cogsy said. As I said above, your code is correct.
> 
> I'm sorry for making this a big deal ... It's just that I have written a lot of software, and this is the sort of thing that can bite you later, and take forever to debug. Better to get it all straight now, both in the code AND in your comments or descriptions.



You have no reason to apologize. Discussion is enlightening.

Ron


----------



## OldRon (Dec 21, 2019)

> Ron
> Hi Ron,
> am interested in what U are designing re this divider.. Any chance of an overview, micro type, Display type, objectives etc?
> Sorry but cant seem to find the beginning of the story!
> cheers,



The man that sponsor all of the information in this thread delivered a very thorough review of how he built his indexer. It's hard for me to wrap my head around another mans logic so I have to start from ground zero and develop my own logic and engineering. I have the rotary table, the servo motor and the drive amplifier along with the key components shown in this thread. When I grow tired of looking at the motor sitting on the rotary table then when able I'll make some chips and mount the motor. At this time I'm enjoying a gout attack so I'm doing the paper work. I would suggest that chose the type of motor for the project and then make yourself knowledgeable of the motor and drive. There's no shortage of help for those that make an effort but there's very little help for those that want a finished solution. Dive in.


----------



## OldRon (Dec 22, 2019)

awake, Cogsy,

I want to thank both of you for your input and insight. When  a person 'knows' that they are right then it's difficult to show them that they are wrong. My logic was on both sides of the argument (discussion). First it was 2.34375 pulses and mid way through the discussion I unwittingly flipped to 2.34375 seconds. I started to concede the argument last night but once again I wavered. Below is my last conclusion and if it's not right then the world is going to have to change.

In the interest of resolution I switched from a 3:1 gearhead to a 5:1 gearhead. I don't need to rotate 360º in 9 seconds.

/*
Given:
Degree * Minutes * Seconds = 3600 Seconds
Rotary Table gear ratio: 90:1
Motor ppr: 2048
Motor Gearhead ratio: 5:1

Logic:
One revolution @ 5:1 Gearhead final drive = 10240 Motor ppr (2048 * 5)
One revolution of Rotary Table hand crank = 4º * 3600 = 14400 Seconds
Therefore, 1 Motor ppr rotates the Rotary Table 1.40625 Seconds (Indexer Resolution)
Formula: ((Degrees * Seconds) / Indexer Resolution) = pulses
Seconds) / Indexer Resolution will be a constant
*/


----------



## Cogsy (Dec 23, 2019)

Your math is fine but your pseudo-code formulas are a little confusing to me (but that might just be your own personal short-hand). In your first formula, *Degree * Minutes * Seconds = 3600 Seconds*, I think this should be : (Degrees * 3600) + (Minutes * 60) + (Seconds) = Total Seconds (this is really being pedantic as I can see what you were trying to do).

For your final formula, I don't really get what you're aiming at (that equals sign inside the brackets confuses me). You seem to be saying that your ratio "Total seconds/resolution" equals your motor speed * time "Pulses per second * seconds" (which would be true) which is all then divided by your resolution to achieve a constant (which I don't believe to be true). The first half of the equation can be simplified to "required pulses = pulse speed * time" which is always going to be true but is going to vary depending on how far you want to move the indexer so this value over the indexer resolution will vary. I'm sure you were aiming for something different here but I've missed the point.


----------



## Qtron (Dec 24, 2019)

Cogsy said:


> Your math is fine but your pseudo-code formulas are a little confusing to me (but that might just be your own personal short-hand). In your first formula, *Degree * Minutes * Seconds = 3600 Seconds*, I think this should be : (Degrees * 3600) + (Minutes * 60) + (Seconds) = Total Seconds (this is really being pedantic as I can see what you were trying to do).
> 
> For your final formula, I don't really get what you're aiming at (that equals sign inside the brackets confuses me). You seem to be saying that your ratio "Total seconds/resolution" equals your motor speed * time "Pulses per second * seconds" (which would be true) which is all then divided by your resolution to achieve a constant (which I don't believe to be true). The first half of the equation can be simplified to "required pulses = pulse speed * time" which is always going to be true but is going to vary depending on how far you want to move the indexer so this value over the indexer resolution will vary. I'm sure you were aiming for something different here but I've missed the point.


Hi,
am interested in this divider.. 
where do i find  an overview, micro type, Display type, objectives etc?
Sorry but cant seem to find the beginning of the story!
cheers,


----------



## Cogsy (Dec 28, 2019)

Qtron said:


> Hi,
> am interested in this divider..
> where do i find  an overview, micro type, Display type, objectives etc?
> Sorry but cant seem to find the beginning of the story!
> cheers,


I think this project is just starting so this thread is the beginning...


----------



## Qtron (Dec 28, 2019)

OldRon said:


> The man that sponsor all of the information in this thread delivered a very thorough review of how he built his indexer. It's hard for me to wrap my head around another mans logic so I have to start from ground zero and develop my own logic and engineering. I have the rotary table, the servo motor and the drive amplifier along with the key components shown in this thread. When I grow tired of looking at the motor sitting on the rotary table then when able I'll make some chips and mount the motor. At this time I'm enjoying a gout attack so I'm doing the paper work. I would suggest that chose the type of motor for the project and then make yourself knowledgeable of the motor and drive. There's no shortage of help for those that make an effort but there's very little help for those that want a finished solution. Dive in.


At mercy of others, cant write code. Could learn, but would ages, & take away from remaining health. (Oedema)


----------



## tjwal (Dec 28, 2019)

OldRon said:


> The man that sponsor all of the information in this thread delivered a very thorough review of how he built his indexer. It's hard for me to wrap my head around another mans logic...in.


Hi Ron
Which thread are you referring to?  There are quite a few different indexer/dividing threads.

I have a version of the one described in”Rotary Table for Dummies” thread hooked up to a spin index.  I’ve thought about mounting it to my rotary table as well.  As designed it does not provide power feed.  Does the one you’re working on do that?

The only comment I have on the code is; why on earth are you working in seconds? It’s bad enough that there are 360 degrees to a circle without making it any worse. LOL

John


----------



## awake (Dec 28, 2019)

Yes, but ... maybe it is just second nature.


----------



## OldRon (Dec 30, 2019)

tjwal said:


> Hi Ron
> Which thread are you referring to?  There are quite a few different indexer/dividing threads.
> 
> I have a version of the one described in”Rotary Table for Dummies” thread hooked up to a spin index.  I’ve thought about mounting it to my rotary table as well.  As designed it does not provide power feed.  Does the one you’re working on do that?
> ...



John,

There are a few things to consider about using rotary table positioning for machining. 1st, motor capability. 2nd, longevity of the rotary table gears. 3rd, practicality. I can assure you that the straight cut gear on the table will fail due to the worm having a very small contact patch. In my case my CNC milling machine only has 3 axis so the rotary table cannot be part of the coordinated motion. For me that combined with having circular interpolation makes machining with the rotary table impractical.

Ron


----------



## OldRon (Dec 30, 2019)

Qtron said:


> At mercy of others, cant write code. Could learn, but would ages, & take away from remaining health. (Oedema)



Qtron,

Programming is something that people consider to hard before they have tried. I have programmed in Visual Basic 6.0 for 20+ years so for me learning C++ was no harder than singing the national anthem in Chinese. I started with the butt stupid "Hello World" lesson and two hours later I compiled the above prototype code.

Ron


----------



## OldRon (Dec 30, 2019)

The code below is my revision of group member, bmac2's code that he published in Arduino Rotary Table for Dummies. To my knowledge there is nothing wrong with his code. Every coder has their own flavor of logic and it's in that logic where the power of the code resides. Two weeks ago I had never written code in C++ so it was a ground zero start up when I compiled my FIRST C++ console code, the butt stupid default "Hello World" program. Today I'm right at home in the Arduino C++ programming language. I started in MS VS 2019 and I was very disappointed when I learned that the Arduino IDE had it's own version of C++ but it wasn't hard to adopt. The hardest part of learning to write code is shaking off the preconceived notion that it's hard to learn. The down side to learning how to write code is that you will not be able to resist writing a program to control everything including your wife. If you have ever wanted to learn how to write computer programs then don't wuss out before you have tried.


```
/*
My revision of homemodelenginemachinist member, bmac2's code.
https://www.homemodelenginemachinist.com/threads/arduino-rotary-table-for-dummies.26744/

Auto Indexer Control
     (12/29/19)
     = Given =
Rotary Table gear ratio: 90:1
360 degrees = 1296000 seconds
Servo Motor gear ratio: 5:1
Servo Motor ppr: 2048

Servo Motor ppr per Rotary Table Revolution: ((2048 * 5:1) * 90:1) = 921600
(1296000 / 921600) = 1.40625
Therefore, the Rotary Table will rotate 1.40625 seconds per Motor ppr.
*/

#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <Wire.h>

const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] =
{
 {'1','2','3','A'},
 {'4','5','6','B'},
 {'7','8','9','C'},
 {'.','0','#','D'}
};
byte rowPINS[ROWS] = {11,10,9,8};
byte colPINS[COLS] = {7,6,5,4};

/*
Initialize instance of Classes, Keypad and LiquidCrystal_I2C.
*/
Keypad kypd = Keypad(makeKeymap(keys),rowPINS,colPINS, ROWS, COLS);
LiquidCrystal_I2C lcd(0x27,20,4);

/*
Set Up Variables
*/
const float shorty = (3600 / (1296000 / ((2048 * 90) * 5)));
const int dwell = 1;
float degrees = 0;
float curpos = 0;
long steps = 0;
int evnt = 0;

/*
Assign Pins
*/
const int step = 2;
const int direction = 3;

void setup()
{
  lcd.init(); // Initialize the LCD 
  pinMode(step, OUTPUT);
  pinMode(direction, OUTPUT);
  lcd.backlight();
  lcd.print("Auto Indexer Control"); // Print welcome message to the LCD.
  lcd.setCursor(4,2);
  lcd.print(" ");
  lcd.setCursor(3,3);
  lcd.print("updated 2019");
  delay(2000);
  lcd.init();
  evnt = 0;
  char key = kypd.getKey();
  lcd.print("Enter Selection:"); 
  lcd.setCursor(0,1);
  lcd.print("Degrees   = A");
  lcd.setCursor(0,2);
  lcd.print("Divisions = B");
  lcd.setCursor(0,3);
  lcd.print("JOG       = C");

  while(evnt == 0)
  {
    key = kypd.getKey();

    switch (key)
    {
      case NO_KEY: break;
  
      case 'A': degrees = getdegrees();
                lcd.clear();
                evnt = 1;
                break;
      case 'B': degrees = getdivisions();
                evnt = 2;
                break;
      case 'C': degrees = getjoginc();
                lcd.clear();
                evnt = 3;
                break;
    }
  }
}
 
void loop()
{
  lcd.clear();
  char key = kypd.getKey();
  curpos = 0;
  lcd.setCursor(7,0);
  lcd.print("Total:  ");
  lcd.print(curpos,2); // total steps
  lcd.setCursor(0,3);
  lcd.print("FOR=A   REV=B    X=C");

  while(key != 'C') // C will return to start menu
  {
    lcd.setCursor(0, 0);
    lcd.print(degrees, 2);
    lcd.print((char)223);
    key = kypd.getKey();

    if(key == 'A') // FORWARD
    {
      curpos = curpos + degrees;
      steps = getsteps(degrees);
      digitalWrite(direction, LOW);
      printadvance();
    }

    if(key =='B') // REVERSE
    {
      curpos = curpos - degrees;
      steps = getsteps(degrees);
      digitalWrite(direction, HIGH);
      printadvance();
    }
  }
  lcd.init();
  setup();
}
 
int getjoginc()
{
  int joginc = 0;
  char key = kypd.getKey();

  lcd.clear();
  lcd.setCursor(6,0);
  lcd.print("Jogging");
  lcd.setCursor(0,1);
  lcd.print("A=1 B=10 C=100 Steps");
  lcd.setCursor(0,2);
  lcd.print("Enter Degrees:");
  lcd.setCursor(0,3);
  lcd.print("OK = # ");
  lcd.print((char)60);
  lcd.print((char)45);
  lcd.print(" D");

  while(key != '#')
  {
    switch (key)
    {
      case NO_KEY: break;
      case 'A': joginc = 1;
                lcd.setCursor(14,2);
                lcd.print(degrees);
                break;
      case 'B': joginc = 10;
                lcd.setCursor(14,2);
                lcd.print(degrees);
                break;
      case 'C': joginc = 100;
                lcd.setCursor(14,2);
                lcd.print(degrees);
                break;
      case 'D': lcd.setCursor(14,2);
                lcd.print(" ");
                lcd.setCursor(14,2);
                break;
    }
    key = kypd.getKey();
  }
  return joginc;
}
 
float getdivisions()
{
  char key = kypd.getKey();
  float dgrs = 0;
  String divstr;
  int divs = 0;

  lcd.clear();
  lcd.setCursor(0,1);
  lcd.print("Enter Division:");
  lcd.setCursor(0,3);
  lcd.print("OK = # ");
  lcd.print((char)60);
  lcd.print((char)45);
  lcd.print(" D");
  lcd.setCursor(16,1);

  while(key != '#')
  {
    switch (key)
    {
      case NO_KEY: break;
      case 'D': lcd.setCursor(16,1);
                lcd.print(" ");
                lcd.setCursor(16,1);
                divstr = "";
                break;
      case '0': divstr += ("0");
      case '1': divstr += ("1");
      case '2': divstr += ("2");
      case '3': divstr += ("3");
      case '4': divstr += ("4");
      case '5': divstr += ("5");
      case '6': divstr += ("6");
      case '7': divstr += ("7");
      case '8': divstr += ("8");
      case '9': divstr += ("9");
                lcd.print(key);
                break;
    }
    key = kypd.getKey();
  }
  dgrs = (360 / divstr.toInt());
  divstr = "";
  return dgrs;
}

float getdegrees()
{
  char key = kypd.getKey();
  float dgrs = 0;
  String dgrstr;

  lcd.clear(); // initialize LCD
  lcd.setCursor(0, 1);
  lcd.print("Enter Degrees:");
  lcd.setCursor(0, 3);
  lcd.print("OK = # ");
  lcd.print((char)60);
  lcd.print((char)45);
  lcd.print(" D");
  lcd.setCursor(15, 1);

  while(key != '#')
  {
    switch (key)
    {
      case NO_KEY: break;
      case '.': dgrstr += (".");
                break;
      case 'D': lcd.setCursor(15, 1);
                lcd.print(" ");
                lcd.setCursor(15, 1);
                dgrstr = "";
                break;
      case '0': dgrstr += ("0");
      case '1': dgrstr += ("1");
      case '2': dgrstr += ("2");
      case '3': dgrstr += ("3");
      case '4': dgrstr += ("4");
      case '5': dgrstr += ("5");
      case '6': dgrstr += ("6");
      case '7': dgrstr += ("7");
      case '8': dgrstr += ("8");
      case '9': dgrstr += ("9");
                break;
    }
    key = kypd.getKey();
  }
  dgrs = dgrstr.toFloat();
  dgrstr = "";
  return dgrs;
}
long getsteps(float dgrs)
{
  return round(dgrs * shorty);
}
 
void printadvance()
{
  lcd.setCursor(6, 1);
  lcd.print("Moving");
  lcd.setCursor(4, 2);
  lcd.print("Steps  ");
  lcd.print(steps, 0);
  lcd.setCursor(13, 0);
  lcd.print(curpos, 2);
  index(steps, 0);
  lcd.setCursor(6, 1);
  lcd.print("      ");
}

void index(long stps, int dw)
{
  for(int i = 0; i < steps; i++)
  {
    digitalWrite(step, HIGH);
    delay(dwell);
    digitalWrite(step, LOW);
    delay(dwell);
  }
}

void software_Reset() // Initializes program without resetting the peripherals and registers.
{
  asm volatile ("  jmp 0");
}
```


----------



## Cogsy (Dec 30, 2019)

Your code looks good so far (although maybe a smidge under-commented for my old professors' liking). I know it doesn't make much of a difference unless you're making a lot of consecutive movements, but your rounding errors would eventually compound in the 'getsteps' function:
*long getsteps(float dgrs)
{
  return round(dgrs * shorty);
}*
It could certainly be argued that a missed step here and there was no big deal and also that missed steps would be counteracted by added steps, but if you were (for some reason) to rotate by a known amount, say 1 degree, and repeat for a revolution or two of the table, your error would eventually show up. One way to allow for this would be to accumulate the rounding error in another variable and adjust for the error as it became significant (i.e. add or deduct one step as your rounding figure approached +/- 1 step). Then you'd never be more than a single step (half a step maybe?) from theoretical even if you incremented by half a degree for a thousands of degrees.


----------



## rodw (Dec 31, 2019)

Personally, I think its smart working in seconds becasue that lets you work with integers.

I'm wondering if the fact you are using a servo with an encoder if you don't have an advantage around accuracy. Are you sending the position error back into your script? Your encoder accuracy may exceed your step accuracy. If each division was resolved to an absolute position around the circle, you should know exactly where you are before you start any move so it would be a matter of programming a move to the next position. If you can get the feedback signals working for this model, you will never need to to count pulses.


----------



## OldRon (Dec 31, 2019)

rodw said:


> Personally, I think its smart working in seconds becasue that lets you work with integers.
> 
> I'm wondering if the fact you are using a servo with an encoder if you don't have an advantage around accuracy. Are you sending the position error back into your script? Your encoder accuracy may exceed your step accuracy. If each division was resolved to an absolute position around the circle, you should know exactly where you are before you start any move so it would be a matter of programming a move to the next position. If you can get the feedback signals working for this model, you will never need to to count pulses.



rodw,

I will eventually I close the loop. Motor inertia will also have to be dealt with. Barring bad electronics unmanaged motor inertia is the #1 cause of lost or gained steps. Because I will be using a small servo motor so it will be necessary to use a 5:1 gear reduction between the the  motor and the table. The up side of that is increased torque and the down side is increased inertia. I had planned on using a 3:1 gear reduction which would have rotated the table 2.34375 seconds per motor pulse. By using 5:1 gear reduction it reduced the table resolution down to 1.40625 seconds. To put things in the proper perspective (1 / (3600 / 1.40625) = 0.000390625º. In Mastercam CAD/CAM configured to 8 places after the decimal point that  measures ZERO on the circumference of  a 6" diameter circle.

Ron


----------



## tjwal (Dec 31, 2019)

OldRon said:


> The code below is my revision of group member, bmac2's code that he published in Arduino Rotary Table for Dummies. To my knowledge there is nothing wrong with his code. Every co...snip... code in C++ so it was a ground]



Ron
I found 3 “errors???” in BMACs code.  From my memory..
1.  There had to be an integer number of steps for 1 degree.  For people using rotary tables with ratios 36:1 this wasn’t a concern.  IIRC that’s what BMAC has.  I think the same is true with other common RT ratios.  For those of us using belt driven spindles it just didn’t work.

2.  It accumulated errors. With each division there could be an error of up to 1 step.  With a reasonable number of divisions and steps per revolution it probably doesn’t matter.  Who cares if you’re out 20 steps in 18000?  It is an error though and was pretty easy to correct.

3. Its limited to 32k steps per revolution.  This is more of a limit than an error, but it’s pretty easy to bump into it.  I think your arrangement would hit it without even using microstepping.

John
Ps I still dislike minutes and seconds.


----------



## OldRon (Dec 31, 2019)

Cogsy said:


> Your code looks good so far (although maybe a smidge under-commented for my old professors' liking). I know it doesn't make much of a difference unless you're making a lot of consecutive movements, but your rounding errors would eventually compound in the 'getsteps' function:
> *long getsteps(float dgrs)
> {
> return round(dgrs * shorty);
> ...



Cogsy,

The code below is from my start in MS VS 2019. Imagine my surprise when I discovered that the Arduino IDE didn't speak that language. It was a minor set back that was easily recovered from. If you have a C++ debugger then run the attached code and I think that you will be impressed by the accuracy. Hard to believe that MS VS 2010 doesn't  have the round function. Ceil and Floor are the raw components for the round function.

Ron


```
/*
Given:
Degree * Minutes * Seconds = 3600 Seconds
Rotary Table gear ratio: 90:1
Motor ppr: 2048
Motor Gearhead ratio: 5:1
Logic:
One revolution @ 5:1 Gearhead final drive = 10240 Motor ppr (2048 * 5)
One revolution of Rotary Table hand crank = 4º * 3600 = 14400 Seconds
Therefore, 1 Motor ppr rotates the Rotary Table 1.40625 Seconds (14400 / 10240)
Formula: ((Degrees * Seconds) / Indexer Resolution) = pulses
Short Formula: Degress * 2560 = pulses
*/
#include <iostream>
#include <iomanip>
double crums = 0;
double steps = 0;
double degrees = 0;
double shorty = 2560; // Degree(Seconds) * Table resolution.
int main()
  for (degrees = .000;degrees < 360;degrees +=3.09375)
  {
    steps=int(degrees * shorty);
    if((steps-int(steps))>=0.5)
    {
      steps=ceil(steps);
    }
    else if((steps-int(steps))<0.5)
    {
      steps = floor(steps);
    }
    std::cout << " Degrees input: " << degrees << " - Degrees output: " << std::setprecision(9) << (steps / shorty) << " - Steps: " << steps;
    std::cout << "\n";
  }

  std::cin.get();
  return 0;
}
```


----------



## rodw (Dec 31, 2019)

OldRon said:


> rodw,
> 
> I will eventually I close the loop. Motor inertia will also have to be dealt with. Barring bad electronics unmanaged motor inertia is the #1 cause of lost or gained steps. Because I will be using a small servo motor so it will be necessary to use a 5:1 gear reduction between the the  motor and the table. The up side of that is increased torque and the down side is increased inertia. I had planned on using a 3:1 gear reduction which would have rotated the table 2.34375 seconds per motor pulse. By using 5:1 gear reduction it reduced the table resolution down to 1.40625 seconds. To put things in the proper perspective (1 / (3600 / 1.40625) = 0.000390625º. In Mastercam CAD/CAM configured to 8 places after the decimal point that  measures ZERO on the circumference of  a 6" diameter circle.
> 
> Ron



Ron, you have a servo motor where position is determined by encoder feedback back to the controller from the encoder. There is therefore no need to be concerned with lost steps once the servo drive is tuned. The fact that it can act as a step and direction motor is simply for convenience for you the machine integrator. If you tell it to move 100 steps, it will make sure you move 100 steps. If it gets behind, it will catch up. You should review the documentation for your servo drive, it might be able to be controlled in velocity mode or as an analog servo with a 0-10 volt voltage range determining velocity. Then you will then not have any steps to loose!

You have this opportunity to do this in a different (superior) way to everybody else if you get your head around this and your servo drive's capabilities.

Finally, it would be good if you learn about the Arduino timer interrupts before you get much further. If you use these to generate your step pulses, ramping up at the start of a move and ramping  down at the end of a move becomes quite trivial as all you need to do is change the interrupt interval. But you do need to catch the situation where the move is too short to ramp up and ramp down to get to you your target velocity . In this case, you must shorten the  ramp up so there is room for a shortened ramp down (eg. You never get to your target velocitty.)

There is already a very good example of how to do interrupts and ramping in an Arduino script I wrote here on this forum


----------



## OldRon (Dec 31, 2019)

rodw said:


> Ron, you have a servo motor where position is determined by encoder feedback back to the controller from the encoder. There is therefore no need to be concerned with lost steps once the servo drive is tuned. The fact that it can act as a step and direction motor is simply for convenience for you the machine integrator. If you tell it to move 100 steps, it will make sure you move 100 steps. If it gets behind, it will catch up. You should review the documentation for your servo drive, it might be able to be controlled in velocity mode or as an analog servo with a 0-10 volt voltage range determining velocity. Then you will then not have any steps to loose!
> 
> You have this opportunity to do this in a different (superior) way to everybody else if you get your head around this and your servo drive's capabilities.
> 
> ...



The drive is a positioning drive and therefore not capable of torque/velocity control. If you wouldn't mind sending me a link to your article then I would appreciate it

Ron


----------



## OldRon (Jan 1, 2020)

tjwal said:


> Ron
> I found 3 “errors???” in BMACs code.  From my memory..
> 1.  There had to be an integer number of steps for 1 degree.  For people using rotary tables with ratios 36:1 this wasn’t a concern.  IIRC that’s what BMAC has.  I think the same is true with other common RT ratios.  For those of us using belt driven spindles it just didn’t work.
> 
> ...



John,

I don't care to discuss bmac2's program. If he has an error then he is welcome to copy my code just as I copied his code. There are very few programmer that honestly say that they have never copied code.  It doesn't smell bad until a person claims another person's code to be their work

Once the program is finish then all input will be decimal values. As for accuracy, it is not possible to improve with givens that I have to work with. However, I'm quite pleased with the accuracy of my program, Underlined with red is the formula in reverse thatcalculates the degrees output by dividing the steps by shorty.

Ron


----------



## Cogsy (Jan 1, 2020)

OldRon said:


> Cogsy,
> 
> The code below is from my start in MS VS 2019. Imagine my surprise when I discovered that the Arduino IDE didn't speak that language. It was a minor set back that was easily recovered from. If you have a C++ debugger then run the attached code and I think that you will be impressed by the accuracy. Hard to believe that MS VS 2010 doesn't  have the round function. Ceil and Floor are the raw components for the round function.
> 
> Ron



Ron,

I ran the code and noticed a couple of things.

1. You've changed the 'shorty' to a round figure from your original so there's a slight accuracy loss there.
2. If I alter your loop increment value to something different (I used 3.11111 and 3.08) then indicated accuracy is slightly degraded, although I may have missed something about using that particular increment in your code.
3. Your rounding functions are redundant as you explicitly typecast 'steps' to an integer value with 'steps = int(degrees * shorty)'. So then your following calculations of  (steps-int(steps)) effectively become int(steps)-int(steps) which will always return zero.

Finally, what I meant with accumulated errors is not on an absolute move such as your code simulates, but on incremental motion. There's usually not going to be a perfect integer number of steps for a move so rounding must take place but if these 'partial' steps are not accounted for then they can add up. For example, a move requiring 256.4 steps gets rounded down to 256 steps and nothing will be affected, but once we add another required movement of 256.4 steps our total movement should be 256.4 + 256.4 = 512.8 => 513 total steps for best accuracy, except we've only moved 2 * 256 for 512 steps. Only one step difference but continued on for a large number of moves and the error eventually becomes noticeable.

Like I said, not likely a big deal in hobby environments with our requirements but in a production machine it could make a difference (plus I lost so many marks on coding assignments for seemingly minor details like these that I find it hard to go past them without comment).


----------



## OldRon (Jan 1, 2020)

Cogsy said:


> Ron,
> 
> I ran the code and noticed a couple of things.
> 
> ...



Cogsy,

The number of degrees in the debug code have no relevance. The degree of accuracy is limited by the givens. As for shorty being an integer value:

360º * 60 *60 = 1296000"
Servo motor ppr: 2048
Servo motor gearhead ratio 5:1
Servo motor ppr at final drive: 10240
Rotary table gear ratio: 90:1
Servo motor ppr per rotary table revolution: 10240 * 90 = 921600
1296000 / 921600 = 1.40625
1 º * 60 * 60 = 3600"
3600" / 1.40625 = 2560 (no conversion)

However, thank you for catching (steps =  int(steps)) prior to rounding. That was from a prior debugging session and rather than making rounding redundant it made rounding impossible. The error in the results of the code below is spot on up to the 3rd place behind the decimal point. Five thousandths (.005) of a degree measures .00026180" on the circumference of a 6" diameter circle. Rounding up from the 4th place creates a .001º degree error and that is 0.00005236" linear error measured on the circumference of a 6" diameter circle. I could live with 0" to 0.00005236" linear error if I had any way of measuring it. 

BTW, MS VS 2010 didn't have round and I didn't make the switch when I upgraded to MS VS 2019

Ron


```
/*
Given:
Degree * Minutes * Seconds = 3600 Seconds
Rotary Table gear ratio: 90:1
Motor ppr: 2048
Motor Gearhead ratio: 5:1
Logic:
One revolution @ 5:1 Gearhead final drive = 10240 Motor ppr (2048 * 5)
One revolution of Rotary Table hand crank = 4º * 3600 = 14400 Seconds
Therefore, 1 Motor ppr rotates the Rotary Table 1.40625 Seconds (14400 / 10240)
Formula: ((Degrees * Seconds) / Indexer Resolution) = pulses
Short Formula: Degress * 2560 = pulses
*/
#include <iostream>
#include <iomanip>
long steps = 0;
float degrees = 0;
float shorty = 2560; // Degree(Seconds) * Table resolution.
int main()
{
    for (degrees = 0.0;degrees < 360;degrees += 12.3456789)
    {
        steps = round(degrees * shorty);
        std::cout << " Degrees input: " << degrees << " - Degrees output: " << std::setprecision(8) << (steps / shorty);
        std::cout << "\n";
    }
    std::cin.get();
    return 0;
}
```


----------



## tjwal (Jan 1, 2020)

OldRon said:


> John,
> 
> I don't care to discuss bmac2's program. If he has an error then he is welcome to copy my code just as I copied his code. There are very few programmer that honestly say that they have never copied code.  It doesn't smell bad until a person claims another person's code to be their work
> 
> ...



Ron
My posting was to start a discussion on bmacs code but rather a heads up if you’re using logic similar to bmac’s there are a few things to watch for.  I’m actually using a modified version of his that works quite well.

cheers
John


----------



## Cogsy (Jan 1, 2020)

Not sure how I messed up the shorty math - initially I had a figure with a few decimal places (I'm guessing I fat-fingered it).

Absolutely your error is small and there's no need to address it in this application, but if you wanted to you could stop it compounding. I most likely would, but purely pedantically because that was what was drummed into us in coding classes (code re-usability is KING they tell us). 

It's interesting that you use visual studio and your code compiles fine. I run C++ in codeblocks and have to #include <cmath> to get your code to compile or it doesn't recognise the round(). I guess the .exe would be fine from either application but wasn't aware of different dependencies by application like that. Is this project destined to be Arduino powered? If it is, I'd consider making the switch to the Arduino environment if I was you - it's not the best IDE but it would save on reconfiguring code down the line as Arduino is not precisely C++.


----------



## awake (Jan 2, 2020)

Wellll ... since you mentioned being pedantic ... here goes:


> BTW, MS VS 2010 didn't have round and I didn't make the switch when I upgraded to MS VS 2019



The distinction between the _language_ and the _libraries_ has gotten fuzzy indeed, but still can be helpful. Strictly speaking, C++ itself does not "have" the round function, not as a built-in part of the language. Rather, the round() function is available in one of the standard libraries - specifically in cmath, as Cogsy has illustrated. Likely MS VS 2010 "had" the round function in one of its libraries - it's not new by any means - but if you didn't include the library header then you couldn't use it. Likely MS VS 2019 is including that library automatically, behind the scenes. Likely, the list of which libraries to include automatically is configurable in the IDE.

Strictly speaking, Arduino _is_ precisely C++, and in fact it is compiled with the standard gnu C++ compiler. Most of what people think of as "Arduino" is simply the libraries that the Arduino IDE automatically includes. So, for example, pinMode() and digitalWrite() and so on are simply library functions, not actually part of the C++ language.

To be sure, depending on the processor chip involved, some of the larger data types may not be available (e.g., double float or long long int). But even that is "standard" C++, since one of the more infamous "features" of C/C++ has always been that the definition of data types is dependent on the hardware. Thus, an int on one hardware platform may be 32 bits, while on another it may be 16 bits. This is why newer specifications for C/C++ include a library header called inttypes.h which defines specifically sized data types such as uint8 or int64 or so on - and yes, you can use these in the Arduino IDE, so long as you include the header; just note that for the 8-bit AVR processors, 64 bit integers will not be defined.

One other thing that the Arduino IDE automatically includes: Most people think that setup() and loop() are the two required Arduino functions. In fact, in keeping with standard C/C++, the only required function is main(). Behind the scenes, the IDE includes a _default_ main() function that does three things: it sets up some of the hardware (e.g., timers); it calls setup(); and then it enters an endless loop calling loop(). Thus, in the Arduino IDE, you can write your program only using setup() and loop() in your Arduino code, but in reality, even here, it is standard C++ at work behind the scenes.

Actually, you can supply your own main() function; if you do, the IDE will not include the default. If you supply your own main() function, you will need to do any hardware initialization and other setup that you require, and you will need to create whatever appropriate loop(s) you need. Most people don't need to do this, but if you are needing to set up the hardware in a very specific way, rather than according to the defaults, or if your logic is more complex than a single loop, then it makes sense to bypass the default.

Okay, pedantic mode off ...


----------



## Cogsy (Jan 3, 2020)

awake said:


> Okay, pedantic mode off ...



Some really interesting info in there - thanks! (Oh how I wish I could turn my pedantic mode off sometimes )


----------



## OldRon (Jan 4, 2020)

awake said:


> Wellll ... since you mentioned being pedantic ... here goes:
> 
> 
> The distinction between the _language_ and the _libraries_ has gotten fuzzy indeed, but still can be helpful. Strictly speaking, C++ itself does not "have" the round function, not as a built-in part of the language. Rather, the round() function is available in one of the standard libraries - specifically in cmath, as Cogsy has illustrated. Likely MS VS 2010 "had" the round function in one of its libraries - it's not new by any means - but if you didn't include the library header then you couldn't use it. Likely MS VS 2019 is including that library automatically, behind the scenes. Likely, the list of which libraries to include automatically is configurable in the IDE.
> ...



MS VS 2019 has the round function. In the code below I'm comparing the Degrees input with the Degrees output, dividing the difference by 360, and multiplying the circumference of a 6" diameter circle by that decimal fraction. I think that it would acceptable in Rocket Science. See attached picture.


```
/*
Given:

Degree * Minutes * Seconds = 3600 Seconds
Rotary Table gear ratio: 90:1
Motor ppr: 2048
Motor Gearhead ratio: 5:1

Logic:

One revolution @ 5:1 Gearhead final drive = 10240 Motor ppr (2048 * 5)
One revolution of Rotary Table hand crank = 4º * 3600 = 14400 Seconds
Therefore, 1 Motor ppr rotates the Rotary Table 1.40625 Seconds (14400 / 10240)
Formula: ((Degrees * Seconds) / Indexer Resolution) = pulses
Short Formula: Degress * 2560 = pulses
*/

#include <iostream>
#include <iomanip>

float crums = 0;
float steps = 0;
float degrees = 0;
float shorty = 2560; // (Degree(Seconds) / Table resolution).

int main()
{
    for (degrees = 0.0;degrees < 360;degrees += 23.456789) // index increment chosen to force fractional output
    {
        steps = ((degrees * shorty) + crums); // crums can equal a Cookie
        crums = (steps - round(steps));
        steps = round(steps);
        std::cout << std::fixed;
        std::cout << " Degrees input: " << degrees << " - Degrees output: " << std::setprecision(5) << (int(steps) / shorty) <<
        " - LinearError on Circumference of 6 inch Diameter Circle: " << std::setprecision(5) <<
        ((6 * 3.14159265) * ((degrees - (steps / shorty)) / 360));
        std::cout << "\n";
    }
    std::cin.get();
    return 0;
}
```


----------



## Cogsy (Jan 5, 2020)

Again, this level of error handling is not required for this application but you've almost got it nailed. Your sample code is based on absolute, rather than iterative movements though, so just for interest I reworked it to be iterative. I didn't use the crums value as explained after the code.


```
/*
Given:

Degree * Minutes * Seconds = 3600 Seconds
Rotary Table gear ratio: 90:1
Motor ppr: 2048
Motor Gearhead ratio: 5:1

Logic:

One revolution @ 5:1 Gearhead final drive = 10240 Motor ppr (2048 * 5)
One revolution of Rotary Table hand crank = 4º * 3600 = 14400 Seconds
Therefore, 1 Motor ppr rotates the Rotary Table 1.40625 Seconds (14400 / 10240)
Formula: ((Degrees * Seconds) / Indexer Resolution) = pulses
Short Formula: Degress * 2560 = pulses
*/

#include <iostream>
#include <iomanip>
#include <cmath>

float crums = 0;
float steps = 0; //steps per movement
float absSteps = 0; //steps needed for absolute movement
float actualSteps = 0; //cumulative steps taken
float degrees = 0;
float moveDegrees = 23.456789;
float totalDegrees = 0;
float shorty = 2560; // (Degree(Seconds) / Table resolution).

int main()
{
    for (degrees = 0.0;degrees < 360;degrees += moveDegrees) // index increment chosen to force fractional output
    {
        if (degrees > 1) //initial null movement handler
        {
            steps = ((moveDegrees * shorty));
            //crums = (steps - round(steps)); error handling disabled
            absSteps = round(degrees*shorty);
            steps = round(steps);
            actualSteps += steps;
            std::cout << std::fixed;
            std::cout << " Degrees input: " << degrees << " - Degrees output: " << std::setprecision(5) << (int(actualSteps) / shorty) <<
            " - LinearError on Circumference of 6 inch Diameter Circle: " << std::setprecision(5) <<
            ((6 * 3.14159265) * ((degrees - (actualSteps / shorty)) / 360));
            std::cout << "\n" << "Step difference between absolute value and incremental: " << (absSteps - actualSteps) << "\n";
        }
    }
    std::cin.get();
    return 0;
}
```

Output example :






You have a slight logic error where currently, as each move you make in your code is calculated as an absolute, apart from your first move when crums = 0, crums is actually hurting your accuracy (you work out the absolute closest number of steps to achieve your desired movement, then add the error from the previous 'exact' calculation and add that, which may make your rounded actual steps one higher, or lower, than the closest number). To fix this, I incremented your crums value to accumulate the error at each iteration and used it to adjust the steps value as it varied away from theoretical.


```
/*
Given:

Degree * Minutes * Seconds = 3600 Seconds
Rotary Table gear ratio: 90:1
Motor ppr: 2048
Motor Gearhead ratio: 5:1

Logic:

One revolution @ 5:1 Gearhead final drive = 10240 Motor ppr (2048 * 5)
One revolution of Rotary Table hand crank = 4º * 3600 = 14400 Seconds
Therefore, 1 Motor ppr rotates the Rotary Table 1.40625 Seconds (14400 / 10240)
Formula: ((Degrees * Seconds) / Indexer Resolution) = pulses
Short Formula: Degress * 2560 = pulses
*/

#include <iostream>
#include <iomanip>
#include <cmath>

float crums = 0;
float steps = 0; //steps per movement
float absSteps = 0; //steps needed for absolute movement
float actualSteps = 0; //cumulative steps taken
float degrees = 0;
float moveDegrees = 23.456789;
float totalDegrees = 0;
float shorty = 2560; // (Degree(Seconds) / Table resolution).

int main()
{
    for (degrees = 0.0;degrees < 360;degrees += moveDegrees) // index increment chosen to force fractional output
    {
        if (degrees > 1) //initial null movement handler
        {
            steps = ((moveDegrees * shorty));
            crums += (steps - round(steps)); //accrue error
            if(crums <= -0.5) //adjust for total cumulative error as necessary
            {
                steps -= 1;
                crums += 1;
            }
            else if(crums >= 0.5)
            {
                steps += 1;
                crums -= 1;
            }
            absSteps = round(degrees*shorty);
            steps = round(steps);
            actualSteps += steps;
            std::cout << std::fixed;
            std::cout << " Degrees input: " << degrees << " - Degrees output: " << std::setprecision(5) << (int(actualSteps) / shorty) <<
            " - LinearError on Circumference of 6 inch Diameter Circle: " << std::setprecision(5) <<
            ((6 * 3.14159265) * ((degrees - (actualSteps / shorty)) / 360));
            std::cout << "\n" << "Step difference between absolute value and incremental: " << (absSteps - actualSteps) << "  Crums value: " << crums << "\n";
        }
    }
    std::cin.get();
    return 0;
}
```

Output example :






That's the sort of approach I would take but being that you're working in seconds it really won't make much difference if you don't handle the error at all.

P.S. I know the "Rocket Science" was tongue-in-cheek but I had to include, one second of arc across the radius of the earth is equivalent to about 30 metres linearly. One second of arc would then make a massive difference heading off to a distant planet.

Edit to add: The unused variable 'totalDegrees' I declared intending to use it in error checking but using actualSteps/shorty was simpler. It bugs me I didn't remove it though (and I hope I didn't make too many more silly errors).


----------



## OldRon (Jan 5, 2020)

awake said:


> Wellll ... since you mentioned being pedantic ... here goes:
> 
> 
> The distinction between the _language_ and the _libraries_ has gotten fuzzy indeed, but still can be helpful. Strictly speaking, C++ itself does not "have" the round function, not as a built-in part of the language. Rather, the round() function is available in one of the standard libraries - specifically in cmath, as Cogsy has illustrated. Likely MS VS 2010 "had" the round function in one of its libraries - it's not new by any means - but if you didn't include the library header then you couldn't use it. Likely MS VS 2019 is including that library automatically, behind the scenes. Likely, the list of which libraries to include automatically is configurable in the IDE.
> ...



I tried moving MS VS 2010 C++ library files into the Arduino libraries folder taking care not to trample on the native library files and the Arduino IDE would have nothing to do with that. I wrote a recursive folder and file search program and with a little tweaking I was able to compile a list of the #included, #included, #included...... libraries. I had just learned enough MS VS 2010 C++ to write some simple prototypes  and I didn't like what turned out to be a minor difference between that and the Arduino language. After failing at building my own library I settled down and adapted to the difference.


----------



## OldRon (Jan 5, 2020)

When indexing with fractional increments none of the steps are an absolute value until they have been rounded. However, where each step represents 1.40625 seconds then the maximum linear error on the circumference of a 6" diameter circle would be .0000204531". That is derived from ((6 * π)  / 921600). The servo motor steps per rotary table revolution is  (2048 * 5 * 90) = 921600. The notion that leaving the error unmanaged improves the degree of precision is incomprehensible.

BTW,  You have been misinformed on cmath. MS VS 2019 is the first version to include round and that is what I'm coding in. None of my code has cmath included yet it complied without errors. To make sure that cmath wasn't being called from another library I renamed cmath to cmathX and once again my code compiled without error.


----------



## Cogsy (Jan 5, 2020)

It won't compile for me in Codeblocks without including cmath. Just throws an exception for an undefined function.


----------



## awake (Jan 6, 2020)

Microsoft is infamous for going "off reservation" with the way they implement numerous things, including languages. There is a standard that everyone else adheres to, and then there is Microsoft.

Which is to say, it is entirely possible that MS has put the round function in another library than called for in the C++ standard. But be assured it is in some library somewhere, because any and all functions are by definition part of the libraries, not part of the language itself.

Again, even without Microsoft's help, this distinction has gotten increasingly fuzzy, as C and C++ have added "standard libraries" to their definitions, as suggested above ... so in that sense one could argue that the libraries are in fact part of the language. From a compiler perspective, however, the distinction remains quite clear. The compiler knows exactly what + or - or struct or for or while or so on mean; it doesn't have to look anywhere else to turn that into machine code. But the compiler has no idea how perform round() or printf() or max() or so on. It knows only that they are functions, so it looks for them in the included libraries and hands off the task.


----------



## OldRon (Jan 6, 2020)

awake said:


> Microsoft is infamous for going "off reservation" with the way they implement numerous things, including languages. There is a standard that everyone else adheres to, and then there is Microsoft.
> 
> Which is to say, it is entirely possible that MS has put the round function in another library than called for in the C++ standard. But be assured it is in some library somewhere, because any and all functions are by definition part of the libraries, not part of the language itself.
> 
> Again, even without Microsoft's help, this distinction has gotten increasingly fuzzy, as C and C++ have added "standard libraries" to their definitions, as suggested above ... so in that sense one could argue that the libraries are in fact part of the language. From a compiler perspective, however, the distinction remains quite clear. The compiler knows exactly what + or - or struct or for or while or so on mean; it doesn't have to look anywhere else to turn that into machine code. But the compiler has no idea how perform round() or printf() or max() or so on. It knows only that they are functions, so it looks for them in the included libraries and hands off the task.



There's no disputing that the round function resides in cmath. However, when I can rename cmath to cmathX and successfully compile code that calls for the round function then that tells me that cmath is not the sole source for the round function. By renaming the library I eliminated the possibility of it being included by another library. That stated, if a function is not within the scope of a library then the library will not seek out other libraries. Else there would be no need to include more than 1 library.

The accuracy of my code should play very well with a rotary table that is certified to be accurate within 40 seconds.  That seems to be the better end of the spectrum in machining rotary table accuracy.


----------



## awake (Jan 6, 2020)

By the way, which file did you rename - the cmath.h file? If so, that doesn't actually prevent the possibility that another library is calling the cmath library - if the other library has already been compiled, it will not need to refer to cmath.h. Nonetheless, it does indicate that the round function is defined somewhere else in MS's header files.


----------



## OldRon (Jan 6, 2020)

awake,

There is no cmath.h in VS 2019. As I explained if the function is not within the scope of a library then the library will not search other libraries for the function. If a function in a library calls for a function in another library then that library will be included in the library that is calling the function. Should the required library be cmath then the compiler will throw an error if the library is not available.  I went a step beyond renaming the library and removed it from the folder. I was going to add round to math.h in VS 2010 Express but it made more sense to download the free VS 2019.

Assuming that you are using VS 2019 then locate the library file yvals_core.h and search for // P0092R1 <chrono> floor(), ceil(), round(), abs()
Once that you have found that string then scroll up to the top of that column and you will find // implemented unconditionally:
That library is used by the Compiler.

If you would like I will send you a copy of my app, My File Mole and it will read the files for you and log each occurrence of the search string in a list box. When you want to see the search in the file context then you simply click on the list box and it will load the file and highlight the search string. It's amazing what can be Learned at the Library. If you have a VB6 compiler available then I can send you the code.


----------



## awake (Jan 6, 2020)

Well, being pedantic again ... except in the case of a template library, the .h file is not the library; it is merely the header for the library. The actual library will consist either of statically linked object files, or of one or more .dll files.

Thank you for the offer, but for many years my OS of choice has been Linux, and grep already does that job quite well. Plus, having developed software since 1981, including quite a lot of embedded systems programming, I have a reasonably thorough understanding of the files on my system. 

What I'm not personally familiar with is the various MS VS versions; my compiler of choice has been gcc and its stablemates. With that in mind, I'll bow out of this thread.


----------



## rodw (Jan 11, 2020)

Its totally unreasonable to expect a small embedded CPU like the Atmel chips used by the Arduino to  support the same features of the MS compiler which is written for CPU's with exponentially more processing power. Also you are failing to understand that floating point arithmetic is very expensive in terms of CPU processing. 

Smart programmers avoid  using floating point maths on the arduino. In fact the early incarnations of the Arduino library, it was something that needed an additional library to support. From memory, the floating point library  consumed 4K of RAM which is huge in terms of the limited resources the Arduin Uno has at its disposal. This is why I say it is smart to work in seconds of a degree becasue that becomes the smallest divisions and then you can use integer maths and avoid the overhead of floating point. The other issue is that floats can cause errors because of the conversion of  hexadecimal numbers (base 16) back to decimal (base 10) numbers. That is why some environments designed to support business applications introduce a new decimal data type which is a BCD (Binary coded decimal) that counts in base 10 maths. eg. This is a valid data type in SQL ( database which often stores business numbers). This error is less of an issue now when we have the processing power to deal with 64 bit numbers.

Back in the 70's when I first played with programming we quickly became acutely aware of the risks of using floating point and we would avoid it if at all possible. Back then, it was a big step to  move from 8 bit to 16 bit processing. So, we used to have to do our own rounding. This is pretty easy to do. Lets assume a variable pi = 3.1417 and you want to round it to 3 decimals. So multiply it by 1000  ( pi * 1000 = 3141.7) then add 0,5 to it (3141.7 + 0.5 = 3142.2)  so then take the int of that (3142). So now by divide by 1000, you end up with pi = 3.142. But the problem is that the use of hexadecimal maths might add errors back into this result so we would  use string manipulation to ensure the number was formatted correctly when it was displayed. So this algorithim will correctly round up  numbers  where the decimal  fraction is >= 0.5

So now you know how to write your own round function.


----------



## awake (Jan 11, 2020)

Rod, the problem the OP has is that his setup does not permit him to work in whole numbers of seconds. One step on the stepper motor does not translate into a whole number of seconds, so one way or another he is going to have to deal with fractions.

I will confess I have not looked through the code in any detail, but in general the "cost" of floating point is not so high that you can't or shouldn't use it in the interface part of the code, e.g., to calculate the resulting number of steps to move. Definitely shouldn't use FP inside a tight loop, of course, or worse yet in an interrupt.

It is possible, of course, to do some calculations using integer multiplication and division (the latter still not "cheap" on the 8-bit AVR, but still cheaper than FP), essentially working in fractions rather than in decimal. You divide the denominator into the numerator to get the steps, but keep track of the remainder in order to round as needed (and to avoid build up of error).

One minor point - I think you meant EEPROM rather than RAM when you were talking about the memory requirements for FP libraries. The Uno only has 2K of RAM, so if FP required 4K of RAM, it would definitely not work. 

Oh, wait - I said above I was bowing out of this thread. Rats - hard for me not to jump into a conversation about embedded systems programming! Especially since, some 35 years ago, I had occasion to write a floating point library for an 8-bit system in assembler ...

Okay, this time I really am bowing out. Really. For sure ...


----------



## OldRon (Jan 11, 2020)

rodw said:


> Its totally unreasonable to expect a small embedded CPU like the Atmel chips used by the Arduino to  support the same features of the MS compiler which is written for CPU's with exponentially more processing power. Also you are failing to understand that floating point arithmetic is very expensive in terms of CPU processing.
> 
> Smart programmers avoid  using floating point maths on the arduino. In fact the early incarnations of the Arduino library, it was something that needed an additional library to support. From memory, the floating point library  consumed 4K of RAM which is huge in terms of the limited resources the Arduin Uno has at its disposal. This is why I say it is smart to work in seconds of a degree becasue that becomes the smallest divisions and then you can use integer maths and avoid the overhead of floating point. The other issue is that floats can cause errors because of the conversion of  hexadecimal numbers (base 16) back to decimal (base 10) numbers. That is why some environments designed to support business applications introduce a new decimal data type which is a BCD (Binary coded decimal) that counts in base 10 maths. eg. This is a valid data type in SQL ( database which often stores business numbers). This error is less of an issue now when we have the processing power to deal with 64 bit numbers.
> 
> ...


----------



## OldRon (Jan 11, 2020)

(IF) floating point numbers present a problem then all that I have to do is ditch the native Geometric unit of measure, seconds and substitute the servo motor ppr at the final drive which is 921600  ((2048 * 5) * 90). Divide the circumference of a 6" diameter circle by 921600 and that equals .00002045307717180855" linear error per user unit. At some point the processor is going to have to perform division or I guess that I could calculate the number of steps for the index and enter whole numbers. If there is a flaw in my logic then it will be the degree of precision returned from String.toFloat(). In Visual Basic 6.0 the degree of precision on type conversions is 4 places. In reality that amount of error far exceeds the rotary table's degree of precision. If the Arduino doesn't meet my expectations then I can always revert back to plan A which was to use a Galil Motion Control DMC-1830 DSP motion control board and base the rotary table control in the PC that hosts the motion control board for my CNC milling machine. I chose the Arduino approach because I did not want to make the rotary table integral to the machine. This project is too simple for your superior intelligence.


----------



## OldRon (Jan 12, 2020)

The result from the calculation is returned to the display before my finger clears the key. That's fast enough for me.


----------



## awake (Jan 12, 2020)

OldRon said:


> This project is too simple for your superior intelligence.



Ron, was this comment directed at my posts? I probably went into more depth than I should have, but I thought my last post was supporting you ... ??



OldRon said:


> The result from the calculation is returned to the display before my finger clears the key. That's fast enough for me.



Yep. That's the point, always - there are always ways to tweak the code just a bit more to wring just a bit more speed out of it, but if the code is fast enough, why bother?

For this project, ignorance is bliss and you don't need the experts. Just remember that, while it may be fast enough this time, you may attempt another project where the code can't keep up, and you find yourself wondering why your code is not working consistently (or at all). At that point, give us a call ...


----------



## OldRon (Jan 13, 2020)

The three of you continuously cited problems that did not exist. For a reason that I will never understand Cogsy had a problem with me using native units of Geometrical measurement.  Assigning an arbitrary number of units to the table per revolution does not and cannot change the givens. I could have assigned the motor ppr at the final drive to the table per revolution and the resolution would have been the same. Sure in a perfect world the motor ppr at the final drive would have been equal to the seconds in 360 degrees but that is not what I have to work with.  C++ is new to me but programming is not new to me. I have written several full feature CNC machine tool control operating systems in Visual Basic 6.0 for Galil Motion Control DMC1840 motion control boards.  Half way through the first program I realized hat I chose the wrong language but I had too much time invested to start over and I never turned back. When and if my knowledge stands between me and success then I'm still quite capable of learning. The critique of my code  was not solicited nor was I asking for help.

BTW, What part of the IEEE 754 standard for 32 bit floating point arithmetic is not implemented on an AVR Arduino?

Ron


----------



## awake (Jan 13, 2020)

Ron, that makes sense; I can see how that was frustrating, and I apologize.

I can't speak for the others, but my excuse is that I've written a good many embedded systems programs that required squeezing every last drop out of the processor, often to the point of counting cycles in the machine code to be sure an interrupt will be able to complete in time. It is easy to forget that sometimes, maybe lots of times, you just don't need to work that hard.



OldRon said:


> BTW, What part of the IEEE 754 standard for 32 bit floating point arithmetic is not implemented on an AVR Arduino?



Umm ... some, none, or all, depending on what exactly you are asking. The AVR microprocessor has no built-in floating point hardware, so any floating point operations have to be implemented by software. There's enough EEPROM to implement any or all of IEEE 754, if one chooses to do so -- or more likely for most of us, if the compiler one uses does so. If you are compiling using MS Visual Studio, the question is whether the Microsoft compiler fully implements IEEE 754 (likely). The Arduino IDE uses the gnu compiler, which in general fully implements IEEE 754 ... but which also has the option to relax some requirements: https://gcc.gnu.org/wiki/FloatingPointMath. Off hand, I don't know how, but I suppose it is possible to tell the IDE to set any of those options when it passes the code to the compiler ... but again, do you really need to work that hard? If it ain't broke ...


----------



## OldRon (Jan 13, 2020)

awake said:


> Ron, that makes sense; I can see how that was frustrating, and I apologize.
> 
> I can't speak for the others, but my excuse is that I've written a good many embedded systems programs that required squeezing every last drop out of the processor, often to the point of counting cycles in the machine code to be sure an interrupt will be able to complete in time. It is easy to forget that sometimes, maybe lots of times, you just don't need to work that hard.
> 
> ...



The answer to the closing question is NONE. The Arduino UNO rev3's only limitation at this time is that while it will accept double precision variables the level of precision remains single precision. I used to chuckle every time that I saw something about the Arduino and now here I am playing with one of them.

Ron


----------



## OldRon (Jan 14, 2020)

awake said:


> Ron, that makes sense; I can see how that was frustrating, and I apologize.
> 
> I can't speak for the others, but my excuse is that I've written a good many embedded systems programs that required squeezing every last drop out of the processor, often to the point of counting cycles in the machine code to be sure an interrupt will be able to complete in time. It is easy to forget that sometimes, maybe lots of times, you just don't need to work that hard.
> 
> ...



awake,

As they say, "The Proof is In the Pudding". That is 360º / 34 divisions. That is exactly what my debug loop in MS VS 2019 ended up with..... but it wouldn't do that a couple of hours ago. I declared the function getSteps() as long because steps ends up as a long value. However by using long I cast the floats to long and shut of my accumulate error handling routine along with inducing error. That result is derived by feeding the steps output back into the equation in reverse so it is the true position. I would not have found that error had I not 'read the display library'. It was in the library where I learned how to setprecision() without the standard namespace. Default is 2 places after the decimal point.


----------



## OldRon (Feb 15, 2020)

Progress is slow due to work load. The shaft on the Vertex HV6 will not carry a sprocket so I made a tool steel sleeve to put over the rotary table spigot (for lack of a better word). A heavy duty 20mm wide needle bearing in the sprocket will run on the sleeve. The outer sprocket flange will be made from steel and it will drive the rotary table shaft. That arrangement will also shield the rotary table shaft from the belt tension. My code is attached. If you don't have an Arduino to run the sketch then do not comment on my logic. If you have an Arduino and you find an error that I have overlooked then  I want to know about it so that I can correct it.

The 'D' key is dynamic in that it serves dual purposes within a screen. Jogging is performed in the Jogging screen. Select the increment and jogging begins. While jogging is active the 'D' key serves as an E-Stop. when jogging ceases then the 'D' resumes the role of the Exit button. The '#' key toggles CW/CCW. The '0' key returns the table to Home. It will eventually Home the rotary table. When time permits I will update the positioning screen so that it is like the jogging screen. Keypad stutter is common so while entering divisions or degrees the 'D' key becomes the Backspace key. Once that you have completely cleared the input then the 'D' key resumes the role of the Exit key.

If there is interest in how to create the involute  curve for the HTD sprocket tooth profile then I will present a tutorial.


----------



## Cogsy (Feb 15, 2020)

OldRon said:


> The three of you continuously cited problems that did not exist. For a reason that I will never understand Cogsy had a problem with me using native units of Geometrical measurement.



Ron, just caught up on this thread and noticed this (and other) comments. I don't have a problem with using seconds but if I gave you that impression I apologise. I also didn't realise I was being such a pain and that you were such an expert. Again I apologise for offering advice and needlessly pointing out your logic errors in prior code. From what you've now said, contrary to your posts on the first page of this thread, you did not want, nor need any feedback or assistance and so I'll bow out of this thread and leave you to it.


----------



## pyromancer (Oct 24, 2020)

motorSteps = fabs (gear_ratio_top_array[gearratioindex]*Microsteps*StepsPerRevolution)/gear_ratio_bottom_array[gearratioindex]; 

......

 stepsperdiv = (motorSteps / num_divisions);                          
 stepsperdivv = round (stepsperdiv * (cur_pos-1) ) ;                
 stepsperdiv = round (stepsperdiv * cur_pos ) ;                    
 stepsperdiv_d = stepsperdiv - stepsperdivv + 1  ;



I'm using  "fabs()" function..
I solved the floating point error by remembering the previous moved step when calculating, calculating the step to move this time, and re-inserting the amount of the difference.


----------



## OldRon (Oct 25, 2020)

pyromancer said:


> motorSteps = fabs (gear_ratio_top_array[gearratioindex]*Microsteps*StepsPerRevolution)/gear_ratio_bottom_array[gearratioindex];
> 
> ......
> 
> ...




pyromancer,

I did not have any problem with the Arduino UNO other than it being limited to single precision. In the calculations some index increments return a whole number while others return a fractional value. Obviously it is not possible to send a fraction of a pulse so I rounded the fractional value. That is where the accumulated error went wild. While the Arduino UNO will accept the double type the precision remains to be single. I have since switched to the Arduino DUE and it has true double precision. I mention that to ward off lectures about the use of double in my code. With true double precision the positioning (code) accuracy is as good as high end rotary indexing tables. 

I believe that we are using the same logic to manage the accumulated error.

Servo motor encoder ppr: 2048
External gear ratio: 4:1
Internal gear ratio: 90:1
Servo motor encoder ppr per revolution of rotary table axis: 737280 ((4 * 90) * 2048)
Arc(seconds) per servo motor encoder ppr: 1.7578125 (1296000 / 737280) < the resolution
Arc(seconds) per degree: 3600

user =  2048 (3600/ 1.7578125)

Arduino C++

```
double getSteps(double dgrs)
{
  double stps = 0.0;
  stps = ((dgrs * user) + crumbs);
  crumbs = (stps - round(stps));
  return round(stps);
}
```


----------

