2 digit display with Sevseg library - "A" segment staying on

I am using the sevseg library for a 7-segment 2-digit common cathode LED display, controlling it with an Arduino Mega.
For some reason, the "A" segment (top horizontal) of Digit 1 will not turn off during a countdown or count up. I switched the D1 and D2 lines and the problem moves to the other digit, so I know the segment works. It also turns off reliably with the sevseg.blank command.
The pins I am using are segments on 24, 25, 26, 27, 28, 29, 30, and the digit pins are 31 and 32.
I tried this with an Uno using pins 2,3, and 6,7,8,9,10,11,12. Countup works fine, but countdown has the problem with segment "A"
here is the code. I appreciate any thoughts!

#include "SevSeg.h"
SevSeg display;

bool gameTimed = true;
int timeleft = 90; // Total time is 90 seconds

unsigned long timer;
unsigned long currentTime;

byte numDigits = 2;
// byte digitPins[] = {31, 32};
// byte segmentPins[] = {24, 25, 26, 27, 28, 29, 30};
byte digitPins[] = {2, 3};
byte segmentPins[] = {6, 7, 8, 9, 10, 11, 12};
bool resistorsOnSegments = false; // 'false' means resistors are on digit pins
byte hardwareConfig = COMMON_CATHODE; // See README.md for options
bool updateWithDelays = false; // Default. Recommended
bool leadingZeros = false; // Use 'true' if you'd like to keep the leading zeros

void setup() {
  Serial.begin(115200);
  display.begin(hardwareConfig, numDigits, digitPins, segmentPins, resistorsOnSegments, updateWithDelays, leadingZeros);
  display.setBrightness(100);
  // Timer
  currentTime = millis();
  display.setNumber(timeleft);
  display.refreshDisplay();
  Serial.print("\nEnd of setup");
}

void loop() 
{
  timeleft = 90;    // Countdown timer from 90
  currentTime = millis();

  while (gameTimed) {
    timer = millis();
    if ((timer - currentTime) >= 200)  // .2 sec per countdown
    { currentTime = millis();
      timeleft--;
    }
    display.setNumber(timeleft);
    display.refreshDisplay();
     
    if (timeleft == 0)
      gameTimed = false;
      display.blank();
  }
  waitForSerial();
}

void waitForSerial() {
  Serial.println("\nType any character then hit Enter to start timer...");
  while (Serial.available() == 0) {}
  Serial.read();
  gameTimed = true;
}

OK, this was baffling until...

I realized that the refreshDisplay() has to be called continuously, rapidly and all the time.

You can't just setNumber() a number, call refreshDisplay() once and expect to see anything coherent.

You shouldn't really call refreshDisplay() except once per loop:

You need to rearrange you logic. The loop() should not have any loops within it, rather each time through the loop (thousands of times a second) your logic must decide if it is time to ddecrement the timeleft and call setNumber().

You sketch is very nearly close to pretty easy to rework.

You also have some problems at the point where you solicit a new time to count down from which, we can get to those once you un-looped your loop.

Give it a shot. You already have a mechanism in there that keeps your while loop running fast, it is built on the idea of "blink without delay".

That makes it look like everything is jake until you stop... changing the number and while-looping.

a7

Hmm... someone here did what I said.

I stand behind all what I said, for now, but unless both you and she made the same wiring error, or have broken 7 segment displays with exactly the same defects, something strange is going on, as...

the 'A' segment of the tens digit is on even if all that's in the loop is, with the number set to, say, 7:

void loop() 
{
  display.refreshDisplay();
}

So far, we have no clue, no solid leads.

a7

I'd guess that because you have configured a resistor on the digit pins the library is using a multiplexing strategy of lighting only one segment at any instant to maintain an even brightness across the display. Since you stop calling display.refreshDisplay() when the counter is 0 and because you have suppressed the leading zero, the display is frozen at displaying the first segment of the zero in digit position 1.
If you do as @alto77 suggests and call display.refreshDisplay() continuously, that is irrespective of whether or not the number is changing, the problem should go away.

