I have an Arduino project (data logger intended to sample 100 samples per second for months) with firmware that is too long to post. It mostly works as supposed to, but I have a confusing issue I can't figure out: rarely, the indicator LEDs (driven by digital pins) stop working. By rarely, I mean if I run 20 devices for two weeks, maybe one will develop the issue, so it's very hard to test empirically but it's frequent enough that it matters. I know it's not a hardware problem because they start again after a reset. The device apparently continues working fine apart from the LED issue when this happens.

I have three hypotheses:

  1. There's a logic problem in my firmware code. I'm skeptical because the relevant block of code is pretty simple.
  2. A memory overflow or some similar problem causes the program to fail at this one particular task but not others. I'm a little skeptical because the device reports free stack in the metadata every second and it's always over 40 bytes; I may be misunderstanding memory use though.
  3. Somehow, the digital pin (4) driving the LED gets flipped from OUTPUT to INPUT. There's definitely no pinMode(4, INPUT) command anywhere in my code though, so if this is happening it's not my code doing it.

Let's suppose it isn't #1. Considering #2: is it likely that a weird memory issue is causing a very specific failure while not affecting core functions of the device?

Considering #3, is there anyway the register setting a pin to INPUT or OUTPUT could be flipped unexpectedly? It sounds crazy to me but I want to rule it out.

The platform is similar to the Arduino Pro (3.3V, 8 MHz, ATMEGA328p) and I'm running the current IDE version (1.8.13). Thanks for any help.

  • of course pins can flip unexpectedly ... you just have to make sure that your program does only expected actions – jsotola Feb 19 at 4:54
  • Is the LED going off or going to Input? You normally see that because if a LED is attached to an input pin, it is glimming instead of on or off (because there's some current going out of an input pin). – PMF Feb 19 at 5:56
  • The LED stops blinking, although it seems very unlikely to me that the relevant code stops running. The pin getting set to INPUT could explain how the LED could stop blinking visibly while the code is still running as normal. The LED's series resistor is only 470 ohms which is much less than the pin's high-impedance state, so even if it did supply some current it probably wouldn't be enough for short-blink visibility in daylight. – Vulcan Feb 19 at 16:11
  • Concerning 2.: it's not trivial to get information about free ram on a non-multitasking system, because it will only give you the free ram at the point at which you call the corresponding "get_free_ram()" function. If you call this function in a high scope (e.g. in loop()) it will return something completely different than when you call it in deeper scope, where local variables, return addresses, etc. are also stored in ram. If you don't use dynamic memory allocation, it will always give the same result, but that probably is not the "high-water-mark" of ram usage. – Sim Son Feb 19 at 18:26

The fact that it is possible to set them incorrectly is why, in some projects where I'm concerned about this, I'll add a small series resistance to an inbound signal. Should the pin become OUTPUT and driven in an opposing direction to the signal, the resistance serves to prevent a short through both devices. The possibility is always there, whether you have an outright bug, or instability, or flash corruption.

With pinMode the opportunity to accidentally set your port to the wrong direction is maybe a bit worse than for code that directly manipulates the data direction register. pinMode calls typically don't get inlined, because pinMode on the Arduino is a fairly complicated function at runtime for what it actually does. So, there's this generic code that can be jumped to that will set various ports to either direction, as opposed to smaller chunks of code that contain instructions that set specific ports to specific settings.

INPUT roughly takes your digital pin out of the circuit. And INPUT_PULLUP sort of does that, with the exception that internal to the AVR there's going to be 20k - 50k of resistance going to "5V". Knowing that, you can test your hypothesis three. When you find your project in the state where the LED isn't lit, take some some resistance (say 1K) and use it to pull the pin toward "5V". Then pull it to GND. Watch the voltage at the pin with a meter. Based on what you have attached and what you see for a voltage, you can determine whether it has entered the INPUT or INPUT_PULLUP state. You'll also be able to see if it's just being driven to whatever state you're calling "not working" ("off" I'm guessing).

You can take all the guess work out of what voltages to see in each scenario if you upload a sketch that sets the pin in question to the INPUT, INPUT_PULLUP, OUTPUT/HIGH, and OUTPUT/LOW states and measure the voltage at the pin when you pull it to each rail with your resistor. Later you can consult the table you've built to see what has happened.

There's only so much to say about this, particularly your first two ideas, without seeing the code and other details. Just that sometimes otherwise simple code has bugs in it. In an ATMega328P project, 40 bytes is not a lot of free space at all. To give you an idea what sort of problem this can trigger: The Arduino implementation of new that's in the 1.8.3 core will happily blow away your register file and whatever else it can get to after that, if the underlying malloc reports not enough memory. Basically new continues to initialize the object it thinks was allocated even when the pointed returned is nullptr. The __malloc_margin is 128 in the 1.8.3. So if you try to malloc with 40 bytes distance between the stack and heap, you will trash your program state, perhaps even setting a data direction register in the process.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.