Lesson 20 Simple Creation-Stopwatch: count incorrect

I start to learn Arduino and tried Lesson 20 Simple Creation-Stopwatch code.
but counting seems incorrect as below. could someone help me how to correct the code?

0295
0296
0397
0398
0399
0300

0790
0791
0892
0893
0894
0895
0896
0897
0899
0800
0801

Hundred’s works incorrectly when counting up.

/***************************************
name: Stopwatch
function: Increment a number every second on the 4-digit 7-segment display.
***********************************/
// Email:service@sunfounder.com
// Website:www.sunfounder.com

// Define the pins that are connected to the segments and the digits of the 7-segment display
int segmentPins[] = {2, 3, 4, 5, 6, 7, 8, 9};
int digitPins[] = {13, 12, 11, 10};

long n = 0; // Variable to store the current stopwatch number
int del = 5; // Delay time (in milliseconds) to keep each digit illuminated
unsigned long previousMillis = 0; // Store the last time the stopwatch incremented
const long interval = 300; // One-second interval (in milliseconds)

// Numbers 0-9 for a 7-segment display (common-cathode)
byte numbers[10][8] = {
  {HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, LOW, LOW},  // 0
  {LOW, HIGH, HIGH, LOW, LOW, LOW, LOW, LOW},      // 1
  {HIGH, HIGH, LOW, HIGH, HIGH, LOW, HIGH, LOW},   // 2
  {HIGH, HIGH, HIGH, HIGH, LOW, LOW, HIGH, LOW},   // 3
  {LOW, HIGH, HIGH, LOW, LOW, HIGH, HIGH, LOW},    // 4
  {HIGH, LOW, HIGH, HIGH, LOW, HIGH, HIGH, LOW},   // 5
  {HIGH, LOW, HIGH, HIGH, HIGH, HIGH, HIGH, LOW},  // 6
  {HIGH, HIGH, HIGH, LOW, LOW, LOW, LOW, LOW},     // 7
  {HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, LOW}, // 8
  {HIGH, HIGH, HIGH, HIGH, LOW, HIGH, HIGH, LOW}   // 9
};

void setup() {
  // Configure all segment and digit pins as OUTPUT
  for (int i = 0; i < 8; i++) {
    pinMode(segmentPins[i], OUTPUT);
  }
  for (int i = 0; i < 4; i++) {
    pinMode(digitPins[i], OUTPUT);
    digitalWrite(digitPins[i], HIGH); // Initially turn off all digits (for common-cathode displays, HIGH is OFF)
  }
}

void loop() {
  // Check if a second has passed since the last increment
  if (millis() - previousMillis >= interval) {
    previousMillis += interval; // Update the last increment time
    n = (n + 1) % 10000; // Increment the stopwatch number and wrap around at 9999
  }

  displayNumber(n); // Display the current stopwatch number on the 7-segment display
}

// Function to display a 4-digit number on the 7-segment display
void displayNumber(long num) {
  int divisor = 1000; // Start with the highest divisor for the first digit
  for (int digit = 0; digit < 4; digit++) {
    clearLEDs(); // Turn off all segments and digits
    pickDigit(digit); // Activate the current digit
    int value = (num / divisor) % 10; // Extract the specific digit from the number
    pickNumber(value); // Illuminate the segments to display the digit
    divisor /= 10; // Reduce the divisor for the next digit
    delay(del); // Keep the digit illuminated for a short time
  }
}

// Function to activate a specific digit (0 to 3)
void pickDigit(int x) {
  digitalWrite(digitPins[x], LOW); // Turn ON the selected digit (for common-cathode displays, LOW is ON)
}

// Function to display a single number (0-9) on the currently activated digit
void pickNumber(int x) {
  for (int i = 0; i < 8; i++) {
    digitalWrite(segmentPins[i], numbers[x][i]); // Set each segment according to the pattern for the given number
  }
}

// Function to turn off all segments and digits
void clearLEDs() {
  for (int i = 0; i < 8; i++) {
    digitalWrite(segmentPins[i], LOW); // Turn off all segments
  }
  for (int i = 0; i < 4; i++) {
    digitalWrite(digitPins[i], HIGH); // Turn off all digits
  }
}

Please try running it with this code.

It works fine!!
I compared two (old and this) and found one line has been added. not yet fully understand the meaning result the differences but I will investigate more…

anyway, very appreciated for your kind support, and I love this sample code so much because I learned lots of things!

best regards,

1 Like

I would like to share some insights here to help you understand this issue.
There have been some improvements made to the code, as detailed below:

The main improvement is in the displayNumber function, which is used to extract each digit to be displayed on the four-digit seven-segment display:

From a mathematical perspective, the original method is correct:

For example, with num = 297, in the loop

  • When digit is 0: 297 / 10^(3-0) % 10 = 297 / 1000 % 10 = 0

  • When digit is 1: 297 / 10^(3-1) % 10 = 297 / 100 % 10 = 2

  • When digit is 2: 297 / 10^(3-2) % 10 = 297 / 10 % 10 = 9

  • When digit is 3: 297 / 10^(3-3) % 10 = 297 / 1 % 10 = 7

However, during this process, the code uses the pow() function to calculate the power of the number, which returns a result in double type. This may introduce imprecision in floating-point calculations. When we convert these double types to int, especially in a microcontroller environment with limited resources, this imprecision may lead to calculation errors.

Therefore, in the optimized code, we introduced an integer divisor divisor (initial value of 1000), and divided it by 10 in each loop. This method avoids the use of pow(), reduces potential errors introduced by floating-point calculations, and also makes the code more concise and understandable.

Thank you for your feedback!

https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems