Division returning zero [solved]

I'm no programmer, and (sometimes) know just enough to get myself by. Currently I'm struggling with a division operator that leaves the resultant dutyCycle = 0
I have tried many different variable types, casting, position of function, but nothing helps. Maybe one of you actual programmers could spot my error?? In function sum(), dutyCycle always stays at zero. tempPulseWidth and tempPhaseLength both range from 0-100

My code reads in a PWM signal and simply reports a duty-cycle. This will be used to create its own PWM signal to power a hobby ESC, turning a motor, which is the drive motor for my spindle on a small home-made CNC mill.

Hopefully all questions starting with "Why did you.... " are already answered by my first sentence.

  // LCD feedback
  #include <LiquidCrystal.h>
  LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
  
  // ESC
  #include <Servo.h> 
  Servo spindle;
  
  
  // Variables
  byte i                       = 0;
  byte j                       = 0;
  byte watchDog                = 0;
  unsigned int spindleLo       = 95;
  unsigned int spindleHi       = 165;
  unsigned int spindleSpeed    = 95;
  unsigned int dutyCycle       = 0;
  unsigned int timePhaseLength[10] = {};
  unsigned int timePulseWidth[10]  = {};
  unsigned long timeRisingOld  = 0;
  unsigned long timeRisingNew  = 0;
  unsigned long timeFalling    = 0;
  
  
  
  
  
  void setup() {
    //
    Serial.begin(115200);
    //
    // Begin LCD
    lcd.begin(16, 2);
    lcd.setCursor(0, 0);
    lcd.print("**CNC  SPINDLE**");
    //
    // Begin ESC
    lcd.setCursor(0, 1);
    lcd.print("init output...      ");
    spindle.attach(6);
    spindle.write(spindleSpeed);
    delay(1000);
    lcd.setCursor(0, 1);
    lcd.print("cal. output...      ");
      for (int i = 0; i<=180; i++) {
        spindle.write(i);
        delay(75);
      }
    spindleSpeed=spindleLo;
    spindle.write(spindleLo);
    //
    // Interrupt Config
    lcd.setCursor(0, 1);
    lcd.print("pcint config... ");
    pinMode(14,INPUT);
    PCICR = _BV(PCIE1);
    PCMSK1 = _BV(PCINT8);
    delay(500);
    //
    // Done
    lcd.clear();
    lcd.print("    Complete    ");
    delay(1000);
    lcd.clear();
    lcd.print("Spindle Pwr:");
  }
  
  
  
  
  
  
  void loop() {
    if (watchDog) {
      watchDog=0;
    } else {
      timePhaseLength[i] = 0; i++; if (i>=10) i=0; // Run numbers back down to 0 if no PWM detected
      timePulseWidth[j]  = 0; j++; if (j>=10) j=0; //
    }
    
    delay(100);
    sum();
//    spindleSpeed = map(dutyCycle,0,100,spindleLo,spindleHi);
//    lcd.setCursor(12, 0);
//    lcd.print(dutyCycle);
//    lcd.print("%  ");
//    spindle.write(spindleSpeed);
  }
  
  
  
  
  
  ISR(PCINT1_vect) {
    watchDog=1;
    if (digitalRead(14)) {
      // IF RISING EDGE
      timeRisingOld = timeRisingNew;
      timeRisingNew = millis();
      timePhaseLength[i] = constrain((timeRisingNew-timeRisingOld),0,500);
      i++;
      if (i>=10) i=0;
    } else {
      timeFalling = millis();
      timePulseWidth[j] = constrain((timeFalling-timeRisingNew),0,500);
      j++;
      if (j>=10) j=0;
    }
  }
  
  
  
  
  void sum() {
    unsigned int tempPhaseLength = 0;
    unsigned int tempPulseWidth = 0;
    for (int x=0; x<10; x++) tempPhaseLength += timePhaseLength[x];
    for (int x=0; x<10; x++) tempPulseWidth += timePulseWidth[x];
    tempPulseWidth = (tempPulseWidth / 10);
    tempPhaseLength = (tempPhaseLength / 10);
    if (tempPhaseLength == 0) tempPhaseLength = 1;
    dutyCycle = tempPulseWidth/tempPhaseLength;
    //  
    Serial.println(dutyCycle);
    Serial.println(tempPulseWidth);
    Serial.println(tempPhaseLength);
    
  }
    dutyCycle = tempPulseWidth/tempPhaseLength;