edit
You could also try moving this display.setNumber(timeleft); into the preceding if statement so it is called only when the number to be displayed changes.

Thank you for the advice! I got the display to work by setting up a loop that inquired about time for all the differnt tasks (reading sensors at a certain time, checking scores, the like) and only did 1 refresh per main loop. The numbers looked great.
But, the MP3 player is not happy.
I'm using the DFRobotDFPlayerMini which wants the processor to be paused in a delay when it is playing a sound file.
I haven't yet got the 2 worked out, but now see the benefit of an LCD display, or a driver chip for the LED 7-segment display....

Thanks for the help!

Aha - the resistors on the digit lines - I wondered how the brightness would be even but it looked good when I ran the code. I may try the resistors on segments configuration and see if that helps.
Thanks!

Please post code that works, that does not leave the 'A' segment illuminated when it should not be.

TIA

a7

The remaining odd thing is my extra simple loop that did nothing but call refreshDisplay() which had the same problem with the 'A' segment.

I hope to run this to ground, yes, but I will not let it keep me awake at night.

a7

The display is multiplexed and, at any one instant, is showing either a specific segment on both displays or a single digit on one display depending on the configuration. This relies on a persistence of vision effect. If there is any pause for more than a few milliseconds there will be noticeable flicker. If you must have delay() statements in the loop you may be able to drive display.refreshDisplay() from a hardware timer's interrupt service routine say every 4 ms.

This is indeed most odd.

You've also tested this yourself?
I checked on github to see if there are any relevant open issues with the library and unsurprisingly there are none. Surely such a basic error would be well known by now.

It is probably only necessary to call display.refreshDisplay() once every few milliseconds, say by using a millis() timer instead of at every loop iteration. display.setNumber(timeleft) should probably be called significantly less often in case it interferes somehow with the multiplexing cycle.

Maybe not. :frowning:

First, as you say, if this happened, it would have happened to someone else long ago.

I wasn't calling setNumber() at all, but I was pounding on refreshDisplay(), expecting that it would either not matter, or be handled by a timer throttle of some kind in the routine itself, too soon to step the multiplexing along kinda thing.

Placing a healthy delay() on the loop() that does nothing but scan lets you see the multiplexing happen, one step per call, so either all segments of one digit at a time or one segment of all digits at a time. Calling refresh very often shouldn't matter too much, I think, until one was doing ridiculously too fast, faster than the loop can go?

But. I was using the wokwi simulator. It has rarely been the problem, but here I believe it is.

The wokwi's strong point is not hardware. A LED connected between ground and an input pin with its internal pull-up enabled will glow as brightly as the same LED with no current limiting series resistor on an output pin set to HIGH…

So, without wasting more of anyone's time, I will guess at some point in the scan of the segments the lines are set such that a tiny amount of current will flow in segment 'A' of the first (or is it the last) digit, for the full interval between on call to refresh and the next.

@DianeBMaker had the wrong resistor placement (or setting) or was also using the simulator.

The defect disappeared in the simulator by switching the scan to digit by digit.

It occurs to just now that with a longer delay, I can pause the wokwi at the point where the 'A' is glowing and see what all the lines are doing. I will have to do that. Not just now.

THX for the help, this was some kinda fun.

a7

This is exactly the way I wish to waste my time solving such puzzles.
I've looked at the library on Github SevSeg/SevSeg.cpp at master · DeanIsMe/SevSeg · GitHub and have come to the following conclusions:

  1. The theory of the wokwi's representation of a led being lit via its pullup resistor is probably wrong in this case. As I see it, it could only be the case if pinMode() was used to switch a led and this is not the case.

  2. I have seen that if there is no decimal point parameter handed over to the begin method, it assumes that a decimal point is in use (i.e. not disabled)

  void begin(uint8_t hardwareConfig, uint8_t numDigitsIn, const uint8_t digitPinsIn[],
          const uint8_t segmentPinsIn[], bool resOnSegmentsIn=0,
          bool updateWithDelaysIn=0, bool leadingZerosIn=0,
		  bool disableDecPoint=0);

