takeone-reflex-pod/new-update
2026-03-24 19:37:21 +03:00

894 lines
27 KiB
Plaintext

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 <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include <Adafruit_NeoPixel.h>
#include <Preferences.h>
// ==========================================
// ⚙️ 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! 🎨💪