# NeoPixel Timing Control
Model: gemma-4-e4b-it
Created: 6/9/2026, 11:31:09 PM
Exported from: LM Studio 0.4.15+2

### User
where in this code do i control how long all pixels stary red for and if the board will respond to the "motion detected" message?

#include &lt;esp_now.h&gt;
#include &lt;WiFi.h&gt;
#include &lt;Adafruit_NeoPixel.h&gt;



// --- Configuration ---
#define LED_PIN     10
#define LED_COUNT   10
// Removed PAUSE_TIME from here, as the timing logic will be handled by millis() in the callback.



// Structure definition (MUST MATCH THE SENDER)
typedef struct struct_message {
    char a[32]; // For status text
    int b;       // Integer (e.g., scaled reading)
    float c;     // Float (e.g., magnitude or raw float data)
    bool d;      // Boolean (Movement detected flag)
} struct_message;


struct_message myData;



// --- Object Declarations ---
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);



// Variable to track how many pixels have been lit (This is our persistent counter)
int activePixelCount = 0;


// *** NEW STATE VARIABLES FOR TIMING CONTROL ***
unsigned long lastTriggerTime = 0;
const unsigned long COOLDOWN_MS = 5000; // 5 seconds cooldown period



// --- State Saving Variables for Restoration ---
// We need to store the color of every pixel before a flash occurs.
uint32_t savedPixelColors[LED_COUNT];
bool stateSavedForFlash = false;



//=============================================================
// FUNCTION DEFINITIONS (Helpers must be defined outside setup/loop)
//=============================================================


/**
 * @brief Flashes all NeoPixels red for a specified duration and restores the previous state.
 */
void flashAllRed(unsigned long durationMs) {
    Serial.println("\n!!! RECEIVED 'TOO FAST' - FLASHING ALL RED !!!");
    
    // 1. Save Current State BEFORE flashing (Crucial Step!)
    for (int i = 0; i &lt; LED_COUNT; i++) {
        savedPixelColors[i] = strip.getPixelColor(i);
    }
    stateSavedForFlash = true; // Mark that we have saved the state


    // --- FLASHING SEQUENCE ---
    // Set all pixels to Red
    for (int i = 0; i &lt; LED_COUNT; i++) {
        strip.setPixelColor(i, strip.Color(255, 0, 0)); // Pure Red
    }
    strip.show();


    // Wait for the specified duration
    delay(durationMs); 


    // Turn all pixels off (This is part of the flash effect)
    for (int i = 0; i &lt; LED_COUNT; i++) {
        strip.setPixelColor(i, strip.Color(0, 0, 0)); // Off
    }
    strip.show();


    // --- RESTORATION SEQUENCE ---
    Serial.println("Restoring previous state...");
    for (int i = 0; i &lt; LED_COUNT; i++) {
        strip.setPixelColor(i, savedPixelColors[i]); // Restore the color
    }
    strip.show(); // Show the restored colors


    // Reset the flag and reset the counter state after restoration
    stateSavedForFlash = false; 
    // NOTE: We DO NOT reset activePixelCount here because we want the sequence to continue from where it left off.
    Serial.println("Red flash complete. State restored.");
}



/**
 * @brief Handles lighting the next pixel and cycling the state when capacity is reached.
 * NOTE: This function should only be called if the time cooldown has passed AND no red flash occurred.
 */
void activateNextPixel() {
    // 1. Check if we need to reset (Cycle condition)
    if (activePixelCount &gt;= LED_COUNT) {
        Serial.println("--- CYCLE COMPLETE! Resetting all pixels for next cycle. ---");
        strip.clear(); // Turn ALL lights off
        strip.show();  // Update the physical LEDs
        activePixelCount = 0; // Reset the counter to start over from Pixel 0
    }



    // 2. Light up the current pixel (This runs whether we just reset or not)
    if (activePixelCount &lt; LED_COUNT) {
        // Increment the count *before* lighting, so it reflects the index being lit
        activePixelCount++; 
        
        Serial.print("Lighting Pixel Index: ");
        Serial.println(activePixelCount - 1);



        // --- COLOR MAPPING START HERE (Using your specified colors) ---
        uint32_t color;
        int current_index = activePixelCount - 1; // The physical index being lit (0, 1, 2...)



        if (current_index == 0) {
            color = strip.Color(255, 50, 50); // Reddish
        } else if (current_index == 1) {
            color = strip.Color(255, 255, 50); // Yellowish
        } else if (current_index == 2) {
            color = strip.Color(50, 255, 50); // Green
        } else if (current_index == 3) {
            color = strip.Color(50, 255, 255); // Cyan/Light Blue
        } else if (current_index == 4) {
            color = strip.Color(50, 50, 255); // Blue
        } else if (current_index == 5) {
            color = strip.Color(255, 50, 255); // Magenta/Purple
        } else if (current_index == 6) {
            color = strip.Color(255, 255, 100); // Light Yellow/Cream
        } else if (current_index == 7) {
            color = strip.Color(250, 100, 50); // Orange/Peach
        } else if (current_index == 8) {
            color = strip.Color(100, 200, 150); // Light Teal/Mint
        } else if (current_index == 9) {
            color = strip.Color(200, 80, 10); // Brownish Orange
        } else {
             // Fallback color just in case something goes wrong
             color = strip.Color(255, 0, 0); 
        }



        strip.setPixelColor(current_index, color);
        strip.show(); // Update the physical LEDs
    }
}



