overflow problem by using the Arbitrary precision (big number) library

Hello,

this is a question regarding the BigNumber.h Library from: Arbitrary precision (big number) library port for Arduino.
I’m currently working on a device to measure corrugated tube. I’m using an encoder on a roll for the measurement and a other roll with a motor to roll up the tube.
The measurement is allredy to a certen degree precise but it would be nice if it could get better.
For that i wanted to make the floats more precise.
The Problem is that I get an overflow by approx. 5000mm for the var. Lang (meaning it jumps to approx. -5000mm).
I tryed to lower the BigNumber Scale but even with a Scale of 1 i get the same overflow.
Is there a way for that not to happen?
I’m currently useing an Arduino nano and the code below (the code is quiet long so i had to shorten it due to the char. limit).
I know the Arduino nano isn’t the best for this job but it is what I currently have and as far as I understand the Library it should be possible to work.
Thanks for the help in advance

#include <LiquidCrystal.h>
#include <Keypad.h>
#include "BigNumber.h"

// Definition der Große des LCD Displays
#define LCD_ROWS 4
#define LCD_COLS 16

//Definition des Keypads
const byte ROWS = 4; // Four rows
const byte COLS = 3; // Three columns

char keys[ROWS][COLS] = {
  {'1', '2', '3'},
  {'4', '5', '6'},
  {'7', '8', '9'},
  {'*', '0', '#'}
};
byte rowPins[ROWS] = {19, 18, 17, 16}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {15, 14, 12}; //connect to the column pinouts of the keypad
// Create the Keypad
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

const int rs = 6, en = 7, d4 = 8, d5 = 9, d6 = 10, d7 = 11; //dec der LCD Konstanten
LiquidCrystal lcd(rs, 255, en, d4, d5, d6, d7); //definierng der LCD beschaltung

//Var für Lichtschranke
int Anschlag;
int sp;
int LS = A6;
int Sp = A7; //Lichtschranke für Startposition

//Var für Encoder
static int pinA = 2; 
static int pinB = 3; 
volatile byte aFlag = 0; 
volatile byte bFlag = 0; 
volatile float encoderPos = 0; 
volatile float oldEncPos = 0; 
volatile byte reading = 0; 
int s, d = 255;

bool Start = LOW;
int ein[5] = {0, 0, 0, 0, 0};

//Menü Var
int p = 1;
bool Man;
int I = 0;

//State Var
int State = 1;
bool M = HIGH, G = HIGH, F = LOW, A = LOW, B = LOW, C = LOW, D = LOW, SollR = LOW, H = LOW;

void setup() {
  Serial.begin(9600);

  //ini encoder und Interrupt
  pinMode(pinA, INPUT_PULLUP); 
  pinMode(pinB, INPUT_PULLUP); 
  attachInterrupt(0, PinA, RISING); 
  attachInterrupt(1, PinB, RISING); 
  //ini PinModes
  pinMode(5, OUTPUT);
  pinMode(4, INPUT_PULLUP);
  
  //enable Interrupts
  interrupts();

  //ini LCD
  lcd.begin(16, 4);
  lcd.setCursor(0, 0);
  lcd.display();

  lcd.print("FAFNIR");
  do {
    sp = analogRead(Sp);
    analogWrite(5, 255);
    Serial.println(sp);
  }
  while (sp < 100);
  analogWrite(5, 0);
}

void printBignum (BigNumber n)
{
  char * s = n.toString ();
  lcd.print (s);
  //Serial.println (s);
  free (s);
}
//Start Menü
void SartMenu() {
 ...
}
void StShMenu() {
 ...
}
void ManMenu() {
 ...
}
void Beenden() {
  ...
}
void Warten() {
 ...
}
void readKeypadEnd() {
 ...
}
void readKeypadManMenu()
{
  ...
}
void readKeypadStShMenu()
{
 ...
}
void readKeypadStartMenu()
{
 ...
}
void readKeypadGewichtMenu()
{
 ...
}
void Kolibri() {
...
}
//Interrupts
void PinA() {
  cli(); 
  reading = PIND & 0xC; 
  if (reading == B00001100 && aFlag) { 
    encoderPos --; 
    bFlag = 0; 
    aFlag = 0; 
  }
  else if (reading == B00000100) bFlag = 1; 
  sei(); 
}

void PinB() {
  cli(); 
  reading = PIND & 0xC; 
  if (reading == B00001100 && bFlag) { 
    encoderPos ++; 
    bFlag = 0; 
    aFlag = 0;
  }
  else if (reading == B00001000) aFlag = 1; 
  sei();
}

void Messung() {
  if (SollR == LOW) {
    if (H == LOW) {
      if (M == HIGH) {
        if (G == HIGH) {
          Soll = ein[4] + ein[3] * 10 + ein[2] * 100 + ein[1] * 1000 + ein[0] * 10000;
          Soll = Soll - 1057.6;
        }
        else {
          Soll = ein[4] + ein[3] * 10 + ein[2] * 100 + ein[1] * 1000 + ein[0] * 10000;
          Soll = Soll - 1015.6;
        }
      }
      else if (M == LOW) {
        if (G == HIGH) {
          Soll = ein[4] + ein[3] * 10 + ein[2] * 100 + ein[1] * 1000 + ein[0] * 10000;
          Soll = Soll - 557.6;
        }
        else {
          Soll = ein[4] + ein[3] * 10 + ein[2] * 100 + ein[1] * 1000 + ein[0] * 10000;
          Soll = Soll - 515.6;
        }
      }
    }
    else if (H == HIGH) {
      Soll = ein[4] + ein[3] * 10 + ein[2] * 100 + ein[1] * 1000 + ein[0] * 10000;
    }
    SollR = HIGH;
  }
  Anschlag = analogRead(LS);
  if (Man == HIGH && Anschlag < 100) {
    lcd.clear();
    lcd.print("Bereit!");
    delay(100);
  }

  if (Anschlag > 100) {
    BigNumber::begin (15);  // initialize library
    BigNumber Lang;
    BigNumber U("0.150480225537958");
    BigNumber u("0.340296593401490");
    if (oldEncPos != encoderPos) {
      Lang = (BigNumber(encoderPos) * U ) - u;  //Encoder 1024 Tic/U
      lcd.clear();
      lcd.print("L");
      lcd.print("\xE1");
      lcd.print("nge in mm:");
      lcd.setCursor(0, 1);
      //lcd.print(Lang);
      printBignum (Lang);
      if (Man == LOW) {
        lcd.setCursor(0, 2);
        lcd.print("Soll:");
        lcd.print(Soll);
        lcd.setCursor(0, 3);
        lcd.print("Roh:");
        lcd.print(encoderPos);
      }
      else if (Man == HIGH) {
        lcd.setCursor(0, 2);
        lcd.print("Encoder Rohwert:");
        lcd.setCursor(0, 3);
        lcd.print(encoderPos);
      }
      delay(100);

      oldEncPos = encoderPos;

    }
    BigNumber::finish ();
    char key1 = keypad.getKey();
    if (key1 == '#' || F == HIGH) {
      F = HIGH;
      if (Lang < Soll && Start == LOW) {
        for (int s = 100; s <= 255; s++) {
          analogWrite(5, s);
          delay(50);
          Start = HIGH;
        }
        Start = HIGH;
      }
      else if (Lang >= Soll - 200) {
        d = Soll - Lang;
        s = map(d, 0, 200, 80, 255);

        analogWrite(5, s);

      }
      if (d <= 80) {
        if (Lang < Soll) {
          analogWrite(5, 80);
        }
        else {
          analogWrite(5, 0);
          Start = LOW;
          F = LOW;
        }
      }
    }
    if (key1 == '*' && F == LOW) {
      State = 6;
    }
  }
  else {
    encoderPos = 0;
  }

 }