What are the values in tempPulseWidth and tempPhaseLength? You are performing integer arithmetic, so if tempPhaseLength is greater than tempPulseWidth, the result will be 0.

    tempPulseWidth = (tempPulseWidth / 10);
    tempPhaseLength = (tempPhaseLength / 10);

Why? If it is the ratio that is important, why are you dividing each value by 10, introducing a lot of round-off error?

    unsigned int tempPhaseLength = 0;
    unsigned int tempPulseWidth = 0;
    for (int x=0; x<10; x++) tempPhaseLength += timePhaseLength[x];
    for (int x=0; x<10; x++) tempPulseWidth += timePulseWidth[x];

Are you certain that adding 10 values does not overflow an int?

while you are correct on your first point, changing dutyCycle to a float still results in 0.00

Regarding your questions:
tempPulseWidth and tempPhaseLength are used to get a rolling average of the pulseWidth and phaseLength. for each width and length, I add the 10 most recent values, and then divide by ten. Because the largest value going in is constrained to 500, 500*10=5k, and small enough to fit into an unsigned int. In any case I changed all those variables back to unsigned longs.

while you are correct on your first point, changing dutyCycle to a float still results in 0.00

Because you're still doing integer arithmetic?
You need to change the operands to float too.

awjennin:
I'm no programmer, and (sometimes) know just enough to get myself by. Currently I'm struggling with a division operator that leaves the resultant dutyCycle = 0
I have tried many different variable types, casting, position of function, but nothing helps. Maybe one of you actual programmers could spot my error?? In function sum(), dutyCycle always stays at zero. tempPulseWidth and tempPhaseLength both range from 0-100

Because there's quite a lot going on in that sketch it's not obvious what is going on.

Could you provide a test sketch that demonstrates the problem in the simplest way possible? For example you could just use hard-coded constants for the input values, perform your calculation and print out the resulting value - tell us what value you were expecting, and what the actual output was.

while you are correct on your first point, changing dutyCycle to a float still results in 0.00

To add to what AWOL said, the type of variable on the left of the equals sign has nothing to do with how the value is constructed from the data on the right side. Both values on the right, unless you changed their type, are int, so integer arithmetic is performed.

You can change the types on the right, or you can cast one of them to float.

dutyCycle = (float)tempPulseWidth/tempPhaseLength;

This will cause floating point arithmetic to be formed, instead of integer arithmetic.

1 Like

AWOL nailed it - casting tempPulseWidth and tempPhaseLength as float()'s works

THANKS SO MUCH everyone!!!

For what it's worth, here's the final code.

Its a working interface between the CNC's spindle control output and the ESC that I'm using on my mill.
I started 4 modes of operation for CNC's outputting analog, frequency modulation, PWM, and custom.

  //
  // Spindle Speed Controller using Arduino Nano - ANDREW JENNINGS 7/5/2013
  // Mode 0 Tested with CSMIO-IP-M cnc controller,  Jeti 77-Opto + Hacker C50 on Paul Jone's 3-speed spindle
  // Mode 1 not developed
  // Mode 2 Theoretically tested with manual PWM
  // Mode 3 not developed
  //
  // D2,3,4,5,11,12 Typical 2x16 lcd hooked up per usual. Not needed, but one was lying around....
  // D6 = PWM out to ESC
  // D7,8 inputs for optional mode switches (atmega internally pulled low)
  // D13 = status LED
  // A0 = Signal In (digital pin 14) - Make sure to fully optically isolate!
  //
  
  
  // LCD feedback
  #include <LiquidCrystal.h>
  LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
  
  // ESC
  #include <Servo.h> 
  Servo spindle;
  
  // Variables
  byte MODE                    = 0; // Mode0 = analog in, Mode1 = frequency controlled, Mode2 = PWM controlled (duty cycle - frequency independant)
  unsigned int spindleLo       = 95;
  unsigned int spindleHi       = 165;
  unsigned int powerLevel      = 0;
  unsigned int dutyCycle       = 0;
  unsigned int timePhaseLength = 0;
  unsigned int timePulseWidth  = 0;
  unsigned int frequency       = 0;
  unsigned long timeRisingOld  = 0;
  unsigned long timeRisingNew  = 0;
  unsigned long timeFalling    = 0;
  
  
  
  