/**
 * @brief Callback function executed when data is received via ESP-NOW.
 */
void OnDataRecv(const esp_now_recv_info_t *info, const uint8_t *incomingData, int len) {
    // 1. Copy the received data into our local structure variable
    memcpy(&myData, incomingData, sizeof(myData));
    
    Serial.println("\n=========================================");
    Serial.print("Packet Received from MAC: ");
    for (int i = 0; i &lt; 6; i++) {
        Serial.printf("%02X", info-&gt;src_addr[i]);
        if (i &lt; 5) Serial.print(":");
    }
    Serial.println();



    // Display the received data fields (for debugging)
    Serial.print("Char: ");
    Serial.println(myData.a);
    Serial.print("Int: ");
    Serial.println(myData.b);
    Serial.print("Float: ");
    Serial.println(myData.c);
    Serial.print("Bool: ");
    Serial.println(myData.d ? "TRUE" : "FALSE");



    // 2. CHECK FOR THE SPECIAL MESSAGE FIRST (Highest Priority)
    if (strcmp(myData.a, "TOO FAST") == 0) {
        // Execute the special red flash sequence for 3 seconds
        flashAllRed(500); // *** CHANGED TO 3000 MS ***
        
        // Reset cooldown timer after a manual override
        lastTriggerTime = millis(); 
        return; // Exit immediately to prevent running movement logic below
    }



    // 3. CHECK THE MOVEMENT FLAG AND ENFORCE COOLDOWN (Original Logic)
    if (myData.d == true) {
        unsigned long currentTime = millis();
        
        // Check if enough time has passed since the last successful trigger
        if (currentTime - lastTriggerTime &gt;= COOLDOWN_MS) {
            Serial.println("\n*** MOVEMENT DETECTED! Cooldown elapsed. Starting timed sequence... ***");
            activateNextPixel(); // This starts the process by lighting Pixel 0
            lastTriggerTime = currentTime; // IMPORTANT: Update the timer ONLY when action is taken
        } else {
            // If movement is detected but cooldown hasn't passed, we ignore it.
            Serial.printf("Movement detected, BUT ignored. Still in cooldown period (%.1f seconds remaining).\n", 
                          (float)(COOLDOWN_MS - (millis() - lastTriggerTime)) / 1000.0);
        }
    } else {
        // If movement is NOT detected, we do nothing to the lights, 
        // allowing previously lit pixels to stay lit until a reset/cycle.
        Serial.println("Movement flag is FALSE. Lights remain as they are.");
    }
}



void setup() {
    // Initialize Serial Monitor (Use 115200 baud rate)
    Serial.begin(115200);



    // Setup the NeoPixels
    strip.begin();          
    strip.show();           // Turn OFF all pixels initially
    strip.setBrightness(50); // Set brightness to a reasonable level (0-255)



    // Initialize timing variables
    lastTriggerTime = 0; // Ensure it starts ready to receive the first trigger



    // --- ESP-NOW Initialization ---
    WiFi.mode(WIFI_STA);




    if (esp_now_init() != ESP_OK) {
        Serial.println("Error initializing ESP-NOW");
        return;
    }
    
    // Register the Receive Callback Function
    esp_now_register_recv_cb(OnDataRecv);



    Serial.println("\n=========================================");
    Serial.println("Receiver Initialized and Listening for ESP-NOW Data...");
    Serial.print("Cooldown set to: ");
    Serial.print(COOLDOWN_MS / 1000);
    Serial.println(" seconds.");
}



