08 - Lab Button State with Timestamp

Video

# Lab: Button State with Timestamp ## Objective Learn to track button state changes with precise timestamps using the `millis()` function. This lab builds upon basic button reading to add time-based monitoring and state change detection. ## Learning Outcomes - Understand state change detection principles - Master the `millis()` function for timing - Learn to track button press and release events with timestamps - Implement efficient state change monitoring without blocking delays ## 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 ### State Change Detection - **State Change**: Occurs when a button transitions from one state to another (LOW to HIGH or HIGH to LOW) - **Edge Detection**: Detecting the moment when a signal changes from one level to another - **Rising Edge**: Transition from LOW to HIGH (button press) - **Falling Edge**: Transition from HIGH to LOW (button release) ### The `millis()` Function - **Purpose**: Returns the number of milliseconds since the Arduino board began running the current program - **Range**: 0 to 4,294,967,295 (approximately 49.7 days) - **Overflow**: Automatically resets to 0 after reaching maximum value - **Non-blocking**: Unlike `delay()`, `millis()` doesn't pause program execution - **Precision**: Provides millisecond-level timing accuracy ### State Tracking Variables - **`lastButtonState`**: Stores the previous button state for comparison - **Purpose**: Enables detection of state changes by comparing current vs. previous state - **Initialization**: Should be set to the initial button state (usually LOW) ### Key Functions Used - `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 ## 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 6: Button State with Timestamp // Push button connected to digital pin D5 // Tracks button state changes with precise timestamps // Define the button pin const int buttonPin = 5; // Button input pin // Variable to store the last button state int lastButtonState = LOW; void setup() { // Configure button pin as input pinMode(buttonPin, INPUT); // Initialize serial communication for debugging Serial.begin(9600); Serial.println("Button State with Timestamp Lab Started"); Serial.println("Press and release the button to see timestamps"); Serial.println("----------------------------------------"); } void loop() { // Read the current button state int buttonState = digitalRead(buttonPin); // Check if button state has changed if (buttonState != lastButtonState) { // Get current time in milliseconds unsigned long currentTime = millis(); // Determine the type of state change if (buttonState == HIGH) { // Button was just pressed (rising edge) Serial.print("Button PRESSED at: "); Serial.print(currentTime); Serial.println(" ms"); } else { // Button was just released (falling edge) Serial.print("Button RELEASED at: "); Serial.print(currentTime); Serial.println(" ms"); } // Update the last button state lastButtonState = buttonState; } // Small delay to prevent too rapid reading and reduce noise delay(10); } ``` ## Code Explanation ### Setup Function ```cpp void setup() { pinMode(buttonPin, INPUT); Serial.begin(9600); Serial.println("Button State with Timestamp Lab Started"); } ``` - Configures pin D5 as input for button reading - Initializes serial communication for debugging - Prints startup messages to Serial Monitor ### Global Variables ```cpp int lastButtonState = LOW; ``` - **Purpose**: Stores the previous button state for comparison - **Initial Value**: Set to LOW to match the initial button state (not pressed) - **Usage**: Compared with current button state to detect changes ### Loop Function ```cpp void loop() { int buttonState = digitalRead(buttonPin); if (buttonState != lastButtonState) { // State change detected - process it } } ``` **State Change Detection:** - `buttonState != lastButtonState`: Compares current and previous states - **True**: Button state has changed (either pressed or released) - **False**: Button state remains the same **Timestamp Recording:** - `unsigned long currentTime = millis()`: Captures precise timing - Records the exact moment when state change occurs - Provides millisecond-level accuracy **State Change Processing:** ```cpp if (buttonState == HIGH) { // Button press detected Serial.print("Button PRESSED at: "); Serial.print(currentTime); Serial.println(" ms"); } else { // Button release detected Serial.print("Button RELEASED at: "); Serial.print(currentTime); Serial.println(" ms"); } ``` **State Update:** - `lastButtonState = buttonState`: Updates the stored previous state - Ensures next comparison will be accurate - Prevents repeated detection of the same state change ### Timing Considerations - `delay(10)`: Small delay to prevent excessive reading - Reduces noise and improves stability - Allows other processes to run - 10ms provides good responsiveness while maintaining efficiency ## Testing and Verification 1. **Upload the code to Lonely Binary UNO R3** 2. **Open Serial Monitor** in Arduino IDE (Tools → Serial Monitor) 3. **Observe the behavior:** - Initial message: "Button State with Timestamp Lab Started" - When button is pressed: Shows "Button PRESSED at: [time] ms" - When button is released: Shows "Button RELEASED at: [time] ms" - The button's built-in LED should light when pressed 4. **Verify timing accuracy:** - Press and hold the button for different durations - Note the time difference between press and release events - Verify that timestamps increase monotonically ## Expected Output Example ``` Button State with Timestamp Lab Started Press and release the button to see timestamps ---------------------------------------- Button PRESSED at: 1250 ms Button RELEASED at: 1850 ms Button PRESSED at: 3200 ms Button RELEASED at: 3450 ms Button PRESSED at: 5200 ms Button RELEASED at: 5800 ms ``` ## Troubleshooting ### No state change detection: - 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 `lastButtonState` is properly initialized to LOW - Open Serial Monitor to see if any output appears ### Repeated state change messages: - Check for loose connections between shield and Arduino - Verify the button module is securely connected - Try reconnecting the TK04 module to a different 4-pin slot - Check for electrical noise or interference ### Incorrect timestamps: - Verify that `millis()` is being called correctly - Check that Serial Monitor is set to 9600 baud rate - Ensure no other timing functions are interfering - Verify the Arduino has been running continuously ### Button's built-in LED doesn't light: - Check if the TK04 module is properly powered (VCC and GND connections) - Verify the module is firmly seated in the shield - Try reconnecting the module to a different 4-pin slot ## Experiment Variations ### 1. Button Hold Duration Calculate and display how long the button was held: ```cpp int lastButtonState = LOW; unsigned long pressTime = 0; void loop() { int buttonState = digitalRead(buttonPin); if (buttonState != lastButtonState) { if (buttonState == HIGH) { // Button pressed - record start time pressTime = millis(); Serial.print("Button PRESSED at: "); Serial.println(pressTime); } else { // Button released - calculate hold duration unsigned long holdDuration = millis() - pressTime; Serial.print("Button RELEASED at: "); Serial.print(millis()); Serial.print(" ms - Held for: "); Serial.print(holdDuration); Serial.println(" ms"); } lastButtonState = buttonState; } delay(10); } ``` ### 2. Multiple Button Press Counter Count button presses and track timing: ```cpp int lastButtonState = LOW; int pressCount = 0; unsigned long lastPressTime = 0; void loop() { int buttonState = digitalRead(buttonPin); if (buttonState != lastButtonState) { if (buttonState == HIGH) { pressCount++; unsigned long currentTime = millis(); Serial.print("Press #"); Serial.print(pressCount); Serial.print(" at: "); Serial.print(currentTime); Serial.println(" ms"); if (pressCount > 1) { unsigned long timeSinceLastPress = currentTime - lastPressTime; Serial.print("Time since last press: "); Serial.print(timeSinceLastPress); Serial.println(" ms"); } lastPressTime = currentTime; } lastButtonState = buttonState; } delay(10); } ``` ### 3. Button Activity Monitor Track button activity patterns: ```cpp int lastButtonState = LOW; unsigned long sessionStartTime = millis(); int totalPresses = 0; int totalReleases = 0; void loop() { int buttonState = digitalRead(buttonPin); if (buttonState != lastButtonState) { unsigned long currentTime = millis(); unsigned long sessionTime = currentTime - sessionStartTime; if (buttonState == HIGH) { totalPresses++; Serial.print("Press #"); Serial.print(totalPresses); Serial.print(" at "); Serial.print(currentTime); Serial.print(" ms (Session: "); Serial.print(sessionTime); Serial.println(" ms)"); } else { totalReleases++; Serial.print("Release #"); Serial.print(totalReleases); Serial.print(" at "); Serial.print(currentTime); Serial.println(" ms"); } lastButtonState = buttonState; } delay(10); } ``` ## Questions for Understanding 1. What is the difference between `millis()` and `delay()`? 2. Why do we need to store the `lastButtonState`? 3. What is a "state change" in the context of button input? 4. How does the `millis()` function help with timing measurements? 5. What would happen if we didn't update `lastButtonState`? 6. Why do we use `delay(10)` instead of a longer delay? 7. How can you calculate the duration between button press and release? 8. What is the advantage of using timestamps over simple state reporting? ## Next Steps - Experiment with different timing intervals and patterns - Try combining timestamp tracking with other sensors - Learn about interrupt-based button handling for more precise timing - Explore creating a button activity logger with timestamps - Try implementing button gesture recognition using timing patterns - Learn about debouncing techniques for more reliable state detection ## 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 - Monitor Serial output to ensure proper operation and detect any issues