void loop() {
  Man = digitalRead(4);
  if (Man == HIGH) {
    State = 5;
  }

  switch (State) {
case 1:                               
SollR = LOW;      
if (A == LOW) {
        SartMenu();
        A = HIGH;
      }
      B = LOW;
      C = LOW;
      H = LOW;
      D = LOW;

      readKeypadStartMenu();
      break;
    case 2:
      if (B == LOW) {
        StShMenu();
        B = HIGH;
      }
      M = HIGH;
      A = LOW;

      if (C == HIGH) {

        readKeypadStShMenu();
        lcd.cursor();
      }
      else {
        readKeypadGewichtMenu();
      }
      break;
    case 3:
      if (B == LOW) {
        StShMenu();
        B = HIGH;
      }
      M = LOW;
      A = LOW;
      if (C == HIGH) {
        readKeypadStShMenu();
        lcd.cursor();
      }
      else {
        readKeypadGewichtMenu();
      }
      break;
    case 4:
      if (D == LOW) {
        ManMenu();
        D = HIGH;
      }
      A = LOW;
      H = HIGH;
      readKeypadManMenu();
      lcd.cursor();
      break;
    case 5:
      lcd.noCursor();
      Messung();
      A = LOW;
      B = LOW;
      C = LOW;
      D = LOW;
      break;
    case 6:
      Beenden();
      readKeypadEnd();
      break;
    case 7:
      Warten();
      do {
        sp = analogRead(Sp);
        analogWrite(5, 255);
      }
      while (sp < 100);
      analogWrite(5, 0);
      Soll = 0;
      d = 255;
      State = 1;
      break;
    case 8:
      Kolibri();
      break;
    default:
      lcd.setCursor(0, 2);
      lcd.print("  ERORR!");
      break;
  }
}

where is Soll defined ? is it a float ?

I've not seen anything modifying the ein array, but I assume it's in the missing code

one of your possible challenge is that the right part of the assignment in the lines such asSoll = ein[4] + ein[3] * 10 + ein[2] * 100 + ein[1] * 1000 + ein[0] * 10000; is calculated as int since the ] array is of type int and the literals are also integers. This will overflow at 32767

I suggest you rewrite this to ensure the computation is done as long``Soll =  10000L * ein[0] + 100L * ein[2] + 10L*ein[3] + ein[4];

HI,

where is Soll defined ? is it a float ?

Sorry I seem to have deleted it by mistake. Yes soll is a float.
the array ein[] is modefide in the deletet code and only captures numbers from 0-9 from the keypad.
I followd your suggestion but it didn't make a change.

as mentioned an int can only represent up to 32767, so if you have 5 digits and type something higher than this, your previous code would fail. so good thing you fixed it.

what do you have in ein really ? is that the key code that hase been typed in (the char '1', '2', ...) or did you subtract '0' to get the actual number ? 1 for '1', 2 for '2' etc.

Hi,
I think the easiest would be to show an example:

void readKeypadStShMenu()
{
  char key = keypad.getKey();
  if (key != NO_KEY) {
    switch (key) {
      case '#':
        I = 0;
        lcd.setCursor(0, 3);
        lcd.print("Bereit");
        State = 5;
        break;
      case '*':
        I = 0;
        p = 1;
        State = 1;
        break;
      case '1':
        if (I < 4) {
          ein[I] = 1;
          I++;
        }
        else {
          I = 0;
          ein[I] = 1;
          I++;
        }
        lcd.setCursor(I, 2);
        lcd.print("1");
        break;
      case '2':
        if (I < 4) {
          ein[I] = 2;
          I++;
        }
        else {
          I = 0;
          ein[I] = 2;
          I++;
        }
        lcd.setCursor(I, 2);
        lcd.print("2");

        break;
      case '3':
        if (I < 4) {
          ein[I] = 3;
          I++;
        }
        else {
          I = 0;
          ein[I] = 3;
          I++;
        }
        lcd.setCursor(I, 2);
        lcd.print("3");
        break;
      case '4':
        if (I < 4) {
          ein[I] = 4;
          I++;
        }
        else {
          I = 0;
          ein[I] = 4;
          I++;
        }
        lcd.setCursor(I, 2);
        lcd.print("4");
        break;
      case '5':
        if (I < 4) {
          ein[I] = 5;
          I++;
        }
        else {
          I = 0;
          ein[I] = 5;
          I++;
        }
        lcd.setCursor(I, 2);
        lcd.print("5");
        break;
      case '6':
        if (I < 4) {
          ein[I] = 6;
          I++;
        }
        else {
          I = 0;
          ein[I] = 6;
          I++;
        }
        lcd.setCursor(I, 2);
        lcd.print("6");
        break;
      case '7':
        if (I < 4) {
          ein[I] = 7;
          I++;
        }
        else {
          I = 0;
          ein[I] = 7;
          I++;
        }
        lcd.setCursor(I, 2);
        lcd.print("7");
        break;
      case '8':
        if (I < 4) {
          ein[I] = 8;
          I++;
        }
        else {
          I = 0;
          ein[I] = 8;
          I++;
        }
        lcd.setCursor(I, 2);
        lcd.print("8");
        break;
      case '9':
        if (I < 4) {
          ein[I] = 9;
          I++;
        }
        else {
          I = 0;
          ein[I] = 9;
          I++;
        }
        lcd.setCursor(I, 2);
        lcd.print("9");
        break;
      case '0':
        if (I < 4) {
          ein[I] = 0;
          I++;
        }
        else {
          I = 0;
          ein[I] = 0;
          I++;
        }
        lcd.setCursor(I, 2);
        lcd.print("0");
        break;
      default:
        break;
    }
  }
}