void loop() {
    // The loop is now empty because all logic runs asynchronously in the callback function.
    delay(20); 
}

### Assistant
You have two main areas to control:

1. **How long the pixels stay red** when receiving the `"TOO FAST"` message.
2. **Whether the board responds** to the `"motion detected"` message (`myData.d == true`).

Here is a detailed breakdown of where and how to make those changes in your code.

---

## 1. Controlling How Long Pixels Stay Red (The "TOO FAST" Flash)

This control is located inside the `flashAllRed()` function, specifically when you call `delay()`.

**Location:** Inside `void flashAllRed(unsigned long durationMs)`

```cpp
// ... (Code before delay)

    // Wait for the specified duration
    delay(durationMs); // &lt;--- THIS IS THE CONTROL POINT!


// ... (Code after delay)
```

**How to Change It:**

The `flashAllRed` function accepts a parameter, `durationMs`, which is passed into it when `"TOO FAST"` is received.

1.  **To make the red flash longer:** Increase the value you pass in the call site.
2.  **To make the red flash shorter:** Decrease the value you pass in the call site.

**Where to change the *value* being passed:**

Look at this section inside `OnDataRecv`:

```cpp
    // 2. CHECK FOR THE SPECIAL MESSAGE FIRST (Highest Priority)
    if (strcmp(myData.a, "TOO FAST") == 0) {
        // Execute the special red flash sequence for 3 seconds
        flashAllRed(500); // &lt;--- CHANGE THE NUMBER HERE! (Currently set to 500ms in your provided code, but commented out a change to 3000)
        
        // Reset cooldown timer after a manual override
        lastTriggerTime = millis(); 
        return; // Exit immediately to prevent running movement logic below
    }
```

*   **If you want the flash to be 3 seconds long:** Change `flashAllRed(500);` to **`flashAllRed(3000);`** (This is what I suspect you intended based on your comment).
*   **If you want it to be 1 second long:** Change it to **`flashAllRed(1000);`**.

---

## 2. Controlling Response to "Motion Detected" Message

The response logic for motion detection is controlled by two main variables/sections: the **Cooldown Period** and the **Conditional Check** in `OnDataRecv`.

### A. Controlling the Cooldown Time (How often it can trigger)

This controls how long the system must wait *after* a successful motion detection before it will respond to another one.

**Location:** Global variable definition near the top of your code.

```cpp
// *** NEW STATE VARIABLES FOR TIMING CONTROL ***
unsigned long lastTriggerTime = 0;
const unsigned long COOLDOWN_MS = 5000; // &lt;--- CHANGE THIS VALUE! (Currently 5000 milliseconds = 5 seconds)
```

**How to Change It:**

*   To make it respond **more often** (shorter wait): Decrease the number (e.g., `2000` for 2 seconds).
*   To make it respond **less often** (longer wait): Increase the number (e.g., `15000` for 15 seconds).

### B. Controlling if Motion Detection is Processed at All (The Gatekeeper)

This logic determines *if* the system even attempts to run the pixel sequence when motion is detected.

**Location:** Inside `void OnDataRecv(...)`, in Section 3.

```cpp
    // 3. CHECK THE MOVEMENT FLAG AND ENFORCE COOLDOWN (Original Logic)
    if (myData.d == true) { // &lt;--- THIS IS THE PRIMARY GATE!
        unsigned long currentTime = millis();
        
        // Check if enough time has passed since the last successful trigger
        if (currentTime - lastTriggerTime &gt;= COOLDOWN_MS) {
            // ... Code runs only if myData.d is true AND cooldown passes
```

**How to Change It:**

1.  **To *always* respond when motion is detected (ignoring the cooldown):** You would need to remove or comment out the entire `if (currentTime - lastTriggerTime &gt;= COOLDOWN_MS)` block and simply call `activateNextPixel()` directly inside the main `if (myData.d == true)` block.
2.  **To *never* respond when motion is detected:** Comment out the entire section starting from `if (myData.d == true) {` down to the end of that block, or change the condition to something always false (e.g., `if (false)`).

### Summary Table

