Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions extras/hardware_test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Test on Real Hardware

This is a tiny example to test the current library code on a real device.

Test with:
```
$ pio run --target upload && pio device monitor
...
Button hardware test - press the button on pin 2
pressed
released
pressed
```
23 changes: 23 additions & 0 deletions extras/hardware_test/platformio.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
; Hardware smoke-test for the Button library.
;
; Builds the demo sketch in src/main.cpp against the LOCAL library source (this
; working tree - i.e. your unreleased changes), not a published release, and
; uploads it to a real board.
;
; lib_extra_dirs points the Library Dependency Finder at the directory that
; CONTAINS this library (the parent folder), so PlatformIO reads the source in
; place - no copying and no symlinks. Editing ../../src/Button.* and re-running
; upload immediately tests the new code.
;
; pio run -d extras/hardware_test -e uno -t upload
; pio device monitor -b 9600
;
; (Run from the repo root. Or `cd extras/hardware_test` and drop the `-d`.)

[env:uno]
platform = atmelavr
board = uno
framework = arduino
lib_extra_dirs = ${PROJECT_DIR}/../../..
lib_deps = Button
monitor_speed = 9600
40 changes: 40 additions & 0 deletions extras/hardware_test/src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
Hardware smoke-test for Button.

Wire a button between pin 2 and GND (the internal pull-up is enabled, so no
external resistor is needed). The onboard LED mirrors the debounced state -
lit while the button is held - and each edge is reported over Serial:

"pressed" on a debounced press
"released" on a debounced release

Upload + monitor:
pio run -d extras/hardware_test -e uno -t upload
pio device monitor -b 9600
*/

#include <Arduino.h>
#include <Button.h>

const uint8_t BUTTON_PIN = 2;
Button button(BUTTON_PIN);

void setup()
{
button.begin();
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(9600);
Serial.println(F("Button hardware test - press the button on pin 2"));
}

void loop()
{
if (button.pressed())
Serial.println(F("pressed"));

if (button.released())
Serial.println(F("released"));

// Mirror the debounced state on the LED (PRESSED == LOW).
digitalWrite(LED_BUILTIN, button.read() == Button::PRESSED ? HIGH : LOW);
}
8 changes: 6 additions & 2 deletions src/Button.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "Button.h"
#include <Arduino.h>
#include <stdint.h>

Button::Button(uint8_t pin, uint16_t debounce_ms)
: _pin(pin), _delay(debounce_ms), _state(HIGH), _ignore_until(0), _has_changed(false)
Expand All @@ -23,8 +24,11 @@ void Button::begin()

bool Button::read()
{
// ignore pin changes until after this delay time
if (_ignore_until > millis())
// ignore pin changes until after this delay time. The subtraction is done
// in modular (wraparound-safe) arithmetic so debouncing keeps working across
// the ~49-day millis() rollover: a negative signed difference means we have
// not yet reached _ignore_until.
if ((int32_t)((uint32_t)millis() - _ignore_until) < 0)
{
// ignore any changes during this period
}
Expand Down
30 changes: 30 additions & 0 deletions test/test_button/test_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,35 @@ void test_custom_debounce_time(void)
TEST_ASSERT_TRUE(button.released());
}

// Debouncing stays correct across the 32-bit millis() rollover (~49 days).
void test_debounce_survives_millis_wraparound(void)
{
Button button(2, 100);

// Prime with a normal press so _ignore_until tracks recent activity, the way
// it always does in real operation (never far behind millis()).
_mock_millis = 0x7FFFFFFF;
_mock_pin_state = LOW;
TEST_ASSERT_TRUE(button.pressed());

// Release at the very top of the range: the debounce deadline (millis + 100)
// overflows and wraps to a tiny value (~99).
_mock_millis = 0xFFFFFFFF;
_mock_pin_state = HIGH;
TEST_ASSERT_TRUE(button.released());

// A bounce at the same (large) millis is still inside the debounce window,
// even though the deadline has wrapped to a small number. The old absolute
// comparison (deadline > millis) would wrongly treat the window as expired
// and let this bounce through.
_mock_pin_state = LOW;
TEST_ASSERT_FALSE(button.pressed());

// Once millis() wraps past the (wrapped) deadline, a genuine press registers.
_mock_millis = 0x70;
TEST_ASSERT_TRUE(button.pressed());
}

int main(int, char **)
{
UNITY_BEGIN();
Expand All @@ -126,5 +155,6 @@ int main(int, char **)
RUN_TEST(test_debounce_ignores_bounce);
RUN_TEST(test_toggled_on_each_change);
RUN_TEST(test_custom_debounce_time);
RUN_TEST(test_debounce_survives_millis_wraparound);
return UNITY_END();
}
Loading