// - - - - - - SETUP SETUP SETUP - - - - - - //
  void setup() {
    //
    // Configure input mode switches, and determine startup state
    pinMode(7, INPUT);
    pinMode(8, INPUT);
    digitalWrite(7,LOW);
    digitalWrite(8,LOW);
    if (!digitalRead(7) && !digitalRead(8)) {
      MODE = 0;
    } else if (!digitalRead(7) && digitalRead(8)) {
      MODE = 1;
    } else if (digitalRead(7) && !digitalRead(8)) {
      MODE = 2;
    } else {
      MODE = 3;
    }
    //
    // Begin LCD & LED
    lcd.begin(16, 2);
    lcd.setCursor(0, 0);
    lcd.print("**CNC  SPINDLE**");
    pinMode(13,OUTPUT);
    digitalWrite(13,LOW);
    //
    // Begin ESC
    lcd.setCursor(0, 1);
    lcd.print("init output...      ");
    spindle.attach(6);
    spindle.write(spindleLo);
    delay(1000);
    lcd.setCursor(0, 1);
    lcd.print("cal. output...      ");
      for (int i = 0; i<=180; i++) {
        spindle.write(i);
        delay(75);
      }
    spindle.write(spindleLo);
    //
    lcd.setCursor(0, 1);
      #if MODE == 0
        lcd.print("M0: Analog Cntrl");
      #elif MODE == 1
        lcd.print("M1: Freq. Cntrl");
      #elif MODE == 2
        // Interrupt Config
        lcd.print("M2: PWM  Cntrl");
        pinMode(14,INPUT);
        PCICR = _BV(PCIE1);
        PCMSK1 = _BV(PCINT8);
      #elif MODE == 3
        lcd.print("M3: Custom Cntrl");
      #endif
    delay(2000);
    //
    // Done
    lcd.clear();
    lcd.print("    Complete    ");
    delay(1000);
  }
  
  
  
  
// - - - - - - LOOP LOOP LOOP - - - - - - //
  void loop() {
    delay(100);
    //
    #if MODE == 0       // pure analog
      lcd.setCursor(0, 0);
      lcd.print("Spindle Pwr:  % ");
      lcd.setCursor(12, 0);
      lcd.print(map(analogRead(0),0,1023,0,99));
      spindle.write(map(analogRead(0),0,1023,spindleLo,spindleHi));
      //
      //
    #elif MODE == 1     // freq controlled
      //
      //
    #elif MODE == 2     // pwm controlled
      if ((timeRisingOld+1000)<=millis()) {
        dutyCycle = 0;
        timePulseWidth = 0;
        timePhaseLength = 0;
        frequency = 0;
        digitalWrite(13,LOW);
      } else {
        digitalWrite(13,HIGH);
      }
      lcd.setCursor(0, 0);
      lcd.print("Spindle Pwr:  % ");
      lcd.setCursor(12, 0);
      lcd.print(constrain(dutyCycle,0,99));
      lcd.setCursor(0, 1);
      lcd.print("   /   ms @   Hz");
      lcd.setCursor(0, 1);
      lcd.print(constrain(timePulseWidth,0,999));
      lcd.setCursor(4, 1);
      lcd.print(constrain(timePhaseLength,0,999));
      lcd.setCursor(11, 1);
      lcd.print(constrain(frequency,0,999));
      spindle.write(map(dutyCycle,0,99,spindleLo,spindleHi));
      //
    #elif MODE == 3     // custom
      //
    #endif
  }
  
  
  



// - - - - - - ISR ISR ISR - - - - - - //
#if MODE == 2
  ISR(PCINT1_vect) {
    if (digitalRead(14)) {
      // RISING EDGE
      timeRisingOld = timeRisingNew;
      timeRisingNew = millis();
      timePhaseLength = (timeRisingNew-timeRisingOld);
      frequency = (1/(float(timePhaseLength)/1000));
    } else {
      // FALLING EDGE
      timeFalling = millis();
      timePulseWidth = (timeFalling-timeRisingNew);
      dutyCycle = 100*(float(timeFalling)-timeRisingNew)/(timeRisingNew-timeRisingOld);
    }
  }
#endif