| Goal | Code Location | Variable/Line to Change | Example Value |
| :--- | :--- | :--- | :--- |
| **Red Flash Duration** | `OnDataRecv()` function, inside the `"TOO FAST"` check. | The argument passed to `flashAllRed(X);` | `flashAllRed(3000)` for 3 seconds. |
| **Motion Cooldown Time** | Global variable definition. | `const unsigned long COOLDOWN_MS = X;` | `2000` for a 2-second cooldown. |
| **Ignore Motion Detection** | `OnDataRecv()` function, Section 3. | The entire block starting with `if (myData.d == true)` | Comment out or change the condition to `if (false)`. |

### User
my microcontroller was working and now it is not working and i didnt change anything other than reupload code that was already working

### Assistant
This is an extremely common, frustrating, but usually solvable problem in embedded electronics! When code that *was* working suddenly stops working after a simple re-upload (without changing the logic), the issue is almost always related to **hardware state**, **timing**, or **resource conflict**.

Since you didn't change the code, here is a systematic troubleshooting checklist, ordered from the easiest/most common fixes to the more complex ones.

---

## 🛠️ The "Did I Change Anything?" Checklist (Hardware & Environment)

### 1. Power and Connections (The #1 Suspect)
*   **Power Supply:** Is the power source stable? If you are powering the NeoPixels, the ESP board, *and* any other components from a single USB port or breadboard rail, the current draw might be too high when the LEDs flash brightly. **Try using an external 5V power supply for the LEDs.**
*   **Wiring:** Did anything physically shift? Check every wire connection—especially ground (`GND`) connections between all boards/components. A single loose ground can cause everything to fail silently.
*   **LED Power:** Are you powering the NeoPixels correctly (usually 5V, with a dedicated ground return)?

### 2. The Upload Process Itself
*   **Board Selection:** Did your IDE accidentally switch the selected board type or COM port? Even if it seems correct, re-verify this setting.
*   **Baud Rate/Upload Speed:** Sometimes, an unstable upload process can corrupt the flash memory slightly. Try uploading at a slower speed (if your IDE allows you to change this).

### 3. Timing and State Management (Software Side)
Since your code relies heavily on timing (`delay()`, `millis()`), these are prime suspects:

*   **The `delay()` Function:** The function `flashAllRed(durationMs)` uses `delay(durationMs);`. **`delay()` halts *everything* on the microcontroller.** If you have other critical background tasks (like reading sensors or maintaining Wi-Fi connections) that need to run while the flash is happening, using a blocking `delay()` can cause subsequent code blocks to fail or time out.
    *   **Test:** Temporarily change `flashAllRed(durationMs)` to use non-blocking timing (using `millis()` checks instead of `delay()`) just for testing purposes.

### 4. Resource Conflicts (ESP-NOW & WiFi)
Your code uses both **WiFi** and **ESP-NOW**. These protocols can sometimes interfere with each other, especially during initialization or high traffic periods.

*   **Test Isolation:** Temporarily comment out the entire ESP-NOW setup (`esp_now_init()`, `esp_now_register_recv_cb(OnDataRecv);`) and see if the basic pixel cycling logic (if triggered manually) works perfectly. If it does, the conflict is between WiFi/ESP-NOW and your LED timing.

---

## 💡 Recommended Debugging Steps (The "Divide and Conquer" Approach)

Do not try to fix everything at once. Isolate the failure point:

**Step 1: Test the LEDs in Isolation.**
*   Comment out **ALL** ESP-NOW code (`OnDataRecv`, `esp_now_init`, etc.).
*   In `setup()`, add a simple loop that just cycles the pixels slowly (e.g., turn on pixel 0, wait 1 second, turn off, repeat).
*   **Goal:** If this works perfectly, your LEDs and basic timing are fine.

**Step 2: Test the Trigger Mechanism in Isolation.**
*   Keep the LED test from Step 1 running (or comment it out if you want to focus only on receiving data).
*   In `OnDataRecv`, **comment out the entire `flashAllRed()` call**.
*   Instead, just print a message: `Serial.println("Motion Detected! Logic path reached.");`
*   **Goal:** If this prints correctly when you send a motion packet, your communication link is fine, and the problem lies within the `flashAllRed()` function or its interaction with other code.

**Step 3: Test the Flash Function in Isolation.**
*   Keep the LED test from Step 1 running (or comment it out).
*   In `setup()`, call `flashAllRed(500);` *once* right after initialization, and then let the loop run normally.
*   **Goal:** If this works, the issue is how the flash function interacts with the asynchronous nature of the ESP-NOW callback.

By following these steps, you can pinpoint whether the failure is due to **Power**, **Wiring**, **Timing Blocking (`delay`)**, or **Communication Protocol Conflict**.
