How to display a long value in a seven segment display?

I have made a few simple scripts to get going and now I want to make something myself.

For my project I require an arduino to keep track of happenings on my DIY pinball table.

To include in my project I want to be able to display scoring and a highscore during attract mode.

I figured that an arduino connected to switches, leds, 7segment 8digit display, external speakers and sd card module would do a good job towards making the pinball table more interesting.

So far a lot is still just a thought but you have to start somewhere.

Back on the subject of displaying scoring during gameplay and highscore during attract mode:

I have looked into ledcontrol and got it working, but I see that when you want to display 8 numbers in the lcd you have to send them as individual byte.

With ever changing scores you will get a problem when you reach 10.

How can I achieve displaying scores and highscores on the display? I have been searching the forum for similar questions but as I'm new here I can't seem to get lucky finding a good example.

Here's my code bit:

void displayHighscore()
{
  // we want to display a highscore value. (this could be a value stored in a txt file on SD card that we should be retrieving here.)
  // there is no SD card module attached to the arduino so we're using a value instead.
  // we expect the value to not exceed 99999999 (we have 1 eight digit LCD display currently, that might be expanded to whatever we need maybe 2x 8 digits.)
  // problem: the LCD display can't just display the full number, they need to be sent one at a time.
  // solution: split the (up to) eight numbers into understandable byte values for the LCD display? How is this accomplished?

  int radix = 10;
  char buffer[8];
  int numbers = sizeof(myLong);

  ltoa(myLong, buffer, radix);

  Serial.println("HighScore:");
  Serial.println(buffer);
  Serial.println("sizeof numbers (in bytes?):");
  Serial.println(numbers);

  long  x = myLong ; // this is the number you want to convert
  int  v[10] ;          // this is the result.   The least significant digit goes in position 0
  int i = 0 ;          // index
  int y ;
  while ( x > 0 )
  {
    y = x / 10 ;
    v[i] = x - (10 * y) ;
    x = y ;
    i++ ;
  }
  Serial.println("Positions:");
  Serial.println(v[0]);
  Serial.println(v[1]);
  Serial.println(v[2]);
  Serial.println(v[3]);
  Serial.println(v[4]);
  Serial.println(v[5]);
  Serial.println(v[6]);
  Serial.println(v[7]);

  delay(5000);
}

myLong is defined at the top of the script as long myLong = 12345678;

When I execute the code on my arduino the console output is:

HighScore:
12345678
sizeof numbers (in bytes?):
4
Position:
8
273
518
2260
0
2048
-11262
12

As you can see it's not what I wanted to get. I wanted to recieve:
8
7
6
5
4
3
2
1

so I can display each number in order perhaps like:

lc.setDigit(0, 0, v[0], false);
lc.setDigit(0, 1, v[1], false);
lc.setDigit(0, 2, v[2], false);
lc.setDigit(0, 3, v[3], false);
lc.setDigit(0, 4, v[4], false);
lc.setDigit(0, 5, v[5], false);
lc.setDigit(0, 6, v[6], false);
lc.setDigit(0, 7, v[7], false);

any ideas?

You need to convert the unsigned long into a character array. Look up the avr-gcc library function ltoa(). You have a copy of the library manual?

DKWatson:
You need to convert the unsigned long into a character array. Look up the avr-gcc library function ltoa(). You have a copy of the library manual?

I did not get any library manuals with the arduino. just the board and usb cable. I'm gonna go try find that library manual.

searching google I find that avr-gcc library manual does not exist like this? I need to refer to avr-libc library manual instead? available here: GCC online documentation - GNU Project and then search where theres many there?

Note: I don't quite understand I found this snippet:

char * ltoa(
	long val,
	char * s,
	int radix)

witch I'm already using but that only gives a full string that seems useless to me.

See attached.

avr-libc-user-manual-2.0.0.pdf (1.6 MB)

DKWatson:
See attached.

Reading into... Thank you...

I find the example in the manual:

23.10.4.16 char∗ltoa( longval, char∗s, intradix )

Convert a long integer to a string. The function ltoa() converts the long integer value from val into an ASCII representation that will be stored under s. The caller is responsible for providing sufficient storage in s.

