Arduino stops sending data to the serial port after a long time period
Asked Answered
B

4

10

I am using an Arduino Uno rev2 device as a permanently connected device that sometimes sends signals to a PC (Windows 7 x64). Code compiled with Arduino 1.0 software from arduino.cc

Topic on arduino.cc, Arduino stops sending data to Serial after a long time period

Souce code

It works perfectly but sometimes, after a long period of time, the PC stops receiving data from the Arduino device. It is not a PC software issue, as all software(putty, telnet, etc..) acts the same - I can send data TO the Arduino (the device responds to commands); I just can't receive it back.

A similar problem was described here in Serial communication stops after long periods., but no solution was suggested.

Disconnecting/connecting the device solved the issue temporarily, but this can't be a solution, as the device is supposed to be used permanently and fully automatically.

Using board reset button that resets program & all values to it's start wont help. Data does not start to be being received by PC.

Notes:

  1. millis() rollover bug is not reproducable on Arduino Uno board with Arduino 1.0 software - I guess this was fixed and millis() now do really rollover only in 50 days, like it is said in documentation. Besides code has millis() independent code that is not responding too.

  2. LED that is blinking during sending data to PC still blinks.

  3. Strings usage could increase memory usage, but this program is way too small for that to be a problem. No additional memory was used after 10+ hours of program running, so I'm not really going to bother with replacing Strings with something else, as Serial port problem is far more major.

If you think that the problem is in arduino program bug, please think of how to explain TX blinking & reset not helping.

Brunhilde answered 5/12, 2011 at 17:37 Comment(15)
If you use a terminal program (TeraTerm, putty) does the PC continue to receive the data? When the PC stops receiving can you see the Arduino is still transmitting with an oscilloscope?Sprite
What code are you using to send data to the PC? Same code as in the linked post? What version of the Arduino software are you using (there have been some changes to Serial in the recently released version 1.0)?Emmons
Jeff, using putty/SecureCRT or serial monitor from arduino.cc IDE act the same: 1. successfully get connected to board 2. successfully send commands to board (I can control it) 3. no characters are received.Brunhilde
Matthew Murdoch, I've switched to 1.0 recently. As errors shows itself in long time period, i need few days to reproduce.Brunhilde
Sorry, I cant read your source code. Is it the same as in the "Topic on arduino.cc" link ?Dimitris
The FixedMillis() function solves nothing, it's only cargo-cult programming. Your watermarks still end up in a unreachable range somewhere below UINT_MAX, it might take another 8 hours until they can ever be incremented again; and after 16 hours the chances are even smaller. See my update for a suitable windowing compare_and_swap function.Dimitris
Your update still solves nothing too. 1. Even in the old code if I send REWIND command Arduino program should response with DIMMED response. This code part is totally independent with millis() function functionality. 2. Reset DOES reset millis() counter too to zero. I specially tested that. 3. I have monitored board behaviour. Board HAVE sent some data every time I lighted "FLASH" sensor, so it DID function correctly. millis() can not be the reason of weird serial behaviour.Brunhilde
You are still forgetting a few variables in your setup() function, for instance lightTimer. After a reset it will still be somewhere beyond reach. AGAIN: it is not about millis() wrapping around, it is about your variables not wrapping around. Try to imagine what happens if lightTimer is 0xffffffff: it can never be set to a new value, since you only check for millis() > lightTimer. The fixedMillis() function wont solve this, cause it is still restricted to the range of values that an unsigned int can represent.Dimitris
Ok, let all them be 0xffff..ff Why that would stop Arduino from responding on 'REWIND' command? Or 'M' command (in new source) Stop ignoring that.Brunhilde
Please refer to this code pastebin.com/8yDn6czm Is it clear now that program should respond to some commands even if millis() wouldn't even work at all?Brunhilde
I uploaded a cleaned-up version of your proof-of-concept code to pastebin. Replaced the Strings by plain character strings, replacing the compare (subsstr(s,0,1) == "A") by a switch on character.Dimitris
Still unresolved and seems to be unresolvable. Now developing a component that would allow to hardware reset Arduino USB by itself.Brunhilde
Did you ever figure anything out? I'm running into the same issue.Mayfair
Are you switching any loads? I have had cases where switching inductive loads like pumps causes the Arduino to lose its footing, requiring either that the drivers be reloaded or the cable connected and reconnected, even though the Arduino code would continue to run.Yordan
@max did you have any luck with this at the end?Rozina
K
2

Perhaps making a software reset your problem would be solved. I ran the following code to find out if a software reset would reset the clock and therefore the millis() function:

void setup()
{
  Serial.begin(9600);
  Serial.println("Will start sending millis values in 5 seconds...");
  delay(5000);
}

void loop()
{
  Serial.println(String(millis()));

  if (Serial.available() > 0)
  {
    if (Serial.read() == '@')
    {
      Serial.println("Rebooting. . .");
      delay(100); // Give the computer time to receive the "Rebooting. . ." message, or it won't show up
      void (*reboot)(void) = 0; // Creating a function pointer to address 0 then calling it reboots the board.
      reboot();
    }
  }
}

As explained in the code, to make a software reboot simply create a function pointer to address 0 and call it. I, indeed, had satisfactory results:

Will start sending clock values in 5 seconds...
5000
5000
5000
5001
5001
5001
5002
5002
5002
. . .
6804
Rebooting...
Will start sending millis value in 5 seconds...
5000
5000
5000
5001
5001
5001
5002
5002
5002
5003
5003
. . .

I hope this solves your problem :)

Kev answered 23/5, 2015 at 7:33 Comment(0)
G
0

If you constantly poll Arduino, then as a workaround you can implement a watchdog in Arduino which will reset Arduino if it hasn't output data to PC for a long time. You can monitor Arduino TX pin, bring it to another input pin, use interrupt... The point is to reset watchdog after each TX activity.

Gamely answered 9/12, 2011 at 11:33 Comment(1)
Resetting board wont solve the problem. Only hardware reconnecting USB restores data receival.Brunhilde
D
0

It is possible that the clock folds around.

previousclock = millis() might get stuck at a high value, just before folding around. You could extend the test to include the case that (currentmilis < previousmillis) plus/minus some fudge factor.

BTW It used ignas's source code (the OP sourcecode was not readble without registration, and I don't want to registrate)

EDIT: I copied the fragment below from wakkerbot, and edited it a bit. It is just to demonstrate how wraparound can get your last_action timestamps stuck at the top of an int interval (if the bump value is not a divisor for int_max) You could probably simplify the above/below logic a bit, since you are only interested in inside/outside interval tests. The typedef for Stamp should of course be adapted to the type of millis() (unsigned long ?) and fakemillis() removed and references to it replaced by millis().

#include <stdio.h>

#define STAMP_INSIDE 0
#define STAMP_BELOW -1
#define STAMP_ABOVE 1
#define STAMP_BEYONDO -1

    /* Intentionally very small, for fast wraparound
    ** Intentionally signed to stress test the logig.
    */
typedef signed char Stamp;

    /* fake clock, returns incrementing value, but folds around
    */
Stamp fakemillis(void)
{
static Stamp ticker =0;
return ticker++;
}

/* Check if "test" is inside or above/below the interval {low,high}
** low and high may have been wrapped around zero (--> low > high)
** return
**      0 := "test" inside interval
**      1 := "test" below interval
**      -1 := "test" above interval (but wrapped)
** The two impossible cases return -2.
*/
static int check_interval(Stamp low, Stamp high, Stamp test)
{
switch (4 *(high >= low)
        +2 *(test >= low)
        +1 *(test > high)
        ) {
        case 0: return STAMP_INSIDE;    /* inside (wrapped) */
        case 1:                 /* outside (wrapped) */
                return ((Stamp)(low - test) < (Stamp)(test - high)) ? STAMP_BELOW : STAMP_ABOVE;
        case 2: break;      /* impossible */
        case 3: return STAMP_INSIDE;    /* inside (wrapped) */
        case 4:                 /* all below */
                return ((Stamp)(low - test) < (Stamp)(test - high)) ? STAMP_BELOW : STAMP_ABOVE;
        case 5: break;      /* impossible */
        case 6: return STAMP_INSIDE;    /* inside normal case */
        case 7:                 /* all above) */
                return ((Stamp)(low - test) < (Stamp)(test - high)) ? STAMP_BELOW : STAMP_ABOVE;
        }   
return STAMP_BEYONDO;
}   

    /* Get new clock value, test if it is inside interval {*old, *old+width)
    ** iff inside: return STAMP_INSIDE;
    ** iff above (or below) return STAMP_ABOVE or STAMP_BELOW
    ** and UPDATE *old
   */
static int test_or_set(Stamp *old, Stamp width)
{
Stamp tick;
int diff;
tick = fakemillis();

diff = check_interval( *old, *old+width, tick);
if (!diff) return 0;
*old = tick;
return diff;
}


int main(void) {
Stamp goodlast=0;
Stamp tick=0;
Stamp badlast=0;
int goodtest;
int badtest;
unsigned uu;

for (uu = 0; uu < 260; uu++) {
    tick= fakemillis();
    if (tick > badlast+10) { badlast=tick; badtest=1; } else {badtest =0;}
    goodtest = test_or_set ( &goodlast, 10);
    printf("%x:Tick=%x bad=%x, badtest=%d good=%x goodtest=%d\n"
    , uu, (unsigned) tick
    , (unsigned) badlast, badtest
    , (unsigned) goodlast, goodtest
    );
    }
return 0;
}

If you compile and run the above program on a "normal" computer, you can see the badlast and badtest getting stuck. That is what happens on your arduino, too, IMHO.

Update: definitely overflow/rollover. (GIYF) http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1200662708

Update2: unrelated, but bad coding practice:

#define CMD_SET_SPEED "S"
...
/* Internal configuration */
if(buffer.substring(0,1)==CMD_SET_SPEED) {
  updateSpeed(buffer.substring(1));
  }

You are comparing two strings here. (this might be handled as intended by c++, but in C it is just plain wrong. I'd also suggest replacing the repeating if(...) {...} by a giant switch statement, that would at least avoid calling the substr() function repeatedly. (or is it inlined?)

UPDATE 20111211: here is a wrap-oblivious compare&set function it needs a pointer to the value to compare and set, and the width of the intended interval:

int test_and_set_if_beyond( unsigned long *pprev, unsigned long width )
{
unsigned long tick, low,high;
low = *pprev;
high = low+width;
tick = millis();

if (low < high) {
    if (tick >= low && tick < high ) return 0; /* normal case */
    }
else { /* interval is wrapped , clock could have wrapped */
    if (tick >= low || tick < high) return 0;
    }
*pprev = tick;
return 1;
}

This function is used in the loop() section as follows:

if (test_and_set_if_beyond ( &lightTimer, lightnessCheckPeriod)) {
    int newLightness = analogRead(brightnessPin);
    if(newLightness-lightness > LIGHT_TRESHOLD) {
      say(RESPONSE_FLASH);
    }
    lightness = newLightness;
  }
  if (test_and_set_if_beyond ( &pingTimer, pingTimerPeriod)) {
    say(RESPONSE_PING);
  }
  if (test_and_set_if_beyond ( &pingLEDTimer, pingTimerPeriod*2)) {
    digitalWrite(failPin, HIGH);
  }
  feed();

Finally: IMHO the reason why RESET does not work, is that not all global variables are initialised in the setup() function. Also: I think you should get rid of the String thingies (is there GC in the runtime ?) and use ordinary character buffers instead.

Dimitris answered 9/12, 2011 at 11:43 Comment(4)
1. According to documentation millis() owerflow in 50 days. And I use (unsigned long) in my code 2. board wont recover sending data after being reset 3. boards blinks with TX led => that is not the issueBrunhilde
From the link to the forum (reply#2) : "The counter is an unsigned long. At 16Mhz, that's 550 seconds, which is about 9 hours."Dimitris
Yes, I'm aware about bad code :) I'll try implementing rollover detecting and one more output function that wont use timer at all. Though it WONT EXPLAIN RESETTING PROGRAM WONT HELP. RESET resets all variables to zero and restarts program from very start, doesn't it? But RESET wont recover serial connection.Brunhilde
millis() DOES reset when board is RESET.Brunhilde
P
-2

After a few hours, the Arduino is not able to execute commands from Arayks and TX 900 with wire, but after the restart the Arduino is enabled.

Puma answered 16/4, 2017 at 5:8 Comment(1)
This could be a more useful Answer if you were to expand upon why you cannot execute commands after a few hours and how you can solve the problem without having to reboot periodically (which is the main thrust of their question). Suggesting helpful modifications to their code would also be a plus, as would references to any related reference sources (including links) that are likely to shed more light on the situation.Hippopotamus

© 2022 - 2024 — McMap. All rights reserved.