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

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

## 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