Note
The minimal size of the buffer s depends on the choice of radix. For example, if the radix is 2 (binary), you need to supply a buffer with a minimal length of 8∗sizeof (long int) + 1 characters, i.e. one character for each bit plus one for the string terminator. Using a larger radix will require a smaller minimal buffer size.

Warning
If the buffer is too small, you risk a buffer overflow.

Conversion is done using the radix as base, which may be a number between 2 (binary conversion) and up to 36. If radix is greater than 10, the next digit after '9' will be the letter 'a'. If radix is 10 and val is negative, a minus sign will be prepended.

The ltoa() function returns the pointer passed as s.

There's no more info for that piece of code. I don't know where to go next. I already have a string what about those individual numbers that I need?

If you do this,

char array[n];
unsigned long data = 12345678; // n (from above would have to equal 9, at least one more than the number of digits
ltoa(data, array, 10); // the 10 is the radix (base 10)

what you end up with is,

array[0] = 1
array[1] = 2
array[2] = 3
array[3] = 4
array[4] = 5
array[5] = 6
array[6] = 7
array[7] = 8

the individual digits of the UL.

Now we're getting somewhere. I applied the changes you suggested and now the output is:

HighScore:
12345678
As individuals:
1
2
3
4
5
6
7
8

I can almost use it now. the individuals are strings now. they need to be converted back to ints so I can do lc.setDigit(0, x, y, false); just putting it like 0, x, array[0], false) does nothing.

byte ones = myLong % 10L;
byte tens = (myLong / 10L) % 10L;
byte hund = (myLong / 100L) % 10L;
byte thou = (myLong / 1000L) % 10L;
byte myri = (myLong / 10000L) % 10L;
byte lakh = (myLong / 100000L) % 10L;
byte mill = (myLong / 1000000L) % 10L;
byte cror = (myLong / 10000000L) % 10L;

thank you, even if it seems hindi based coding I understand what it's doing and it looks very similar to what I was thinking about:

int num8 = (myLong / 1) % 10;
  int num7 = (myLong / 10) % 10;
  int num6 = (myLong / 100) % 10;
  int num5 = (myLong / 1000) % 10;
  int num4 = (myLong / 10000) % 10;
  int num3 = (myLong / 100000) % 10;
  int num2 = (myLong / 1000000) % 10;
  int num1 = (myLong / 10000000) % 10;


  lc.setDigit(0, 0, num8, false);
  lc.setDigit(0, 1, num7, false);
  lc.setDigit(0, 2, num6, false);
  lc.setDigit(0, 3, num5, false);
  lc.setDigit(0, 4, num4, false);
  lc.setDigit(0, 5, num3, false);
  lc.setDigit(0, 6, num2, false);
  lc.setDigit(0, 7, num1, false);
  delay(5000);
  lc.clearDisplay(0);
  delay(1000);

it does output the 12345678 on the display. when the "highscore" long value is changed to 1234 the output on LED screen is 00001234. So it seems to be doing the trick.

what would be nicest IMO is to have the leading zero's not being displayed.

Looks like it's working well now. Or else there must be something I forgot but for now it seems to do okay.

For anyone that is interested in the code snippet here it is. (for anyone else if this could be made cleaner please let me know)

Hopefully my comments are proper for anyone to understand what's happening.

//We always have to include the libraries we need for this script
#include "STDlib.h"
#include "LedControl.h"

/*
  Now we need a LedControl to work with.
 ***** These pin numbers will probably not work with your hardware *****
  pin 12 is connected to the DataIn
  pin 11 is connected to the CLK
  pin 10 is connected to LOAD
  We have only a single MAX72XX.
*/
LedControl lc = LedControl(12, 11, 10, 1);

// we always wait a bit between updates of the display
unsigned long delaytime = 250;

// other delay times
int delaytimeshort = 100;
int delaytimemedium = 200;

// variable to use as virtual highscore since we don't have an SD card file yet.
unsigned long myLong = 4321;

void setup() {

  // in the event we need to display DATA on a computer screen window we need to start serial communications port.
  Serial.begin(9600);

  // setup the MAX72XX 7segment 8digit display, wake up the display from power saving mode. (default behaviour)
  lc.shutdown(0, false); // wake up from power saving mode
  lc.setIntensity(0, 8); // set a brightness value.
  lc.clearDisplay(0); // clear the display before use.
  // call to other functions during setup phase.
  initialiseDisplay();
}

