Arduino Uno stops working for no reason?

I have my Arduino Uno hooked up to a 5x7 led matrix. Here is my code:

int colPins[] = {A0, A1, A2, A3, A4};
int rowPins[] = {2, 3, 4, 5, 6, 7, 8};
int allPins[] = {A0, A1, A4, A2, A3, 8, 7, 2, 6, 3, 4, 5};
int led = 13;
int time;
int delayTime = 500;

int A[7][5] = {
  {0, 0, 1, 0, 0},
  {0, 1, 0, 1, 0},
  {1, 0, 0, 0, 1},
  {1, 1, 1, 1, 1},
  {1, 0, 0, 0, 1},
  {1, 0, 0, 0, 1},
  {1, 0, 0, 0, 1}
};
int B[7][5] = {
  {1, 1, 1, 1, 0},
  {1, 0, 0, 0, 1},
  {1, 0, 0, 0, 1},
  {1, 1, 1, 1, 0},
  {1, 0, 0, 0, 1},
  {1, 0, 0, 0, 1},
  {1, 1, 1, 1, 0}
};
int L[7][5] = {
  {1, 0, 0, 0, 0},
  {1, 0, 0, 0, 0},
  {1, 0, 0, 0, 0},
  {1, 0, 0, 0, 0},
  {1, 0, 0, 0, 0},
  {1, 0, 0, 0, 0},
  {1, 1, 1, 1, 1}
};
int K[7][5] = {
  {1, 0, 0, 0, 1},
  {1, 0, 0, 1, 0},
  {1, 0, 1, 0, 0},
  {1, 1, 0, 0, 0},
  {1, 0, 1, 0, 0},
  {1, 0, 0, 1, 0},
  {1, 0, 0, 0, 1}
};
int E[7][5] = {
  {1, 1, 1, 1, 1},
  {1, 0, 0, 0, 0},
  {1, 0, 0, 0, 0},
  {1, 1, 1, 1, 0},
  {1, 0, 0, 0, 0},
  {1, 0, 0, 0, 0},
  {1, 1, 1, 1, 1}
};

void allOff()
{
  for(int i = 0; i < 12; i++)
  {
    pinMode(allPins[i], INPUT);
    digitalWrite(allPins[i], LOW);
  }
}

void displayCharacter(int character[7][5], int length)
{
  time = millis();
  while(millis() - time < length)
  {
    for(int row = 0; row < 7; row++)
    {
      for(int col = 0; col < 5; col++)
      {
        if (character[row][col] == 1)
        {
          pinMode(colPins[col], OUTPUT);
          pinMode(rowPins[row], OUTPUT);
          digitalWrite(colPins[col], LOW);
          digitalWrite(rowPins[row], HIGH);
          delayMicroseconds(delayTime);
          allOff();
        }
      }
    }
  }
}

void setup()
{
  allOff();
  //Serial.begin(9600);
}

void loop()
{
  digitalWrite(led, HIGH);
  delay(1000);
  digitalWrite(led, LOW);
  displayCharacter(B, 1000);
  displayCharacter(L, 1000);
  displayCharacter(A, 1000);
  displayCharacter(K, 1000);
  displayCharacter(E, 1000);
  //Serial.println(millis());
}

The rotation of the letters in the word "BLAKE" each appear 5 times. After that, the B shows for a second, the L shows for a second, and then the circuit just stops working. No LED's blink anymore. I put this in the troubleshooting section because it doesn't seem to be a programming question (although I might be wrong). What could be the problem???

Can you respost that in [code] tags rather than "copy for forum" please? I think we are missing some brackets and stuff. If not, I see a problem.

Ok, done. What's the problem?

Sure it's a programming problem. Do you think the Arduino just fails after a few seconds?

I initially wondered about the [i] which I couldn't see because you didn't use code tags.

Now I think it is more likely to be your choice of "int" for the times. That will roll over after about 32 seconds. Try changing all the time variables from int to unsigned long.