ein[0]-[4] are getting each a one digit int number assigned which is then composed in to a max 5 digit number as Soll.

Soll = ein[4] + ein[3] * 10L + ein[2] * 100L + ein[1] * 1000L + ein[0] * 10000L;

The max length i want to measure with this is 22000-23000mm so Soll should get a higher number than this.

I see. You could simplify your keypad reading by grouping all the digits into one uniform case and get rid of the array and build Soll directly as you receive the characters

void readKeypadStShMenu()
{
  char key = keypad.getKey();
  if (key != NO_KEY) {
    switch (key) {
      case '#': // assuming this should not void Soll
        I = 0;
        lcd.setCursor(0, 3);
        lcd.print("Bereit");
        State = 5;
        break;
      case '*': // assuming this should reset Soll
        I = 0;
        Soll = 0;
        State = 1;
        break;
      case '1'...'9':
        if (I < 4) {
          Soll = (Soll*10) + (key - '0');
          I++;
        } else {
          Soll = key - '0';
          I = 1;
        }
        lcd.setCursor(I, 2);
        lcd.write(key);
        break;
      default: break;
    }
  }
}

not sure what State is used for

Soll should be a long, not a float as you are only storing integer as you read from the keypad. You’ll get to the float when needed later on

your ISR should be as fast as possible, so:

  • encoderPos should be a long too, not a float (as the only thing you do is ++ or --). You’ll get to the float when needed later on.
  • There is no need to use cli() or sei() in your interrupts as by default interrupts are already deactivated within an ISR.
  • reading is just a transient variable you use in the ISRs, you should not declare it global at all nor even volatile. it can just be a local variable in the ISR and the compiler will likely optimise it away.

I think you could also use the encoder library which is fast and fine tuned rather than deal with your encoder the way you implemented it.

One other issue is also how you access encoderPos. this variable can change through the interruption, and because your arduino is a 8 bit architecture without any floating point unit, all the maths done in float are quite long and work through bytes manipulation. You can start the computation with some bytes and then the interruption modifies another byte and the result becomes just garbage.

=> it’s good practice to work in the loop on copies of the data that can be modified by the interrupt. You create the copy by protecting it from changes in a critical zone where you disable interrupts

noInterrupts();
long encoderPosCopy = encoderPos;
interrupts();

and then you use encoderPosCopy to do all the computations. Same applies to all variables that are shared between the ISRs and the main code.
I noticed that oldEncPos is only used in the loop, so there is no need for it to be volatile.

→ implement those changes, don’t use BigNumbers until you’ve proven you really need 15 decimal precision (0.150480225537958) in your math…

PS/ last but not least, you have so many 1 character variables (A,B,C,D,H, …) that it’s hard to make sense of any of what you do. You should use meaningful variable names.