/*
   This void initialiseDisplay() shows to the user the LED Display
   is working with a predictable sequence (loops the numbers 0 thru 9 and also shows the decimal point)
*/
void initialiseDisplay()
{
  for (int i = 0; i < 10; i++)
  {
    for (int j = 0; j < 8; j++)
    {
      lc.setDigit(0, j, i, false); // 0 (we have one LED display), j (the first LED segment on rightmost position), i (what number to display in segment), false (true to display decimal point)
    }
    delay(delaytimeshort); // we must wait
    lc.clearDisplay(0); // and clear the display for next sequence
    for (int k = 7; k >= 0; k--)
    {
      lc.setDigit(0, k, i, true);
    }
    delay(delaytimeshort); // we must wait
    lc.clearDisplay(0); // and clear the display for next sequence
  }
}
/*
  This method will display every number in a sequence to the LCD.
*/

void displayDemo()
{
  for (int number = 0; number < 10; number++)
  {
    for (int ledNR = 0; ledNR < 9; ledNR++)
    {
      lc.setDigit(0, ledNR, number, false);
      delay(delaytimeshort);
    }
    lc.clearDisplay(0);
    for (int ledNR = 8; ledNR >= 0; ledNR--)
    {
      lc.setDigit(0, ledNR, number, false);
      delay(delaytimeshort);
    }
    lc.clearDisplay(0);
    delay(delaytimemedium);
  }
}

void displayHighscore()
{
  // we want to display a highscore value. (this could be a value stored in a txt file on SD card that we should be retrieving here.)
  // there is no SD card module attached to the arduino so we're using a value instead.
  // we expect the value to not exceed 99999999 (we have 1 eight digit LCD display currently, that might be expanded to whatever we need maybe 2x 8 digits.)
  // problem: the LCD display can't just display the full number, they need to be sent one at a time.
  // solution: split the (up to) eight numbers into understandable byte values for the LCD display? How is this accomplished?

  int radix = 10;
  char array[9];

  ltoa(myLong, array, radix);

  Serial.println("HighScore:");
  Serial.println(array);
  Serial.println("As individuals:");
  Serial.println(array[0]);
  Serial.println(array[1]);
  Serial.println(array[2]);
  Serial.println(array[3]);
  Serial.println(array[4]);
  Serial.println(array[5]);
  Serial.println(array[6]);
  Serial.println(array[7]);

  int num8 = (myLong / 1) % 10;
  int num7 = (myLong / 10) % 10;
  int num6 = (myLong / 100) % 10;
  int num5 = (myLong / 1000) % 10;
  int num4 = (myLong / 10000) % 10;
  int num3 = (myLong / 100000) % 10;
  int num2 = (myLong / 1000000) % 10;
  int num1 = (myLong / 10000000) % 10;

  // Spell HI SCORE.
  lc.setChar(0, 7, 'H', false); // H
  lc.setDigit(0, 6, 1, false); // I
  lc.setRow(0, 4, B01011011); // S
  lc.setRow(0, 3, B01001110); // C
  lc.setDigit(0, 2, 0, false); // O
  lc.setRow(0, 1, B01110111); // R
  lc.setChar(0, 0, 'E', true); // E
  delay(2500);
  lc.clearDisplay(0);
  if(num1 > 0)
  {
    lc.setDigit(0, 7, num1, false);
  }
  if(num2 > 0)
  {
    lc.setDigit(0, 6, num2, false);
  }
  if(num3 > 0)
  {
    lc.setDigit(0, 5, num3, false);
  }
  if(num4 > 0)
  {
    lc.setDigit(0, 4, num4, false);
  }
  if(num5 > 0)
  {
    lc.setDigit(0, 3, num5, false);
  }
  if(num6 > 0)
  {
    lc.setDigit(0, 2, num6, false);
  }
  if(num7 > 0)
  {
    lc.setDigit(0, 1, num7, false);
  }
  if(num8 > 0)
  {
    lc.setDigit(0, 0, num8, false);
  }  
  delay(5000);
  lc.clearDisplay(0);
  delay(1000);
}

void loop() {
  displayDemo();
  displayHighscore();
}

For now this is the code that runs when I power on the arduino.

The result on the UNO is as follows