under normal circumstances this should not be bad, but here there is an unfortunate assumption that if there is a decimal point then 8 pins are used for the segments:

numSegments = disableDecPoint ? 7 : 8; // Ternary 'if' statement

However, if 8 pins are not used, as would be the case if someone forgot to set the disableDecPoint parameter, then strange things will happen, for example:

  for (uint8_t segmentNum = 0 ; segmentNum < numSegments ; segmentNum++) {
    pinMode(segmentPins[segmentNum], OUTPUT);
    digitalWrite(segmentPins[segmentNum], segmentOffVal);
  }

where the array segmentPins[] would appear to be overrun since, in this case, there are only 7 entries in it.

long story short, try setting the disableDecPoint begin() method parameter to true.

Echoing @alto777’s request to see the latest sketch. Can’t contribute to the display discussion but maybe the player.

I am the digital equivalent of speechless. This is why you get the big bucks.

Here a snippet will do. The theory is tested and found to be valid.

bool argh = false; 

void setup() {
  Serial.begin(115200);
  display.begin(hardwareConfig, numDigits, digitPins, segmentPins, resistorsOnSegments, updateWithDelays, leadingZeros, argh);

TBH I did not like the current leak theory, as it would have meant that @DianeBMaker was simulating. The lines are indeed always outputs and the multiplexing is done with output only.

And I am still not fully realizing that there can be functions with the same name and different parameters - so the call is not a document of all and what could be said. And of course I go to the repository, but there too often it means further going to the source for the full story.

Ppl like writing code better than examples better than real documentation.

I'll look, but it seems like all examples should use the begin() method that has all the parameters spelled out...

Nice work. Inspiring. I have said, here, maybe, that things like this must be run to ground.

IDK if raising an issue is appropriate in these circumstances. It's not quite like

  if (a == b);

but it is a mistake that must have been made by others. Besides an advisory like "this is a mature widely used library, if it isn't woking for you it is prolly your fault" I don't know what the point would be, other than suggesting fixing the library code so it was not making "unfortunate" assumptions.

a7

If you've taken the trouble to set up a Wokwi and are prepared to release it, please publish the link. I'd then test a work around which should not cause strange effects if the disableDecPoint parameter is incorrectly set and report the issue in repository.

EDIT

I guess I'd suggest replacing this:

numSegments = disableDecPoint ? 7 : 8; // Ternary 'if' statement

with:

numSegments = sizeof( segmentPinsIn ) / sizeof( segmentPinsIn[0] ) ;

which would at least prevent the array out of bounds problem but that should be tested.

**EDIT 2 **
The above suggestion will not work since only a pointer to the array is available to the begin() method so the number of elements it contains cannot be determined.

@6v6gt here you go


Wokwi_badge SevSeg Segment A Test


Playing with it makes me mad I never noticed that it takes eight refreshes when it thinks there is a decimal point and only seven when there isn't to do one scan.

a7

OK. Thanks. I've played it through and determined that my proposed "fix" would not have worked for reasons which I have added above. However, I have reported the incident. Maybe someone benefits: Unpredictable behavior in a specific case of unused decimal point pins caused by an array out of bounds condition · Issue #109 · DeanIsMe/SevSeg · GitHub

Just continuing this theme it does appear that parameter list to the SevSeg begin() method may have changed over the course of time. This goes back 7 years: Display on seven segment display. - #4 by billhowl
The implication is that the option to disable the decimal points came later. However, the user was then expected to always deliver 8 segment pins.

Anyway, I'm coming round to the conclusion that this is best regarded as a user error and the way the library is structured with parameters including 'C' style arrays, there is little that can be checked if the user gives misleading inputs.