int colPins[] = {A0, A1, A2, A3, A4};
int rowPins[] = {2, 3, 4, 5, 6, 7, 8};
int allPins[] = {A0, A1, A4, A2, A3, 8, 7, 2, 6, 3, 4, 5};
int led = 13;
int time;
int delayTime = 500;

int A[7][5] = {
  {0, 0, 1, 0, 0},
  {0, 1, 0, 1, 0},
  {1, 0, 0, 0, 1},
  {1, 1, 1, 1, 1},
  {1, 0, 0, 0, 1},
  {1, 0, 0, 0, 1},
  {1, 0, 0, 0, 1}
};
int B[7][5] = {
  {1, 1, 1, 1, 0},
  {1, 0, 0, 0, 1},
  {1, 0, 0, 0, 1},
  {1, 1, 1, 1, 0},
  {1, 0, 0, 0, 1},
  {1, 0, 0, 0, 1},
  {1, 1, 1, 1, 0}
};
int L[7][5] = {
  {1, 0, 0, 0, 0},
  {1, 0, 0, 0, 0},
  {1, 0, 0, 0, 0},
  {1, 0, 0, 0, 0},
  {1, 0, 0, 0, 0},
  {1, 0, 0, 0, 0},
  {1, 1, 1, 1, 1}
};
int K[7][5] = {
  {1, 0, 0, 0, 1},
  {1, 0, 0, 1, 0},
  {1, 0, 1, 0, 0},
  {1, 1, 0, 0, 0},
  {1, 0, 1, 0, 0},
  {1, 0, 0, 1, 0},
  {1, 0, 0, 0, 1}
};
int E[7][5] = {
  {1, 1, 1, 1, 1},
  {1, 0, 0, 0, 0},
  {1, 0, 0, 0, 0},
  {1, 1, 1, 1, 0},
  {1, 0, 0, 0, 0},
  {1, 0, 0, 0, 0},
  {1, 1, 1, 1, 1}
};

void allOff()
{
  for(int i = 0; i < 12; i++)
  {

//This doesn't compile? you have a loop to cicle through the array, but always call the array... 
    pinMode(allPins, INPUT);  //pinMode(allPins[i], INPUT);
    digitalWrite(allPins, LOW);//     digitalWrite(allPins[i], LOW);
  }
}

void displayCharacter(int character[7][5], int length)
{
  time = millis();
  while(millis() - time < length)
  {
    for(int row = 0; row < 7; row++)
    {
      for(int col = 0; col < 5; col++)
      {
        if (character[row][col] == 1)
        {
          pinMode(colPins[col], OUTPUT);        //why is this done all the time? Isn't once enough? 
          pinMode(rowPins[row], OUTPUT);     //why is this done all the time? Isn't once enough? 
          digitalWrite(colPins[col], LOW);
          digitalWrite(rowPins[row], HIGH);
          delayMicroseconds(delayTime);
          allOff();
        }
      }
    }
  }
}

void setup()
{
  allOff();
  //Serial.begin(9600);
// initialize LED as output maybe? 
}

void loop()
{
  digitalWrite(led, HIGH);
  delay(1000);
  digitalWrite(led, LOW);
  displayCharacter(B, 1000);
  displayCharacter(L, 1000);
  displayCharacter(A, 1000);
  displayCharacter(K, 1000);
  displayCharacter(E, 1000);
  //Serial.println(millis());
}

Also, you are wasting memory with the characters. 755*2 bytes = 350 bytes. changing the ints to unsigned chars would cut this number to half.

Does the led keep on blinking?

The only thing I see that can cause this is your time variable type. Millis() returns a double. an int can hold more or less 32000 in it. So, your logic will work for 32 seconds. After that, millis - time will always be biggger than interval because time will be truncated.

Imagine past the 32000 millis, the time variable will wrap around and you'll have millis - time. since millis is bigger than 32000 and time started from 0 or a negative number, the while cicle will never execute.

EDIT:

Nick beat me to it again. I need to change time zones. :stuck_out_tongue:

bubulindo:
Also, you are wasting memory with the characters. 755*2 bytes = 350 bytes. changing the ints to unsigned chars would cut this number to half.

Does the led keep on blinking?

The only thing I see that can cause this is your time variable type. Millis() returns a double. an int can hold more or less 32000 in it. So, your logic will work for 32 seconds. After that, millis - time will always be biggger than interval because time will be truncated.

Imagine past the 32000 millis, the time variable will wrap around and you'll have millis - time. since millis is bigger than 32000 and time started from 0 or a negative number, the while cicle will never execute.

EDIT:

Nick beat me to it again. I need to change time zones. :stuck_out_tongue:

THANK YOU!!!! (you too, Nick) I have been fooling with this problem for hours! I changed it to a float, so it should stay lit for a long time. I also changed the the ints to byte. That takes up the same amount of memory as unsigned chars right?

Also, is there a better way to flash a letter for a specific amount of time without using millis()? Because millis() usually looses accuracy at about 70 days.

double is better than float. Remember that millis doesn't return a floating point value...

why would millis lose accuracy after 70 days?
Or do you mean that millis wraps around after 70 days?

Opening file wiring.c and adding a millis reset function is a good way. Problem is, your code isn't easily portable to someone else.

something like

wiring.c

void reset_millis() {
    timer0_overflow_count = 0;
    timer0_millis = 0;
    timer0_fract = 0;
}


Arduino.h

unsigned long millis(void);
unsigned long micros(void);
void reset_millis();

And it should work. :slight_smile:

blake305:
I changed it to a float, so it should stay lit for a long time.
...
Also, is there a better way to flash a letter for a specific amount of time without using millis()? Because millis() usually looses accuracy at about 70 days.

Your objection to unsigned long being?

And no, it doesn't lose accuracy, apart from the normal clock drift over time.

If you are referring to the wrap-around, that happens after 50 days, and does not have to cause any problems if you code correctly. Which you appear to have done (subtracting one time from another).

Float can hold larger values (or at least it appears so in the documentation). Also, I am used to floats because I program in objective-c whenever I make iPhone apps. Just a personal preference, I guess.

Hmmm, larger maybe.

If you look at that, a single-precision float has 23 bits of significand, 1 bit of sign, and 8 for the exponent. Compare that to 32 bits for the significand for an unsigned long.

So what is going to happen is that after you get past 2^24 (16777216) then the only way the float can keep up is to drop some significance, and add to the exponent to compensate. So as it says on Wikipedia you get ~7.2 decimal digits of accuracy.

But if you stick to unsigned long you keep millisecond precision, and just handle the wrap after 50 days by the way you do the comparison.

double is better than float.

On the Arduino, a double IS a float. So, it is hardly better. Put away the PC thinking. It doesn't apply.

Ah, I see what you are saying. I just conducted a test on the arduino:

float floatVar = 4294967295;

void setup()
{
  Serial.begin(9600);
  Serial.print(floatVar);
}

void loop()
{
}

This should output to get "0.00" for some reason (mass precision loss?). Now try changing 4294967295 to 4294967296. You get

integer constant is too large for 'long' type

So is float just a "subclass" of a long? Or is that just a bug?

If I repeat the same thing but change the datatype to long, I get an accurate result, but get the same error when I increase the number by 1 (which is self explanatory, since the maximum value for an unsigned long is 4294967295 = 2^32 - 1)

If an unsigned long can hold 32 bits of data, why are only 31 bits being used? Is it being reserved by the compiler?

I think you are confusing the range of the variable with the range of the literal value you have put in your code. The compiler tries to guess the type for your literal value, but if it guesses wrong and your value is too big for the type it guessed then you'll see compilation errors. Give it a help by telling it what type your literal value is intended to be.

unsigned long ulVar = 4294967296UL;
float  floatVar         = 4294967296.0;

blake305:
So is float just a "subclass" of a long? Or is that just a bug?

No, a float is different. However your syntax is perhaps the issue. Try:

float floatVar = 4294967296.0;

What I think is happening is:

The default for constants is int type.

Example:

void setup()
{
  Serial.begin(115200);
  Serial.println(1000 * 1000);
}

void loop() { }

Output:

16960

1000 * 1000 = 1000000 which is 0xF4240, but we are seeing 16960 which is 0x4240.

So multiplying an int by an int gives an int, hence the truncation.

But if we do this:

void setup()
{
  Serial.begin(115200);
  Serial.println(1000000);
}

void loop() { }

Output:

1000000

The compiler must think "the number 1000000 cannot fit into an int, I'll promote it to a long". Hence the correct output.

However your error message appear to indicate it draws the line at "long long".

For a float though, as I said, you should add the decimal point to the literal so it knows you are using the float type.


blake305:
If I repeat the same thing but change the datatype to long, I get an accurate result, but get the same error when I increase the number by 1 (which is self explanatory, since the maximum value for an unsigned long is 4294967295 = 2^32 - 1)

If an unsigned long can hold 32 bits of data, why are only 31 bits being used? Is it being reserved by the compiler?

What do you mean? It holds 4294967295 which is 2^32 - 1.

Wow, I can't believe I didn't add the ".0" in the float. The error no longer shows up but it still prints out "0.00".

Ah, I was thinking that the maximum number a 32 bit binary can hold is 2^33-1 instead of 2^32-1. Silly me. So what you are saying is that the unsigned long is basically better than a float in my situation?

blake305:
Wow, I can't believe I didn't add the ".0" in the float. The error no longer shows up but it still prints out "0.00".

Hmm, that looks like a bug:

float floatVar = 4294967290.0;

void setup()
{
  Serial.begin(115200);
  Serial.println(floatVar);
}

void loop(){}

Output:

0.00

Even given that the function Print::printFloat looks like it doesn't handle the integral part being > 2^32, my test case is under that.

It is possible 4294967290.0 becomes a number larger than that value when the compiler converts it to a float. Let's see...

f2: 40 e0 ldi r20, 0x00 ; 0
f4: 50 e0 ldi r21, 0x00 ; 0
f6: 60 e8 ldi r22, 0x80 ; 128
f8: 7f e4 ldi r23, 0x4F ; 79

0x4F800000 = 4.2949673e9

We started with 4.294967290e9 and ended up with 4.2949673e9. So "729" was rounded up to "730". The final value is too large for an unsigned long.

The highest printable value you can feed to the compiler is 4294967167.0 which is converted to and printed as 4294967040.0. Basically, the value is right at the fringe of precision for a float.

Ah yes. Well the significand for a float is 24 bits, giving a maximum value of 16777216 that could be stored without scaling. 4294967290 is well over that. So far so good.

This test program here:

volatile float floatVar = 4294967290.0;

void setup()
{
  Serial.begin(115200);
  
//  floatVar /= 100.0;
  
  Serial.print ("floatVar = ");
  Serial.println(floatVar);
  
  unsigned long foo = floatVar;
  Serial.print ("foo = ");
  Serial.println(foo);
}

void loop() {}

Prints:

floatVar = 0.00
foo = 0

If you uncomment the divide by 100, you get:

floatVar = 42949672.00
foo = 42949672

Slightly out, but that's loss of precision for you. We are accurate up to 7 digits.

The fact that the larger number can be divided by 100 to give the right result suggest that the float is actually stored correctly. So the culprit is this line:

  unsigned long foo = floatVar;

I am starting to have my doubts about fixunssfsi:

 // Extract the integer part of the number and print it
  unsigned long int_part = (unsigned long)number;
     90c:	0e 94 5b 06 	call	0xcb6	; 0xcb6 <__fixunssfsi>
     910:	7b 01       	movw	r14, r22
     912:	8c 01       	movw	r16, r24

I mean, what use is it to have the ability to print floats in the Print class, if it fails spectacularly with numbers larger than a long? May as well not have it.

I get what you are saying now. We ended up with 4294967300 which is too large. But surely showing it as zero is going too far? Printing "ovf" or something would be better.

http://code.google.com/p/arduino/issues/detail?id=967