LED initialise animation (only once)

loops:
LED display demo running numbers (takes about 15 seconds)
LED display HI SCORE (2,5 seconds)
LED display data retrieved from myLong (5 seconds)

outputs in the serial log when connected to PC data retrieved from myLong

crazybite:
Looks like it's working well now. Or else there must be something I forgot but for now it seems to do okay.

What happens if you try it on a number that has a zero in it? For example, 250, or 1001. Will either of those display properly?

Setting myLong to a value of 1000 would eventually display after HI SCORE as four blank spaces a 1 and then three blank spaces.

It would error up.

Thinking about that created a fix for it too. I have added a statement to check on the numbers. now 1000 gets displayed.

if (num1 > 0)
  {
    lc.setDigit(0, 7, num1, false);
  }
  if (num2 >= 0 and myLong >= 1000000)
  {
    lc.setDigit(0, 6, num2, false);
  }
  if (num3 >= 0 and myLong >= 100000)
  {
    lc.setDigit(0, 5, num3, false);
  }
  if (num4 >= 0 and myLong >= 10000)
  {
    lc.setDigit(0, 4, num4, false);
  }
  if (num5 >= 0 and myLong >= 1000)
  {
    lc.setDigit(0, 3, num5, false);
  }
  if (num6 >= 0 and myLong >= 100)
  {
    lc.setDigit(0, 2, num6, false);
  }
  if (num7 >= 0 and myLong >= 10)
  {
    lc.setDigit(0, 1, num7, false);
  }
  if (num8 >= 0 and myLong >= 1)
  {
    lc.setDigit(0, 0, num8, false);
  }

crazybite:
Setting myLong to a value of 1000 would eventually display after HI SCORE as four blank spaces a 1 and then three blank spaces.

It would error up.

Thinking about that created a fix for it too. I have added a statement to check on the numbers. now 1000 gets displayed.

if (num1 > 0)

{
    lc.setDigit(0, 7, num1, false);
  }
  if (num2 >= 0 and myLong >= 1000000)
  {
    lc.setDigit(0, 6, num2, false);
  }
  if (num3 >= 0 and myLong >= 100000)
  {
    lc.setDigit(0, 5, num3, false);
  }
  if (num4 >= 0 and myLong >= 10000)
  {
    lc.setDigit(0, 4, num4, false);
  }
  if (num5 >= 0 and myLong >= 1000)
  {
    lc.setDigit(0, 3, num5, false);
  }
  if (num6 >= 0 and myLong >= 100)
  {
    lc.setDigit(0, 2, num6, false);
  }
  if (num7 >= 0 and myLong >= 10)
  {
    lc.setDigit(0, 1, num7, false);
  }
  if (num8 >= 0 and myLong >= 1)
  {
    lc.setDigit(0, 0, num8, false);
  }

Does that fix actually work? It looks to me like it can't work. Did you test it?

Are you sure that you didn't mean to write "or" instead of "and"?

If I'm not mistaken the code is saying if num x is greater or equal to 0 and score is greater or equal to something.

Yes I've tested it on several numbers. inputting 1000 or something like 101010 as myLong values these do display like you would expect them.

I could add code so there is an automatic counter that changes myLong every few seconds like myLong++;

EDIT: i've looked at the no blink example that was here: Gammon Forum : Electronics : Microprocessors : How to do multiple things at once ... like cook bacon and eggs
and I've included this into the code to act as a counter for myLong. now I'm looking at the result on the screen and it's at 171. I've seen 0, 1, 2, 3 etc. 10, 11, 12, 13 etc. 100, 101, 102. As I'm writing this it's now at 211.

So if I'm not mistaken now it works fine?

I will paste the full code.

//We always have to include the libraries we need for this script
#include "STDlib.h"
#include "LedControl.h"

/*
  Now we need a LedControl to work with.
 ***** These pin numbers will probably not work with your hardware *****
  pin 12 is connected to the DataIn
  pin 11 is connected to the CLK
  pin 10 is connected to LOAD
  We have only a single MAX72XX.
*/
LedControl lc = LedControl(12, 11, 10, 1);

// we always wait a bit between updates of the display
unsigned long delaytime = 250;
// other delay times
int delaytimeshort = 100;
int delaytimemedium = 200;

