# NeoPixel Error State Handling
Model: gemma-4-e4b-it
Created: 6/8/2026, 10:04:29 AM
Exported from: LM Studio 0.4.15+2

### User
hi, can you please change this code so that when it recieves the "TOO FAST" message, it turns all Neopixels red? im using esp32 to esp32 wireless comms

#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



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


/**
 * @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.
 */
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 THE MOVEMENT FLAG AND ENFORCE COOLDOWN (THE CORE CHANGE)
    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
This is a great request! To make the NeoPixels turn **all red** when the message `"TOO FAST"` is received, we need to:

1.  **Check the `a` field (the character array)** inside the `OnDataRecv` callback function for the specific string `"TOO FAST"`.
2.  If it matches, we must override the normal pixel cycling logic and instead call a new function that sets every single NeoPixel to red.

Here is the modified code:

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

// --- Global State Flag for Error Handling ---
bool isErrorState = false;


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


/**
 * @brief Sets ALL NeoPixels to a specified color and updates the strip.
 * @param r Red component (0-255)
 * @param g Green component (0-255)
 * @param b Blue component (0-255)
 */
void setAllLedsColor(int r, int g, int b) {
    for (int i = 0; i &lt; LED_COUNT; i++) {
        strip.setPixelColor(i, strip.Color(r, g, b));
    }
    strip.show(); // Update the physical LEDs
}


/**
 * @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 error state is active.
 */
