Problem when using pow function

Hi all, I am in the early stages of making an Arduino controlled signal generator with a 128x64 OLED screen, a rotary encoder and some tactile push buttons. I have yet to add the actual signal generator chip - I am simply working on the display and interface code at the moment.

Basically you change the frequency by using a left and right button to select one of five digits (since the frequency can change from 00000Hz to 99999Hz) you then rotate the rotary encoder to count the selected digit up or down. Here is the full code listing:

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Rotary.h>

Rotary r = Rotary(2, 3);
Adafruit_SSD1306 display(4);

// inputs / outputs
const int inputA = 2;
const int inputB = 3;
const int leftButton = 12;
const int rightButton = 11;

// variables
unsigned long frequency = 10;
int digitToModify = 0;
bool buttonPressed = false;
bool countUp = false;
bool countDown = false;

void setup(){
  // Button setup
  pinMode(leftButton, INPUT);
  pinMode(rightButton, INPUT);

  // Rotary encoder interrupt setup
  PCICR |= (1 << PCIE2);
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
  sei();

  // OLED display Setup
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x64)
  display.setTextColor(WHITE);
  display.clearDisplay();
}

void loop(){ 
  updateFrequency();
  checkButton();
  updateDisplay();
}

void updateDisplay(){
  display.clearDisplay();
  display.setTextSize(2);
  display.setCursor(10,0);
  display.print("Frequency");
  display.setTextSize(3);
  display.setCursor(0,30);
  
  // pad with leading zero's to ensure the frequency always displays 5 digits
  if(frequency < 10000) display.print("0");
  if(frequency < 1000) display.print("0");
  if(frequency < 100) display.print("0");
  if(frequency < 10) display.print("0");
  display.print(frequency);
  display.print("Hz");

  //draw a line underneath the digit you want to modify
  display.drawLine(((4 - digitToModify) * 18), 53, 14 + ((4 - digitToModify) * 18), 53, WHITE);
  display.display();  
}

void updateFrequency(){
  if(countUp == true){
    countUp = false;
    if(frequency + pow(10, digitToModify) <= 99999) frequency = frequency + pow(10, digitToModify);
  }
  else if(countDown == true){
    countDown = false;
    if(frequency - pow(10, digitToModify) > 0) frequency = frequency - pow(10, digitToModify);
  }
}

void checkButton(){ 
  // check for a left button press   
  if(digitalRead(leftButton) == 1){
    if(buttonPressed == false){
      buttonPressed = true;
      digitToModify++;
      if(digitToModify > 4){
        digitToModify = 0;
      }
    }
  }
  
  // check for a right button press  
  else if(digitalRead(rightButton) == 1){
    if(buttonPressed == false){
      buttonPressed = true;
      digitToModify--;
      if(digitToModify < 0){
        digitToModify = 4;
      }
    }
  }
  else{
    buttonPressed = false;
  }
}

ISR(PCINT2_vect) {
  unsigned char result = r.process();
  if (result == DIR_NONE) {
    // do nothing
  }
  else if (result == DIR_CW) {
    countUp = true;
  }
  else if (result == DIR_CCW) {
    countDown = true;
  }
}

The section of code to focus on is with incrementing and decrementing the frequency variable:

void updateFrequency(){
  if(countUp == true){
    countUp = false;
    if(frequency + pow(10, digitToModify) <= 99999) frequency = frequency + pow(10, digitToModify);
  }
  else if(countDown == true){
    countDown = false;
    if(frequency - pow(10, digitToModify) > 0) frequency = frequency - pow(10, digitToModify);
  }
}

When the rotary encoder is rotated, an interrupt is triggered causing the countUp or countDown boolean flag to be set. When counting up it first checks that we will not exceed 99999 if we were to increment the particular digit we have selected. If it will not exceed this value - it will count this digit up by one.

This code works perfectly fine except for the ‘ten thousand’ digit. If this digit is currently a ‘0’ or a ‘1’ and then we count up - it will not only increment the ‘ten thousand’ digit by one - it will also decrement the ‘ones digit’ by one.

I can fix the code by doing the following:

void updateFrequency(){
  if(countUp == true){
    countUp = false;
    if(frequency + pow(10, digitToModify) <= 99999){
      if(digitToModify == 0){
        frequency = frequency + 1;
      }
      else if(digitToModify == 1){
        frequency = frequency + 10;
      }
      else if(digitToModify == 2){
        frequency = frequency + 100;
      }
      else if(digitToModify == 3){
        frequency = frequency + 1000;
      }
      else if(digitToModify == 4){
        frequency = frequency + 10000;
      }
    }
  }
  else if(countDown == true){
    countDown = false;
    if(frequency - pow(10, digitToModify) > 0) frequency = frequency - pow(10, digitToModify);
  }
}

Here is a youtube clip showing the prototype in operation:

Can anyone provide some insight as to why the ‘ones’ column would be decrementing?

it will not only increment the 'ten thousand' digit by one - it will also decrement the 'ones digit' by one

The difficulty may be due to the fact that pow() produces a [u]floating point number[/u], not an integer, and the calculation involves logarithms and antilogarithms. The result is not necessarily equal to an expected integer, so it is not a good idea to use pow() with integer variables.

A better approach would be to use integer constants for the few powers of ten that you will encounter. Or you can use one of the integer pow() functions that other people have written.

The difficulty may be due to the fact that pow() produces a floating point number,

That's what I needed to hear! I didn't even think of that since I had declared my variables as integers and didn't even bother to think about what might be happening within the pow() function. I'll go and fix that now - and thanks very much for the speedy reply :)

I fixed it up by using a look-up-table with the power of the 5 digits:

const int powersOfTen[5]={1, 10, 100, 1000, 10000};

I also changed the frequency variable from an unsigned long to a long (to allow a negative number for when I am checking to see if decrementing a digit will result in a number lower than zero)

long frequency = 10;

Finally, I changed the updateFrequency routine to this:

void updateFrequency(){
  if(countUp == true){
    countUp = false;
    if(frequency + powersOfTen[digitToModify] <= 99999) frequency = frequency + powersOfTen[digitToModify];
  }
  else if(countDown == true){
    countDown = false;
    if(frequency - powersOfTen[digitToModify] > 0) frequency = frequency - powersOfTen[digitToModify];
  }
}

Thanks again for the help.

Does it work correctly now?

jremington: Does it work correctly now?

Yes, it works perfectly now.