// variable to use as virtual highscore since we don't have an SD card file yet.
unsigned long myLong = 0;
// setup a routine to use as timer device to increment the myLong value every x seconds.
const unsigned long myLongInterval = 500; // 0,5 seconds.
unsigned long myLongTimer; // holds the time since last change to myLong.

// player scores from previous game (if 0 no game was played so no score display)
unsigned long p1GameOverScore = 1000;
unsigned long p2GameOverScore = 0;
unsigned long p3GameOverScore = 0;
unsigned long p4GameOverScore = 0;

// player scores (to use during gameplay)
unsigned long p1score = 102;
unsigned long p2score = 25;
unsigned long p3score = 3;
unsigned long p4score = 1005;

// balls per game
int BallsPerGame = 5;

// player ball number
int P1Ball = 0;
int P2Ball = 0;
int P3Ball = 0;
int P4Ball = 0;

void setup() {

  // in the event we need to display DATA on a computer screen window we need to start serial communications port.
  // Serial.begin(9600);

  // setup the MAX72XX 7segment 8digit display, wake up the display from power saving mode. (default behaviour)
  lc.shutdown(0, false); // wake up from power saving mode
  lc.setIntensity(0, 8); // set a brightness value.
  lc.clearDisplay(0); // clear the display before use.
  // call to other functions during setup phase.
  initialiseDisplay();
  // setup the myLong timer in millis.
  myLongTimer = millis();
}

/*
   This void initialiseDisplay() shows to the user the LED Display
   is working with a predictable sequence (loops the numbers 0 thru 9 and also shows the decimal point)
*/
void initialiseDisplay()
{
  for (int i = 0; i < 10; i++)
  {
    for (int j = 0; j < 8; j++)
    {
      lc.setDigit(0, j, i, false); // 0 (we have one LED display), j (the first LED segment on rightmost position), i (what number to display in segment), false (true to display decimal point)
    }
    delay(delaytimeshort); // we must wait
    lc.clearDisplay(0); // and clear the display for next sequence
    for (int k = 7; k >= 0; k--)
    {
      lc.setDigit(0, k, i, true);
    }
    delay(delaytimeshort); // we must wait
    lc.clearDisplay(0); // and clear the display for next sequence
  }
}
/*
  This method will display every number in a sequence to the LCD.
*/

void displayDemo()
{
  for (int number = 0; number < 10; number++)
  {
    for (int ledNR = 0; ledNR < 9; ledNR++)
    {
      lc.setDigit(0, ledNR, number, false);
      delay(delaytimeshort);
    }
    lc.clearDisplay(0);
    for (int ledNR = 8; ledNR >= 0; ledNR--)
    {
      lc.setDigit(0, ledNR, number, false);
      delay(delaytimeshort);
    }
    lc.clearDisplay(0);
    delay(delaytimemedium);
  }
}

void addScore()
{
  myLong++;
  // remember when we toggled it
  myLongTimer = millis ();
}

