长时间后Arduino停止向串行端口发送数据

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

我将Arduino Uno rev2设备用作永久连接的设备,有时会向PC(Windows 7 x64)发送信号。使用arduino.cc的Arduino 1.0软件编译的代码

arduino.cc上的主题,Arduino在很长一段时间后停止向串行发送数据

源代码

它可以正常工作,但有时在很长一段时间后,PC会停止从Arduino设备接收数据。这不是PC软件的问题,因为所有软件(putty,telnet等)的行为都相同-我可以将数据发送到Arduino(设备响应命令);我就是收不到。

在长时间的串行通讯停止后,此处描述了类似的问题,但未提出解决方案。

断开/连接设备暂时解决了该问题,但这不是解决方案,因为该设备应该永久且完全自动使用。

使用板重置按钮将程序和所有值重置为启动将无济于事。 PC不会开始接收数据。

笔记:

  • 在使用Arduino 1.0软件的Arduino Uno板上无法重现millis()滚动错误-我猜这是固定的,并且millis()现在只能在50天之内完成滚动,就像文档中所说的那样。此外,代码具有与millis()无关的独立代码,也没有响应。

  • 将数据发送到PC期间闪烁的LED仍然闪烁。

  • 字符串的使用可能会增加内存的使用,但是该程序太小而无法成为问题。程序运行10个多小时后,不再使用任何额外的内存,因此,我真的不打算用其他方式替换字符串,因为串行端口问题更为严重。

  • 如果您认为问题出在arduino程序错误中,请考虑如何解释TX闪烁和重置无济于事。


    也许重新设置软件可以解决您的问题。
    我运行以下代码来确定软件重置是否会重置时钟并因此重置millis()函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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();
        }
      }
    }

    如代码中所述,要使软件重启,只需创建指向地址0的函数指针并调用它即可。
    我确实取得了令人满意的结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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
    . . .

    我希望这可以解决您的问题:)


    时钟可能会折叠。

    折叠之前,previousclock = millis()可能会停留在较高的值。您可以扩展测试以包括(当前mils <以前的millis)加上/减去一些软糖因子的情况。

    顺便说一句,它使用了ignas的源代码(OP源代码未经注册就无法读取,我也不想注册)

    编辑:我从wakkerbot复制了下面的片段,并对其进行了一些编辑。只是为了
    演示环绕操作如何使您的last_action时间戳停留在int间隔的顶部(如果凹凸值不是int_max的除数)
    您可能会稍微简化上下逻辑,因为您只对内部/外部间隔测试感兴趣。 Stamp的typedef当然应该适应millis()(unsigned long?)和falsemillis()的类型,并用millis()替换对它的引用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    #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 St

        /* 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\
    "
        , uu, (unsigned) tick
        , (unsigned) badlast, badtest
        , (unsigned) goodlast, goodtest
        );
        }
    return 0;
    }

    如果在"正常"计算机上编译并运行上述程序,则可能会看到badlast和badtest卡住。恕我直言,这也是在您的arduino上发生的情况。

    更新:肯定溢出/翻转。 (GIYF)
    http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1200662708

    Update2:不相关,但编码习惯不好:

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

    您在这里比较两个字符串。 (这可能会按照c ++的预期进行处理,但是在C语言中,这是完全错误的。我还建议您使用巨大的switch语句替换重复的if(...){...},这至少可以避免调用重复执行substr()函数(或是否内联?)

    UPDATE 20111211:这是一个可忽略包装的compare&set函数,它需要一个指针来指向要比较和设置的值以及预期间隔的宽度:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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;
    }

    该函数在loop()部分中的用法如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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();

    最后:恕我直言,RESET无法起作用的原因是,并非所有全局变量都在setup()函数中初始化。另外:我认为您应该摆脱String的麻烦(运行时是否存在GC?),而应使用普通的字符缓冲区。


    如果您不停地轮询Arduino,则可以在Arduino中实现看门狗,如果很长时间没有向PC输出数据,它将重置Arduino。您可以监控Arduino TX引脚,将其带到另一个输入引脚,使用中断...重点是在每次TX活动之后重置看门狗。


    几个小时后,Arduino无法通过电线执行来自Arayks和TX 900的命令,但是在重启后启用了Arduino。