We encountered several problems: the millis() overflow (causing timers to block after 49 days), the serial monitor not functioning properly on the DUE platform, using 0 instead of nullptr in linked lists, incorrect parameter passing (by value versus by reference), and other issues. It was time for a comprehensive code walkthrough. I used the Poseidon project as a reference to clean up the code. While not all software in the Poseidon project adheres strictly to the standard, every potential issue related to the standard was reviewed and addressed.
The millis() Overflow
The millis() overflow problem, previously described in a news post, has now been handled in the DUE standard. By following the rule “CurrentTime – StartTime > Interval,” the millis() overflow no longer poses an issue. I had to correct approximately 50 instances of incorrectly implemented code.
For testing, I created a void SoftMillis() function. Since millis() itself cannot be adjusted or set to a value just before overflow, I applied an offset of -60000 (1 minute) to the runtime during the first use of SoftMillis(). This results in a SoftMillis() value of approximately 47.9 days minus 1 minute. Replacing millis() with SoftMillis() causes the overflow to occur just 1 minute after reboot, allowing for quick testing. The result: no more blocked timers! A reboot of Poseidon (the fish tank controller) is now unnecessary in relation to the board clock.
Use of 0 vs. nullptr and Variable Passing
Using 0 instead of nullptr for linked lists, along with incorrect handling of variable passing, seems to behave differently on the DUE compared to the MEGA2560. Consequently, all code related to linked lists (e.g., transitions and timers) was updated and tested.
While the old code was functional to some extent, it wasn’t optimal. It caused memory issues, which in turn blocked the serial monitor on the DUE after a few outputs. By writing the code correctly, the serial monitor is now operational again. This is a significant improvement for debugging the FSM and checking the HMI. For a long time, I believed the serial monitor would never work on the DUE boards—so I’m delighted this has been resolved.
Considerations for Timer Functions
Timers are created when the Timer() function is first called. Once a timer expires, it returns true for the current cycle and is then removed from the linked list at the end of the FSM cycle. If the Timer() function isn’t checked at the right moment in the code, the timer may expire, be removed, and then be reloaded when called again. This behavior can lead to unexpected results.
To address this, some Timer() calls were replaced with DelayTimer(). A DelayTimer remains true until explicitly removed with CancelTimer(). In some cases, using DelayTimer() with CancelTimer() is preferable to Timer(). Although this doesn’t directly relate to the standard, it improves the implementation of the Finite State Machine.
The Watchdog (Pin 13)
The watchdog functionality on pin 13 wasn’t working on the DUE but has now been fixed. When the HMI is not connected, the watchdog operates with a 1Hz waveform. When the HMI is connected, it generates a short 100ms pulse during the off-time of pin 13.
Results of the Changes
The changes have improved performance (shorter cycle times), cleaned up the code, and restored the functionality of the serial monitor. Unexpected behavior has been significantly reduced, which is especially important when implementing new features.
The updated package will be available for download in December this year as VS2024 V2.6 ARM. The next step is to update the MEGA2560 and ESP32 platforms.