From f453e308fc1114b90fd82020ce0c04f96893d692 Mon Sep 17 00:00:00 2001 From: Ghassan Yusuf Date: Tue, 24 Mar 2026 19:37:21 +0300 Subject: [PATCH] Add new-update --- new-update | 894 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 894 insertions(+) create mode 100644 new-update diff --git a/new-update b/new-update new file mode 100644 index 0000000..c5a33b0 --- /dev/null +++ b/new-update @@ -0,0 +1,894 @@ +Perfect! Let's update the project to **TAKEONE Reflex** and add advanced color management for different game scenarios. + +## ✅ 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! 🎨💪 \ No newline at end of file