## ✅ Updated Code with Advanced Color Features ```cpp #include #include #include #include #include #include // ========================================== // ⚙️ USER CONFIGURATION (MODULAR) // ========================================== #define DEFAULT_DEVICE_NAME "TAKEONE_Reflex" #define PROJECT_NAME "TAKEONE Reflex" #define FIRMWARE_VERSION "1.0.0" // --- Feature Toggles (Comment to disable) --- #define FEATURE_PIEZO #define FEATURE_BUTTON #define FEATURE_ACCEL // --- Pin Assignments (ESP32-C3 Super Mini) --- #define LED_PIN 3 #ifdef FEATURE_PIEZO #define PIEZO_PIN 4 #define PIEZO_THRESHOLD 1500 #endif #ifdef FEATURE_BUTTON #define BUTTON_PIN 5 #define BUTTON_THRESHOLD 100 #endif #ifdef FEATURE_ACCEL #define ACCEL_X_PIN 6 #define ACCEL_Y_PIN 7 #define ACCEL_Z_PIN 0 #define ACCEL_THRESHOLD 150 #endif #define NUM_PIXELS 12 #define BRIGHTNESS 50 #define BREATHE_MAX 40 #define MAX_PRESETS 8 // Number of color presets #define MAX_SEQUENCE_STEPS 16 // Max colors in sequence // ========================================== // --- BLE UUIDs --- #define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" #define DEVICE_INFO_SERVICE_UUID "180A" #define MODEL_NUMBER_CHAR_UUID "2A24" #define SERIAL_NUMBER_CHAR_UUID "2A25" #define FIRMWARE_REV_CHAR_UUID "2A26" #define MANUFACTURER_CHAR_UUID "2A29" // --- Pod States --- enum PodState { STATE_BOOT, STATE_WAITING, STATE_CONNECTED, STATE_ACTIVE, STATE_SUCCESS, STATE_SEQUENCE // For playing color sequences }; // --- Color Structure --- struct LEDColor { uint8_t r, g, b; }; // --- Default Colors --- LEDColor colorWait = {0, 0, 255}; // Blue (breathing) LEDColor colorConn = {0, 255, 255}; // Cyan LEDColor colorActive = {255, 0, 0}; // Red LEDColor colorSuccess = {0, 255, 0}; // Green // --- Color Presets (saved in flash) --- LEDColor colorPresets[MAX_PRESETS]; // --- Color Sequence --- LEDColor colorSequence[MAX_SEQUENCE_STEPS]; uint8_t sequenceLength = 0; uint8_t currentStep = 0; unsigned long lastStepTime = 0; unsigned long stepDuration = 500; // ms per step // --- Globals --- Preferences preferences; char deviceName[16] = DEFAULT_DEVICE_NAME; Adafruit_NeoPixel strip(NUM_PIXELS, LED_PIN, NEO_GRB + NEO_KHZ800); BLEServer *pServer = NULL; BLECharacteristic *pCharacteristic = NULL; bool deviceConnected = false; bool oldDeviceConnected = false; PodState currentState = STATE_BOOT; unsigned long stateStartTime = 0; unsigned long lastAnimTime = 0; int animStep = 0; unsigned long startTime = 0; unsigned long reactionTime = 0; bool gameActive = false; bool isTapped = false; int baseX = 0, baseY = 0, baseZ = 0; bool accelCalibrated = false; // --- Helper: Create Color --- uint32_t makeColor(LEDColor c) { return strip.Color(c.r, c.g, c.b); } // --- Helper: Set All LEDs --- void fillStrip(uint32_t color) { for (int i = 0; i < NUM_PIXELS; i++) { strip.setPixelColor(i, color); } strip.show(); } // --- Helper: Parse Color from String "R,G,B" --- LEDColor parseColor(String str) { LEDColor c = {0, 0, 0}; int comma1 = str.indexOf(','); int comma2 = str.lastIndexOf(','); if (comma1 > 0 && comma2 > comma1) { c.r = str.substring(0, comma1).toInt(); c.g = str.substring(comma1 + 1, comma2).toInt(); c.b = str.substring(comma2 + 1).toInt(); } return c; } // --- Helper: Format Color to String --- String formatColor(LEDColor c) { return String(c.r) + "," + String(c.g) + "," + String(c.b); } // --- Save/Load Functions --- void saveDeviceName(const char* name) { preferences.begin("takeone", false); preferences.putString("deviceName", name); preferences.end(); strncpy(deviceName, name, 15); deviceName[15] = '\0'; } void loadDeviceName() { preferences.begin("takeone", false); String savedName = preferences.getString("deviceName", DEFAULT_DEVICE_NAME); preferences.end(); strncpy(deviceName, savedName.c_str(), 15); deviceName[15] = '\0'; } void saveColorPreset(uint8_t index, LEDColor color) { preferences.begin("takeone", false); String key = "preset" + String(index); String value = formatColor(color); preferences.putString(key.c_str(), value); preferences.end(); colorPresets[index] = color; } void loadColorPresets() { preferences.begin("takeone", false); for (int i = 0; i < MAX_PRESETS; i++) { String key = "preset" + String(i); String saved = preferences.getString(key.c_str(), ""); if (saved.length() > 0) { colorPresets[i] = parseColor(saved); } else { // Default presets switch(i) { case 0: colorPresets[i] = {255, 0, 0}; break; // Red case 1: colorPresets[i] = {0, 255, 0}; break; // Green case 2: colorPresets[i] = {0, 0, 255}; break; // Blue case 3: colorPresets[i] = {255, 255, 0}; break; // Yellow case 4: colorPresets[i] = {255, 0, 255}; break; // Magenta case 5: colorPresets[i] = {0, 255, 255}; break; // Cyan case 6: colorPresets[i] = {255, 128, 0}; break; // Orange case 7: colorPresets[i] = {128, 0, 255}; break; // Purple } } } preferences.end(); } void saveSequence() { preferences.begin("takeone", false); String seqStr = ""; for (int i = 0; i < sequenceLength; i++) { if (i > 0) seqStr += ";"; seqStr += formatColor(colorSequence[i]); } preferences.putString("sequence", seqStr); preferences.putUInt("seqDuration", stepDuration); preferences.end(); } void loadSequence() { preferences.begin("takeone", false); String saved = preferences.getString("sequence", ""); stepDuration = preferences.getUInt("seqDuration", 500); sequenceLength = 0; if (saved.length() > 0) { int startIndex = 0; while (startIndex < saved.length() && sequenceLength < MAX_SEQUENCE_STEPS) { int endIndex = saved.indexOf(';', startIndex); if (endIndex == -1) endIndex = saved.length(); String colorStr = saved.substring(startIndex, endIndex); colorSequence[sequenceLength++] = parseColor(colorStr); startIndex = endIndex + 1; } } preferences.end(); } // --- Play Next Sequence Step --- void playNextSequenceStep() { if (sequenceLength == 0) return; fillStrip(makeColor(colorSequence[currentStep])); currentStep = (currentStep + 1) % sequenceLength; lastStepTime = millis(); } // --- BLE Callbacks --- class MyServerCallbacks: public BLEServerCallbacks { void onConnect(BLEServer* pServer) { deviceConnected = true; currentState = STATE_CONNECTED; stateStartTime = millis(); Serial.println("Device Connected"); } void onDisconnect(BLEServer* pServer) { deviceConnected = false; currentState = STATE_WAITING; stateStartTime = millis(); Serial.println("Device Disconnected"); BLEDevice::startAdvertising(); } }; class MyCallbacks: public BLECharacteristicCallbacks { void onWrite(BLECharacteristic *pCharacteristic) { String value = pCharacteristic->getValue(); value.trim(); Serial.print("Received: ["); Serial.print(value); Serial.println("]"); // --- Device Name Commands --- if (value.startsWith("SET_DEVICE_NAME:")) { String newName = value.substring(16); newName.trim(); if (newName.length() > 0 && newName.length() <= 15) { saveDeviceName(newName.c_str()); BLEDevice::deinit(true); delay(100); BLEDevice::init(deviceName); BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); pAdvertising->addServiceUUID(SERVICE_UUID); pAdvertising->setScanResponse(true); BLEDevice::startAdvertising(); pCharacteristic->setValue("OK:DEVICE_NAME:" + String(deviceName)); pCharacteristic->notify(); } else { pCharacteristic->setValue("ERROR:NAME_INVALID"); pCharacteristic->notify(); } } else if (value == "GET_DEVICE_NAME") { pCharacteristic->setValue("DEVICE_NAME:" + String(deviceName)); pCharacteristic->notify(); } // --- Color Preset Commands --- else if (value.startsWith("SAVE_PRESET:")) { // Format: SAVE_PRESET:n:R,G,B int firstColon = value.indexOf(':'); int secondColon = value.indexOf(':', firstColon + 1); if (firstColon > 0 && secondColon > firstColon) { uint8_t index = value.substring(firstColon + 1, secondColon).toInt(); if (index < MAX_PRESETS) { LEDColor c = parseColor(value.substring(secondColon + 1)); saveColorPreset(index, c); pCharacteristic->setValue("OK:PRESET_SAVED:" + String(index)); pCharacteristic->notify(); } else { pCharacteristic->setValue("ERROR:PRESET_INDEX"); pCharacteristic->notify(); } } } else if (value.startsWith("LOAD_PRESET:")) { // Format: LOAD_PRESET:n int colon = value.indexOf(':'); if (colon > 0) { uint8_t index = value.substring(colon + 1).toInt(); if (index < MAX_PRESETS) { fillStrip(makeColor(colorPresets[index])); pCharacteristic->setValue("OK:PRESET_LOADED:" + String(index)); pCharacteristic->notify(); } else { pCharacteristic->setValue("ERROR:PRESET_INDEX"); pCharacteristic->notify(); } } } else if (value == "GET_PRESETS") { String response = "PRESETS|"; for (int i = 0; i < MAX_PRESETS; i++) { if (i > 0) response += "|"; response += String(i) + ":" + formatColor(colorPresets[i]); } pCharacteristic->setValue(response); pCharacteristic->notify(); } // --- Color Sequence Commands --- else if (value.startsWith("SET_SEQUENCE:")) { // Format: SET_SEQUENCE:R1,G1,B1;R2,G2,B2;R3,G3,B3 String seqData = value.substring(13); sequenceLength = 0; int startIndex = 0; while (startIndex < seqData.length() && sequenceLength < MAX_SEQUENCE_STEPS) { int endIndex = seqData.indexOf(';', startIndex); if (endIndex == -1) endIndex = seqData.length(); String colorStr = seqData.substring(startIndex, endIndex); colorSequence[sequenceLength++] = parseColor(colorStr); startIndex = endIndex + 1; } saveSequence(); pCharacteristic->setValue("OK:SEQUENCE_SET:" + String(sequenceLength)); pCharacteristic->notify(); } else if (value.startsWith("SET_STEP_DURATION:")) { // Format: SET_STEP_DURATION:500 (milliseconds) int colon = value.indexOf(':'); if (colon > 0) { stepDuration = value.substring(colon + 1).toInt(); saveSequence(); pCharacteristic->setValue("OK:DURATION:" + String(stepDuration)); pCharacteristic->notify(); } } else if (value == "PLAY_SEQUENCE") { if (sequenceLength > 0) { currentState = STATE_SEQUENCE; currentStep = 0; playNextSequenceStep(); pCharacteristic->setValue("OK:SEQUENCE_PLAYING"); pCharacteristic->notify(); } else { pCharacteristic->setValue("ERROR:NO_SEQUENCE"); pCharacteristic->notify(); } } else if (value == "STOP_SEQUENCE") { currentState = STATE_CONNECTED; fillStrip(makeColor(colorConn)); pCharacteristic->setValue("OK:SEQUENCE_STOPPED"); pCharacteristic->notify(); } // --- Standard Color Commands --- else if (value.startsWith("COLOR_WAIT:")) { colorWait = parseColor(value.substring(11)); pCharacteristic->setValue("OK:COLOR_WAIT:" + formatColor(colorWait)); pCharacteristic->notify(); } else if (value.startsWith("COLOR_CONN:")) { colorConn = parseColor(value.substring(11)); pCharacteristic->setValue("OK:COLOR_CONN:" + formatColor(colorConn)); pCharacteristic->notify(); } else if (value.startsWith("COLOR_ACTIVE:")) { colorActive = parseColor(value.substring(13)); pCharacteristic->setValue("OK:COLOR_ACTIVE:" + formatColor(colorActive)); pCharacteristic->notify(); } else if (value.startsWith("COLOR_SUCCESS:")) { colorSuccess = parseColor(value.substring(14)); pCharacteristic->setValue("OK:COLOR_SUCCESS:" + formatColor(colorSuccess)); pCharacteristic->notify(); } else if (value == "GET_COLORS") { String response = "COLORS|WAIT:" + formatColor(colorWait) + "|CONN:" + formatColor(colorConn) + "|ACTIVE:" + formatColor(colorActive) + "|SUCCESS:" + formatColor(colorSuccess); pCharacteristic->setValue(response); pCharacteristic->notify(); } else if (value == "RESET_COLORS") { colorWait = {0, 0, 255}; colorConn = {0, 255, 255}; colorActive = {255, 0, 0}; colorSuccess = {0, 255, 0}; pCharacteristic->setValue("OK:COLORS_RESET"); pCharacteristic->notify(); } // --- Game Commands --- else if (value == "START") { currentState = STATE_ACTIVE; fillStrip(makeColor(colorActive)); startTime = micros(); gameActive = true; isTapped = false; accelCalibrated = false; pCharacteristic->setValue("OK:STARTED"); pCharacteristic->notify(); Serial.println("Game Started"); } else if (value == "RESET") { currentState = STATE_CONNECTED; fillStrip(makeColor(colorConn)); gameActive = false; pCharacteristic->setValue("OK:RESET"); pCharacteristic->notify(); Serial.println("Game Reset"); } // --- Device Info --- else if (value == "GET_DEVICE_INFO") { String response = "INFO|NAME:" + String(deviceName) + "|MODEL:TAKEONE-Reflex" + "|FW:" + String(FIRMWARE_VERSION) + "|MANUFACTURER:DIY" + "|SENSORS:" + String(defined(FEATURE_PIEZO) ? "P" : "-") + String(defined(FEATURE_BUTTON) ? "B" : "-") + String(defined(FEATURE_ACCEL) ? "A" : "-"); pCharacteristic->setValue(response); pCharacteristic->notify(); } else if (value == "FACTORY_RESET") { preferences.begin("takeone", false); preferences.clear(); preferences.end(); strncpy(deviceName, DEFAULT_DEVICE_NAME, 15); loadColorPresets(); BLEDevice::deinit(true); delay(100); BLEDevice::init(deviceName); BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); pAdvertising->addServiceUUID(SERVICE_UUID); pAdvertising->setScanResponse(true); BLEDevice::startAdvertising(); pCharacteristic->setValue("OK:FACTORY_RESET"); pCharacteristic->notify(); Serial.println("Factory reset complete"); } else { pCharacteristic->setValue("ERROR:UNKNOWN_COMMAND"); pCharacteristic->notify(); } } }; // --- Calibrate Accelerometer --- void calibrateAccel() { #ifdef FEATURE_ACCEL if (!accelCalibrated) { baseX = analogRead(ACCEL_X_PIN); baseY = analogRead(ACCEL_Y_PIN); baseZ = analogRead(ACCEL_Z_PIN); accelCalibrated = true; } #endif } // --- Check All Enabled Triggers --- void checkTriggers() { if (!gameActive || !deviceConnected || isTapped) return; bool triggered = false; #ifdef FEATURE_PIEZO int piezoValue = analogRead(PIEZO_PIN); if (piezoValue > PIEZO_THRESHOLD) { triggered = true; } #endif #ifdef FEATURE_BUTTON #if !defined(FEATURE_PIEZO) || (BUTTON_PIN != PIEZO_PIN) if (digitalRead(BUTTON_PIN) == LOW) { triggered = true; } #endif #endif #ifdef FEATURE_ACCEL if (accelCalibrated && !triggered) { int curX = analogRead(ACCEL_X_PIN); int curY = analogRead(ACCEL_Y_PIN); int curZ = analogRead(ACCEL_Z_PIN); int diffX = abs(curX - baseX); int diffY = abs(curY - baseY); int diffZ = abs(curZ - baseZ); if (diffX > ACCEL_THRESHOLD || diffY > ACCEL_THRESHOLD || diffZ > ACCEL_THRESHOLD) { triggered = true; } } #endif if (triggered) { isTapped = true; unsigned long endTime = micros(); reactionTime = (endTime - startTime) / 1000; String result = "RESULT:" + String(reactionTime) + "ms"; pCharacteristic->setValue(result); pCharacteristic->notify(); currentState = STATE_SUCCESS; stateStartTime = millis(); fillStrip(makeColor(colorSuccess)); gameActive = false; } } // --- Reset Trigger After Settle --- void resetTrigger() { if (isTapped) { bool settled = false; #ifdef FEATURE_PIEZO int piezoValue = analogRead(PIEZO_PIN); if (piezoValue < (PIEZO_THRESHOLD / 2)) settled = true; #elif defined(FEATURE_BUTTON) if (digitalRead(BUTTON_PIN) == HIGH) settled = true; #else if (millis() - stateStartTime > 2000) settled = true; #endif if (settled) { isTapped = false; accelCalibrated = false; } } } // --- Smart Animation Engine --- void handleAnimations() { unsigned long now = millis(); if (currentState == STATE_BOOT) { if (now - stateStartTime < 2000) { if (now - lastAnimTime > 50) { for (int i = 0; i < NUM_PIXELS; i++) { uint32_t color = strip.ColorHSV(i * 255 / NUM_PIXELS + animStep * 10, 255, BRIGHTNESS); strip.setPixelColor(i, color); } strip.show(); animStep++; lastAnimTime = now; } } else { currentState = STATE_WAITING; stateStartTime = now; fillStrip(0); } } else if (currentState == STATE_WAITING) { if (now - lastAnimTime > 10) { float breath = sin(now / 800.0); float normalized = (breath + 1.0) / 2.0; float gammaCorrected = pow(normalized, 2.2); int brightness = (int)(gammaCorrected * BREATHE_MAX); uint32_t color = strip.Color(colorWait.r * brightness / BREATHE_MAX, colorWait.g * brightness / BREATHE_MAX, colorWait.b * brightness / BREATHE_MAX); fillStrip(color); lastAnimTime = now; } } else if (currentState == STATE_CONNECTED) { if (now - lastAnimTime > 500) { fillStrip(makeColor(colorConn)); lastAnimTime = now; } } else if (currentState == STATE_ACTIVE) { if (now - lastAnimTime > 500) { fillStrip(makeColor(colorActive)); lastAnimTime = now; } } else if (currentState == STATE_SEQUENCE) { if (now - lastStepTime >= stepDuration) { playNextSequenceStep(); } } else if (currentState == STATE_SUCCESS) { if (now - stateStartTime > 2000) { currentState = STATE_CONNECTED; fillStrip(makeColor(colorConn)); } } } void setup() { Serial.begin(115200); while (!Serial); loadDeviceName(); loadColorPresets(); loadSequence(); Serial.println("================================"); Serial.println(" " + String(PROJECT_NAME)); Serial.println(" Firmware v" + String(FIRMWARE_VERSION)); Serial.println("================================"); Serial.print("Device Name: "); Serial.println(deviceName); Serial.print("Piezo: "); Serial.println(defined(FEATURE_PIEZO) ? "ON" : "OFF"); Serial.print("Button: "); Serial.println(defined(FEATURE_BUTTON) ? "ON" : "OFF"); Serial.print("Accel: "); Serial.println(defined(FEATURE_ACCEL) ? "ON" : "OFF"); Serial.println("================================"); pinMode(LED_PIN, OUTPUT); #ifdef FEATURE_PIEZO pinMode(PIEZO_PIN, INPUT); #endif #ifdef FEATURE_BUTTON pinMode(BUTTON_PIN, INPUT_PULLUP); #endif #ifdef FEATURE_ACCEL pinMode(ACCEL_X_PIN, INPUT); pinMode(ACCEL_Y_PIN, INPUT); pinMode(ACCEL_Z_PIN, INPUT); #endif analogReadResolution(12); strip.begin(); strip.setBrightness(BRIGHTNESS); strip.show(); fillStrip(0); currentState = STATE_BOOT; stateStartTime = millis(); BLEDevice::init(deviceName); pServer = BLEDevice::createServer(); pServer->setCallbacks(new MyServerCallbacks()); BLEService *pService = pServer->createService(SERVICE_UUID); pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_NOTIFY ); pCharacteristic->setCallbacks(new MyCallbacks()); pCharacteristic->addDescriptor(new BLE2902()); pService->start(); BLEService *pDeviceInfoService = pServer->createService(DEVICE_INFO_SERVICE_UUID); BLECharacteristic *pModelNumberChar = pDeviceInfoService->createCharacteristic( MODEL_NUMBER_CHAR_UUID, BLECharacteristic::PROPERTY_READ); pModelNumberChar->setValue("TAKEONE-Reflex"); BLECharacteristic *pSerialNumberChar = pDeviceInfoService->createCharacteristic( SERIAL_NUMBER_CHAR_UUID, BLECharacteristic::PROPERTY_READ); uint8_t baseMac[6]; esp_read_mac(baseMac, ESP_MAC_WIFI_STA); char macStr[18]; snprintf(macStr, sizeof(macStr), "%02X:%02X:%02X:%02X:%02X:%02X", baseMac[0], baseMac[1], baseMac[2], baseMac[3], baseMac[4], baseMac[5]); pSerialNumberChar->setValue(macStr); BLECharacteristic *pFirmwareRevChar = pDeviceInfoService->createCharacteristic( FIRMWARE_REV_CHAR_UUID, BLECharacteristic::PROPERTY_READ); pFirmwareRevChar->setValue(FIRMWARE_VERSION); BLECharacteristic *pManufacturerChar = pDeviceInfoService->createCharacteristic( MANUFACTURER_CHAR_UUID, BLECharacteristic::PROPERTY_READ); pManufacturerChar->setValue("DIY Reflex Pod"); pDeviceInfoService->start(); BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); pAdvertising->addServiceUUID(SERVICE_UUID); pAdvertising->addServiceUUID(DEVICE_INFO_SERVICE_UUID); pAdvertising->setScanResponse(true); pAdvertising->setMinPreferred(0x06); pAdvertising->setMinPreferred(0x12); BLEDevice::startAdvertising(); Serial.println("System Ready - Waiting for connection..."); Serial.print("Advertising as: "); Serial.println(deviceName); } void loop() { if (!deviceConnected && oldDeviceConnected) { delay(500); pServer->startAdvertising(); oldDeviceConnected = deviceConnected; } if (deviceConnected && !oldDeviceConnected) { oldDeviceConnected = deviceConnected; } handleAnimations(); if (gameActive && !accelCalibrated) { calibrateAccel(); } checkTriggers(); resetTrigger(); delay(10); } ``` --- ## 📱 Complete BLE Command Reference ### **Device Management** | Command | Description | Response | | :--- | :--- | :--- | | `SET_DEVICE_NAME:NewName` | Change device name (max 15 chars) | `OK:DEVICE_NAME:NewName` | | `GET_DEVICE_NAME` | Get current name | `DEVICE_NAME:TAKEONE_Reflex` | | `GET_DEVICE_INFO` | Get full device info | `INFO\|NAME:...\|MODEL:...\|...` | | `FACTORY_RESET` | Reset all settings | `OK:FACTORY_RESET` | ### **Standard Colors** | Command | Description | Example | | :--- | :--- | :--- | | `COLOR_WAIT:R,G,B` | Waiting/breathing color | `COLOR_WAIT:0,0,255` | | `COLOR_CONN:R,G,B` | Connected state color | `COLOR_CONN:0,255,255` | | `COLOR_ACTIVE:R,G,B` | Game active color | `COLOR_ACTIVE:255,0,0` | | `COLOR_SUCCESS:R,G,B` | Success/triggered color | `COLOR_SUCCESS:0,255,0` | | `GET_COLORS` | Get all colors | `COLORS\|WAIT:...\|CONN:...\|...` | | `RESET_COLORS` | Reset to defaults | `OK:COLORS_RESET` | ### **Color Presets (NEW!)** Save and recall up to **8 color presets** for different game scenarios: | Command | Description | Example | | :--- | :--- | :--- | | `SAVE_PRESET:n:R,G,B` | Save color to preset slot n (0-7) | `SAVE_PRESET:3:255,128,0` | | `LOAD_PRESET:n` | Load and apply preset n | `LOAD_PRESET:3` | | `GET_PRESETS` | Get all saved presets | `PRESETS\|0:255,0,0\|1:0,255,0\|...` | **Use Case**: - Preset 0: Red (stop/slap) - Preset 1: Green (go/jump) - Preset 2: Blue (squat) - Preset 3: Yellow (push-up) - etc. ### **Color Sequences (NEW!)** Create automatic color patterns for reaction drills: | Command | Description | Example | | :--- | :--- | :--- | | `SET_SEQUENCE:R1,G1,B1;R2,G2,B2;...` | Set sequence (up to 16 colors) | `SET_SEQUENCE:255,0,0;0,255,0;0,0,255` | | `SET_STEP_DURATION:ms` | Set duration per step | `SET_STEP_DURATION:1000` | | `PLAY_SEQUENCE` | Start playing sequence | `OK:SEQUENCE_PLAYING` | | `STOP_SEQUENCE` | Stop sequence | `OK:SEQUENCE_STOPPED` | **Use Case**: - Red → Green → Blue cycle for "follow the light" drills - Random color patterns for cognitive training - Timed sequences for interval training ### **Game Control** | Command | Description | Response | | :--- | :--- | :--- | | `START` | Start reaction test | `OK:STARTED` | | `RESET` | Stop game | `OK:RESET` | --- ## 🎮 Example Training Scenarios ### **Scenario 1: Color-Coded Actions** ``` # Setup different colors for different actions SAVE_PRESET:0:255,0,0 # Red = Slap SAVE_PRESET:1:0,255,0 # Green = Jump SAVE_PRESET:2:0,0,255 # Blue = Squat SAVE_PRESET:3:255,255,0 # Yellow = Push-up # During training, coach sends: LOAD_PRESET:0 # Athlete sees RED → Slaps pod LOAD_PRESET:1 # Athlete sees GREEN → Jumps LOAD_PRESET:2 # Athlete sees BLUE → Squats ``` ### **Scenario 2: Pattern Recognition** ``` # Create a sequence SET_SEQUENCE:255,0,0;0,255,0;0,0,255;255,255,0 SET_STEP_DURATION:800 PLAY_SEQUENCE # Pod cycles: Red → Green → Blue → Yellow → Red... # Athlete must perform action matching each color ``` ### **Scenario 3: Reaction Drill** ``` # Set custom active color (purple) COLOR_ACTIVE:255,0,255 # Start game START # Pod turns PURPLE # Athlete reacts # Result: RESULT:245ms ``` --- ## 📊 Default Color Presets | Slot | Color | RGB Value | Use Case | | :--- | :--- | :--- | :--- | | 0 | Red | 255,0,0 | Stop/Slap | | 1 | Green | 0,255,0 | Go/Jump | | 2 | Blue | 0,0,255 | Squat/Hold | | 3 | Yellow | 255,255,0 | Push-up | | 4 | Magenta | 255,0,255 | Burpee | | 5 | Cyan | 0,255,255 | Rest | | 6 | Orange | 255,128,0 | Lunge | | 7 | Purple | 128,0,255 | Plank | --- ## 🎯 Quick Start Guide 1. **Connect** to `TAKEONE_Reflex` 2. **Enable notifications** on characteristic 3. **Test basic function**: ``` START [Press button] → RESULT:XXXms ``` 4. **Set custom colors**: ``` SAVE_PRESET:0:255,0,0 SAVE_PRESET:1:0,255,0 LOAD_PRESET:0 → Red lights up LOAD_PRESET:1 → Green lights up ``` 5. **Create sequence**: ``` SET_SEQUENCE:255,0,0;0,255,0;0,0,255 SET_STEP_DURATION:1000 PLAY_SEQUENCE ``` --- This makes **TAKEONE Reflex** a professional-grade training tool with unlimited color combinations for different exercises! 🎨💪