07 - Lab Button Hold Detection

Video

# Lab: Button Hold Detection ## Objective Learn to detect and measure how long a button is held down using the TinkerBlock TK04 Push Button module. This lab introduces timing concepts using the `millis()` function and demonstrates how to track button press duration. ## Learning Outcomes - Understand button hold detection concepts - Master the `millis()` function for timing - Learn state tracking with boolean variables - Implement button press and release detection - Use timing calculations to measure hold duration ## Required Components ![](https://cdn.shopify.com/s/files/1/0331/9994/7908/files/Pasted_image_20250722123351.png?v=1753819645) 1. **Lonely Binary UNO R3** - Main Arduino board 2. **TinkerBlock UNO R3 Shield** - Expansion shield that plugs onto the UNO R3 3. **TinkerBlock TK04 - Push Button** - Push button module with 4-pin connector ## Theory ### Button Hold Detection Button hold detection involves: - **Press Detection**: Identifying when a button transitions from not pressed to pressed - **Release Detection**: Identifying when a button transitions from pressed to not pressed - **Timing Measurement**: Calculating the duration between press and release events ### The `millis()` Function - **Purpose**: Returns the number of milliseconds since the Arduino board began running the current program - **Range**: Returns an `unsigned long` value (0 to 4,294,967,295) - **Overflow**: Resets to 0 after approximately 50 days of continuous operation - **Advantages**: Non-blocking, allows other operations to continue while timing ### State Tracking Variables - **`pressStartTime`**: Stores the timestamp when button was first pressed - **`buttonWasPressed`**: Boolean flag to track if button is currently being held - **State Machine**: Program logic that responds differently based on current state ### Timing Calculation ```cpp unsigned long holdTime = millis() - pressStartTime; ``` - **Formula**: Current time minus start time equals duration - **Result**: Time in milliseconds that button was held - **Data Type**: `unsigned long` to handle large time values ### Key Functions Used - `pinMode(pin, mode)`: Configures a pin as input or output - `digitalRead(pin)`: Reads the state of a digital pin (HIGH or LOW) - `millis()`: Returns milliseconds since program start - `Serial.begin(baud)`: Initializes serial communication - `Serial.print()` and `Serial.println()`: Outputs data to Serial Monitor - `delay(milliseconds)`: Pauses the program for a specified time ## Circuit Diagram ![](https://cdn.shopify.com/s/files/1/0331/9994/7908/files/Pasted_image_20250722123629.png?v=1753819650) ## Connection Instructions 1. **Assemble the Hardware:** - Place the TinkerBlock UNO R3 Shield onto the Lonely Binary UNO R3 - Ensure all pins are properly aligned and seated - The shield should fit snugly on top of the Arduino board 2. **Connect the Push Button Module:** - Take the TinkerBlock TK04 - Push Button module - Align the 4-pin connector with any available 4-pin slot on the shield - Gently push the module into the slot until it clicks into place - The module will automatically connect: - **GND** pin to ground - **VCC** pin to 5V power - **NC** pin remains unconnected - **Signal** pin to digital pin D5 3. **Important Notes:** - No breadboard or jumper wires required - The TK04 push button has a built-in 10kΩ pull-down resistor - The button's built-in LED will light when pressed ## Code ```cpp // Lab 5: Button Hold Detection // Push button connected to digital pin D5 // Define the button pin const int buttonPin = 5; // Button input pin // Variables for button hold detection unsigned long pressStartTime = 0; // When button was first pressed bool buttonWasPressed = false; // Track if button is currently held void setup() { // Configure button pin as input pinMode(buttonPin, INPUT); // Initialize serial communication for debugging Serial.begin(9600); Serial.println("Button Hold Detection Lab Started"); Serial.println("Press and hold the button to see timing results"); } void loop() { // Read the button state int buttonState = digitalRead(buttonPin); // Detect button press (transition from LOW to HIGH) if (buttonState == HIGH && !buttonWasPressed) { // Button just pressed - start timing pressStartTime = millis(); buttonWasPressed = true; Serial.println("Button pressed - timing started"); } // Detect button release (transition from HIGH to LOW) else if (buttonState == LOW && buttonWasPressed) { // Button released - calculate hold time unsigned long holdTime = millis() - pressStartTime; Serial.print("Button held for: "); Serial.print(holdTime); Serial.println(" milliseconds"); buttonWasPressed = false; } // Small delay to prevent too rapid reading delay(10); } ``` ## Code Explanation ### Setup Function ```cpp void setup() { pinMode(buttonPin, INPUT); Serial.begin(9600); Serial.println("Button Hold Detection Lab Started"); } ``` - Configures pin D5 as input for button reading - Initializes serial communication for debugging - Prints startup messages to Serial Monitor ### Global Variables ```cpp unsigned long pressStartTime = 0; // When button was first pressed bool buttonWasPressed = false; // Track if button is currently held ``` - **`pressStartTime`**: Stores the timestamp when button was first pressed - **`buttonWasPressed`**: Boolean flag to prevent multiple timing starts ### Loop Function - Press Detection ```cpp if (buttonState == HIGH && !buttonWasPressed) { pressStartTime = millis(); buttonWasPressed = true; Serial.println("Button pressed - timing started"); } ``` **Logic:** - `buttonState == HIGH`: Button is currently pressed - `!buttonWasPressed`: Button was not previously being held - **Result**: This is a new button press, so start timing ### Loop Function - Release Detection ```cpp else if (buttonState == LOW && buttonWasPressed) { unsigned long holdTime = millis() - pressStartTime; Serial.print("Button held for: "); Serial.print(holdTime); Serial.println(" milliseconds"); buttonWasPressed = false; } ``` **Logic:** - `buttonState == LOW`: Button is currently released - `buttonWasPressed`: Button was previously being held - **Result**: Button was released, so calculate and display hold time ### Timing Calculation ```cpp unsigned long holdTime = millis() - pressStartTime; ``` - **`millis()`**: Current time in milliseconds - **`pressStartTime`**: Time when button was first pressed - **`holdTime`**: Duration button was held (in milliseconds) ## Testing and Verification 1. **Upload the code to Lonely Binary UNO R3** 2. **Open Serial Monitor** in Arduino IDE (Tools → Serial Monitor) 3. **Test different hold durations:** - **Short press**: Press and quickly release (should show ~50-200ms) - **Medium hold**: Hold for 1-2 seconds (should show ~1000-2000ms) - **Long hold**: Hold for 5+ seconds (should show ~5000+ms) 4. **Observe the behavior:** - "Button pressed - timing started" appears when button is first pressed - "Button held for: X milliseconds" appears when button is released - The button's built-in LED should light during the hold period 5. **Verify accuracy:** - Compare displayed times with actual hold duration - Test multiple times to ensure consistent results ## Troubleshooting ### Button doesn't respond: - Check if the TinkerBlock shield is properly seated on the Arduino - Ensure the TK04 module is firmly connected to the shield - Verify the module is connected to a slot that uses D5 pin - Check if code uploaded successfully to the Lonely Binary UNO R3 - Open Serial Monitor to see if button states are being read ### Timing seems inaccurate: - Check if `delay(10)` is present in the loop for stable reading - Verify `millis()` is being used correctly (not `micros()` or `delay()`) - Ensure `pressStartTime` is declared as `unsigned long` - Check for any blocking operations that might affect timing ### Multiple timing starts detected: - Verify `buttonWasPressed` flag is working correctly - Check that the flag is reset to `false` when button is released - Ensure the logic conditions are properly structured ### Serial Monitor shows no output: - Ensure Serial Monitor is set to 9600 baud rate - Check that Serial.begin(9600) is in setup() - Verify the correct COM port is selected in Arduino IDE - Make sure button is being pressed and released completely ### Hold time shows 0 milliseconds: - Check if `pressStartTime` is being set correctly - Verify the button press and release detection logic - Ensure there are no delays between press detection and timing start ## Experiment Variations ### 1. Hold Time Categories Categorize hold times into different ranges: ```cpp void loop() { int buttonState = digitalRead(buttonPin); if (buttonState == HIGH && !buttonWasPressed) { pressStartTime = millis(); buttonWasPressed = true; Serial.println("Button pressed - timing started"); } else if (buttonState == LOW && buttonWasPressed) { unsigned long holdTime = millis() - pressStartTime; // Categorize hold time if (holdTime < 100) { Serial.println("Quick tap detected"); } else if (holdTime < 1000) { Serial.println("Short press detected"); } else if (holdTime < 3000) { Serial.println("Medium hold detected"); } else { Serial.println("Long hold detected"); } Serial.print("Duration: "); Serial.print(holdTime); Serial.println(" milliseconds"); buttonWasPressed = false; } delay(10); } ``` ### 2. Continuous Hold Monitoring Monitor hold time continuously while button is held: ```cpp void loop() { int buttonState = digitalRead(buttonPin); if (buttonState == HIGH && !buttonWasPressed) { pressStartTime = millis(); buttonWasPressed = true; Serial.println("Button pressed - timing started"); } else if (buttonState == HIGH && buttonWasPressed) { // Button is still held - show current hold time unsigned long currentHoldTime = millis() - pressStartTime; Serial.print("Still holding... "); Serial.print(currentHoldTime); Serial.println(" ms"); } else if (buttonState == LOW && buttonWasPressed) { unsigned long holdTime = millis() - pressStartTime; Serial.print("Button held for: "); Serial.print(holdTime); Serial.println(" milliseconds"); buttonWasPressed = false; } delay(100); // Longer delay for continuous monitoring } ``` ### 3. Hold Time with LED Feedback Use LED to indicate hold duration: ```cpp const int ledPin = 13; // Built-in LED void loop() { int buttonState = digitalRead(buttonPin); if (buttonState == HIGH && !buttonWasPressed) { pressStartTime = millis(); buttonWasPressed = true; digitalWrite(ledPin, HIGH); // Turn on LED Serial.println("Button pressed - timing started"); } else if (buttonState == LOW && buttonWasPressed) { unsigned long holdTime = millis() - pressStartTime; digitalWrite(ledPin, LOW); // Turn off LED Serial.print("Button held for: "); Serial.print(holdTime); Serial.println(" milliseconds"); buttonWasPressed = false; } delay(10); } ``` ### 4. Multiple Button Hold Detection Detect holds on multiple buttons: ```cpp const int button1Pin = 5; const int button2Pin = 6; unsigned long press1StartTime = 0; unsigned long press2StartTime = 0; bool button1WasPressed = false; bool button2WasPressed = false; void loop() { int button1State = digitalRead(button1Pin); int button2State = digitalRead(button2Pin); // Button 1 logic if (button1State == HIGH && !button1WasPressed) { press1StartTime = millis(); button1WasPressed = true; Serial.println("Button 1 pressed"); } else if (button1State == LOW && button1WasPressed) { unsigned long holdTime = millis() - press1StartTime; Serial.print("Button 1 held for: "); Serial.print(holdTime); Serial.println(" ms"); button1WasPressed = false; } // Button 2 logic if (button2State == HIGH && !button2WasPressed) { press2StartTime = millis(); button2WasPressed = true; Serial.println("Button 2 pressed"); } else if (button2State == LOW && button2WasPressed) { unsigned long holdTime = millis() - press2StartTime; Serial.print("Button 2 held for: "); Serial.print(holdTime); Serial.println(" ms"); button2WasPressed = false; } delay(10); } ``` ## Questions for Understanding 1. What is the difference between `millis()` and `delay()`? 2. Why do we use `unsigned long` for timing variables? 3. What is the purpose of the `buttonWasPressed` flag? 4. How does the program distinguish between a new press and a continued hold? 5. What happens if you hold the button for more than 50 days? 6. Why do we use `delay(10)` in the main loop? 7. How could you modify the code to detect double-clicks? 8. What is the advantage of using `millis()` over counting loop iterations? ## Next Steps - Try implementing button debouncing techniques - Experiment with different hold time thresholds - Learn about interrupt-based button handling - Explore combining hold detection with other sensors - Try creating a button gesture recognition system - Learn about state machines for complex button interactions ## Safety Notes - Always disconnect power before connecting or disconnecting modules - Handle the TinkerBlock shield and modules carefully to avoid bending pins - Ensure proper alignment when connecting modules to prevent damage - The built-in resistor in the TK04 module prevents component damage - Keep the Lonely Binary UNO R3 and shield in a stable position during operation - Be patient when testing hold times - avoid rapid button pressing that might damage the switch