void displayHighscore()
{
  // we want to display a highscore value. (this could be a value stored in a txt file on SD card that we should be retrieving here.)
  // there is no SD card module attached to the arduino so we're using a value instead.
  // we expect the value to not exceed 99999999 (we have 1 eight digit LCD display currently, that might be expanded to whatever we need maybe 2x 8 digits.)

  /* This is used to display the information on the computer serial log screen. Not required for regular operations.

    int radix = 10;
    char array[9];

    ltoa(myLong, array, radix);

    Serial.println("HighScore:");
    Serial.println(array);
    Serial.println("As individuals:");
    Serial.println(array[0]);
    Serial.println(array[1]);
    Serial.println(array[2]);
    Serial.println(array[3]);
    Serial.println(array[4]);
    Serial.println(array[5]);
    Serial.println(array[6]);
    Serial.println(array[7]);

  */

  // Split up the numbers num8 represents the smallets number, num1 represents the largest number.
  int num8 = (myLong / 1) % 10;
  int num7 = (myLong / 10) % 10;
  int num6 = (myLong / 100) % 10;
  int num5 = (myLong / 1000) % 10;
  int num4 = (myLong / 10000) % 10;
  int num3 = (myLong / 100000) % 10;
  int num2 = (myLong / 1000000) % 10;
  int num1 = (myLong / 10000000) % 10;

  // Spell HI SCORE.
  lc.setChar(0, 7, 'H', false); // H
  lc.setDigit(0, 6, 1, false); // I
  lc.setRow(0, 4, B01011011); // S
  lc.setRow(0, 3, B01001110); // C
  lc.setDigit(0, 2, 0, false); // O
  lc.setRow(0, 1, B01110111); // R
  lc.setChar(0, 0, 'E', true); // E
  delay(2500);
  lc.clearDisplay(0);
  if (num1 > 0)
  {
    lc.setDigit(0, 7, num1, false);
  }
  if (num2 >= 0 and myLong >= 1000000)
  {
    lc.setDigit(0, 6, num2, false);
  }
  if (num3 >= 0 and myLong >= 100000)
  {
    lc.setDigit(0, 5, num3, false);
  }
  if (num4 >= 0 and myLong >= 10000)
  {
    lc.setDigit(0, 4, num4, false);
  }
  if (num5 >= 0 and myLong >= 1000)
  {
    lc.setDigit(0, 3, num5, false);
  }
  if (num6 >= 0 and myLong >= 100)
  {
    lc.setDigit(0, 2, num6, false);
  }
  if (num7 >= 0 and myLong >= 10)
  {
    lc.setDigit(0, 1, num7, false);
  }
  if (num8 >= 0 and myLong >= 0)
  {
    lc.setDigit(0, 0, num8, false);
  }
  delay(5000);
  lc.clearDisplay(0);
}

void displayScore()
{
  // Split up the numbers num8 represents the smallets number, num1 represents the largest number.
  int num8 = (myLong / 1) % 10;
  int num7 = (myLong / 10) % 10;
  int num6 = (myLong / 100) % 10;
  int num5 = (myLong / 1000) % 10;
  int num4 = (myLong / 10000) % 10;
  int num3 = (myLong / 100000) % 10;
  int num2 = (myLong / 1000000) % 10;
  int num1 = (myLong / 10000000) % 10;
  // Display the score
  if (num1 > 0)
  {
    lc.setDigit(0, 7, num1, false);
  }
  if (num2 >= 0 and myLong >= 1000000)
  {
    lc.setDigit(0, 6, num2, false);
  }
  if (num3 >= 0 and myLong >= 100000)
  {
    lc.setDigit(0, 5, num3, false);
  }
  if (num4 >= 0 and myLong >= 10000)
  {
    lc.setDigit(0, 4, num4, false);
  }
  if (num5 >= 0 and myLong >= 1000)
  {
    lc.setDigit(0, 3, num5, false);
  }
  if (num6 >= 0 and myLong >= 100)
  {
    lc.setDigit(0, 2, num6, false);
  }
  if (num7 >= 0 and myLong >= 10)
  {
    lc.setDigit(0, 1, num7, false);
  }
  if (num8 >= 0 and myLong >= 0)
  {
    lc.setDigit(0, 0, num8, false);
  }
  delay(1000);
  lc.clearDisplay(0);
}

void loop() {
  // displayDemo(); // comment away if you want to see it.
  // displayHighscore(); // comment away if you want to see it.
  // the following if statement checks if addScore() and displayScore() can be executed.
  if ((millis () - myLongTimer) >= myLongInterval)
  {
    addScore();
    displayScore();
  }
}

crazybite:
If I'm not mistaken the code is saying if num x is greater or equal to 0 and score is greater or equal to something.

Yes I've tested it on several numbers. inputting 1000 or something like 101010 as myLong values these do display like you would expect them.

I could add code so there is an automatic counter that changes myLong every few seconds like myLong++;

EDIT: i've looked at the no blink example that was here: Gammon Forum : Electronics : Microprocessors : How to do multiple things at once ... like cook bacon and eggs
and I've included this into the code to act as a counter for myLong. now I'm looking at the result on the screen and it's at 171. I've seen 0, 1, 2, 3 etc. 10, 11, 12, 13 etc. 100, 101, 102. As I'm writing this it's now at 211.

So if I'm not mistaken now it works fine?

Sorry, my bad.

My mistake was that I saw lines like this

  if (num2 >= 0 and myLong >= 1000000)

and I misread the >= 0 as > 0.

I wouldn't worry about that, it made me take another look and do some more testing. It's now counting happy.