void activateNextPixel() {
    // If we are in an error state, do not run the normal cycle logic
    if (isErrorState) {
        Serial.println("Cannot activate pixel sequence: Currently in Error State.");
        return;
    }

    // 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();


    // --- NEW: CHECK FOR "TOO FAST" ERROR MESSAGE FIRST ---
    const char* errorString = "TOO FAST";
    if (strcmp(myData.a, errorString) == 0) {
        Serial.println("\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
        Serial.println("!!! RECEIVED 'TOO FAST' - TRIGGERING RED ALERT !!!");
        Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
        
        // Set all pixels to bright red (255, 0, 0)
        setAllLedsColor(255, 0, 0); 
        
        // Optionally, you might want to keep the error state active for a moment
        isErrorState = true; 
        lastTriggerTime = millis(); // Reset cooldown timer on error detection
        return; // Stop processing normal logic
    }


    // 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 THE MOVEMENT FLAG AND ENFORCE COOLDOWN (NORMAL 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
    isErrorState = false; // Reset error state flag


    // --- 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); 
}
```

### Key Changes Explained:

1.  **`setAllLedsColor(int r, int g, int b)` Function:**
    *   I added a helper function to encapsulate the logic of setting every pixel to one color. This keeps `OnDataRecv` clean.

2.  **Error State Flag (`isErrorState`):**
    *   A boolean flag was introduced to manage the system's state. When an error occurs, we set this flag to `true`.

3.  **Checking for "TOO FAST" in `OnDataRecv`:**
    *   At the very beginning of `OnDataRecv`, I added a check:
        ```cpp
        const char* errorString = "TOO FAST";
        if (strcmp(myData.a, errorString) == 0) {
            // ... logic to turn all red ...
            setAllLedsColor(255, 0, 0); // Sets all pixels to Red
            isErrorState = true;       // Set the flag
            lastTriggerTime = millis(); // Reset timer on error
            return;                    // Exit immediately after handling the error
        }
        ```
    *   `strcmp()` is used because `myData.a` is a C-style character array (`char[]`). This checks if the received string matches `"TOO FAST"`.

4.  **Integrating Error State into Normal Logic:**
    *   I modified `activateNextPixel()` to check `if (isErrorState)` at the beginning. If it's true, it prevents the normal pixel cycling from happening until some other mechanism resets this flag (you might need to add a separate "Reset" message handler later if you want the lights to return to normal).

### How to Test This:

1.  **Sender Side:** Ensure your sending ESP32 sends the status text `"TOO FAST"` in the `a` field when it detects an error condition.
2.  **Receiver Side (This Code):** When this code receives a packet where `myData.a` is exactly `"TOO FAST"`, all 10 NeoPixels will immediately turn solid red, regardless of what the movement flag (`d`) or cooldown status was doing previously.

### User
will the neopixels resume the normal movement count afterward?

### Assistant
That's a crucial point regarding state management!

**In the current implementation, no, the NeoPixels will NOT automatically resume the normal movement count after an error.**

Here is why, and how we can fix it to make them resume correctly:

### Why It Doesn't Resume Automatically (The Problem)

1.  When `"TOO FAST"` is received, we set `isErrorState = true` and turn all lights red.
2.  We also reset the cooldown timer (`lastTriggerTime = millis();`). This means that if another valid movement signal arrives *after* the error, it will think enough time has passed to trigger a cycle.
3.  However, because `isErrorState` is `true`, the `activateNextPixel()` function explicitly checks this flag and **returns immediately**, preventing the normal pixel counting logic from running.

### Solution: Implementing a "Reset" Mechanism

To make the system resume normally after an error, you need one of two things:

1.  **A specific "RESET" message:** The sender sends a special packet (e.g., `a` = `"SYSTEM_OK"`).
2.  **A time-based reset:** After a set period following the error, the system automatically clears the error state.

I recommend implementing **Option 1 (A Reset Message)** as it gives you explicit control over when the system is ready for normal operation.

### Code Modifications Needed:

We need to add logic in `OnDataRecv` to check for a "RESET" message and then reset both the visual state *and

### User
right now, when there is movement detected, the lights turn on in order. first neopixel 0, then 1, then 2, then 3. whan i move to fast, i want all current neopixels that are on to turn red, and then change back to the most recent on/off state

if neopixels 0,1, 2 were on, then 0,1,2 turn red, then if the next moement is normal, 0,1,2 turn back to their original color, and then neopixel 4 turns on

### Assistant
This is a much more complex state management requirement! You are asking for **state preservation** across different operational modes (Normal Cycle vs. Error State).

To achieve this, we need to store the *intended* color/state of every pixel when the normal cycle was last running successfully.

Here is the revised code incorporating:
1.  Storing the "last known good state" for all pixels.
2.  Handling the `"TOO FAST"` error by coloring only the currently lit pixels red, while preserving their *positional* data.
3.  Handling a hypothetical **`"SYSTEM_OK"`** message to clear the error and restore the saved state.

### Revised Code (Implementing State Preservation)

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

// Structure definition (MUST MATCH THE SENDER)
typedef struct struct_message {
    char a[32]; // For status text (e.g., "TOO FAST", "SYSTEM_OK")
    int b;       // Integer 
    float c;     // Float
    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;

// *** STATE MANAGEMENT VARIABLES ***
unsigned long lastTriggerTime = 0;
const unsigned long COOLDOWN_MS = 5000; // 5 seconds cooldown period

// Array to store the color that *should* be on each pixel when in normal mode.
uint32_t savedPixelColors[LED_COUNT]; 

// State tracking: True if we are currently in an error state (red alert)
bool isErrorState = false;


//=============================================================
// FUNCTION DEFINITIONS
//=============================================================

/**
 * @brief Sets ALL NeoPixels to a specified color and updates the strip.
 */
void setAllLedsColor(int r, int g, int b) {
    for (int i = 0; i &lt; LED_COUNT; i++) {
        strip.setPixelColor(i, strip.Color(r, g, b));
    }
    strip.show(); // Update the physical LEDs
}

/**
 * @brief Resets all pixels to a known safe state (e.g., off or default color).
 */
void resetLeds() {
    setAllLedsColor(0, 0, 0); // Turn everything off
    strip.show();
}


/**
 * @brief Handles lighting the next pixel and cycling the state when capacity is reached.
 * This function now respects the saved state if an error occurred previously.
 */
void activateNextPixel() {
    if (isErrorState) {
        Serial.println("Cannot activate pixel sequence: Currently in Error State.");
        return;
    }

    // 1. Check if we need to reset (Cycle condition)
    if (activePixelCount &gt;= LED_COUNT) {
        Serial.println("--- CYCLE COMPLETE! Resetting all pixels for next cycle. ---");
        resetLeds(); // Turn ALL lights off and clear the state tracker
        activePixelCount = 0; 
        // Note: We don't need to explicitly save colors here, as they are now all zeroed out.
    }

    // 2. Light up the current pixel (This runs whether we just reset or not)
    if (activePixelCount &lt; LED_COUNT) {
        int current_index = activePixelCount; // Use index directly for clarity
        
        // --- COLOR MAPPING START HERE ---
        uint32_t color;

        // Determine the color based on the index (0 to 9)
        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 {
             color = strip.Color(255, 0, 0); 
        }

        // --- STATE SAVING & LIGHTING ---
        
        // 1. Save the color *before* we overwrite it (This is the "normal" state)
        savedPixelColors[current_index] = color; 
        
        // 2. Light up the pixel
        strip.setPixelColor(current_index, color);
        strip.show(); // Update the physical LEDs

        activePixelCount++;
    }
}


/**
 * @brief Handles the "TOO FAST" error state: Turns currently lit pixels red, preserves position data.
 */
void handleTooFastError() {
    Serial.println("\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
    Serial.println("!!! RECEIVED 'TOO FAST' - RED ALERT !!!");
    Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");

    // 1. Turn ALL currently lit pixels red, but only up to the current active count.
    for (int i = 0; i &lt; activePixelCount; i++) {
        strip.setPixelColor(i, strip.Color(255, 0, 0)); // Red
    }
    // Pixels beyond 'activePixelCount' remain whatever color they were before the cycle started (or off).
    strip.show();

    // 2. Update state variables to reflect the error
    isErrorState = true;
    lastTriggerTime = millis(); // Reset cooldown timer on error detection
}


/**
 * @brief Resets the system from an error state back to normal operation.
 */
void handleSystemOk() {
    Serial.println("\n*** SYSTEM OK RECEIVED: Clearing Error State and Restoring Normal Operation ***");
    isErrorState = false;

    // 1. Restore all pixels to their last known good color (saved in savedPixelColors)
    for (int i = 0; i &lt; LED_COUNT; i++) {
        strip.setPixelColor(i, savedPixelColors[i]);
    }
    strip.show();

    // 2. Reset the cycle counter and timer to allow immediate normal operation
    activePixelCount = 0;
    lastTriggerTime = millis(); // Set time so the next movement can trigger immediately if needed
}


/**
 * @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();

    // --- STATE MACHINE LOGIC ---

    // A. Check for Error/Reset Commands FIRST
    const char* errorString = "TOO FAST";
    const char* okString = "SYSTEM_OK";

    if (strcmp(myData.a, errorString) == 0) {
        handleTooFastError();
        return; // Stop processing normal logic
    }
    
    if (strcmp(myData.a, okString) == 0) {
        handleSystemOk();
        // We don't return here because the system is now ready for movement detection below.
    }


    // B. Handle Normal Movement Detection (Only if not in an error state)
    if (!isErrorState && myData.d == true) {
        unsigned long currentTime = millis();
        
        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 and saving its color
            lastTriggerTime = currentTime; // Update the timer ONLY when action is taken
        } else {
            Serial.printf("Movement detected, BUT ignored. Still in cooldown period (%.1f seconds remaining).\n", 
                             (float)(COOLDOWN_MS - (millis() - lastTriggerTime)) / 1000.0);
        }
    } else if (!isErrorState && myData.d == false) {
         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();          
    resetLeds();            // Turn everything off initially
    strip.setBrightness(50); 

    // Initialize state variables
    lastTriggerTime = 0; 
    isErrorState = false;
    
    // Initialize saved colors to OFF (Black)
    for(int i=0; i &lt; LED_COUNT; i++) {
        savedPixelColors[i] = strip.Color(0, 0, 0);
    }

    // --- 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() {
    delay(20); 
}
```

### How the New Logic Works (The State Machine)

1.  **Initialization:** All `savedPixelColors` are set to black (`0, 0, 0`). The system is in a normal state (`isErrorState = false`).
2.  **Normal Cycle (`activateNextPixel`):**
    *   When Pixel $N$ lights up, we **first save its color** into `savedPixelColors[N]`.
    *   Then, we light it up using the calculated color.
3.  **Error Trigger (`"TOO FAST"` received):**
    *   `handleTooFastError()` runs.
    *   It iterates from pixel 0 up to `activePixelCount - 1` (only lighting pixels that were previously lit).
    *   It forces all these currently lit pixels to **Red**.
    *   Crucially, it sets `isErrorState = true`. The normal cycle logic is now blocked.
4.  **Recovery Trigger (`"SYSTEM_OK"` received):**
    *   `handleSystemOk()` runs.
    *   It iterates through *all* pixels (0 to 9). For each pixel, it forces the LED color to whatever was stored in `savedPixelColors[i]`. This restores the visual state perfectly.
    *   It resets `isErrorState = false` and clears the cycle counter (`activePixelCount = 0`), allowing the next movement detection to start a fresh, normal count sequence.

**To make this fully functional, your sender must now send three types of messages:**

1.  **Movement:** (e.g., `a="MOVE"`, `d=true`) $\rightarrow$ Triggers cycle/counting.
2.  **Error:** (`a="TOO FAST"`) $\rightarrow$ Turns lit pixels red and pauses counting.
3.  **Reset:** (`a="SYSTEM_OK"`) $\rightarrow$ Clears the error, restores colors, and resets the counter for normal operation.
