/* * LED-Laser-Frequenzsteuerung für Rotlichttherapie * Ziel-Hardware: ESP32-C6 mit WS2812B LED-Streifen (30 LEDs) * * Funktion: Steuert 30 WS2812B-LEDs über verschiedene Frequenz-Presets * die Rotlichttherapie nachbilden. Jedes Preset definiert Farbe, Helligkeit * und Pulsfrequenz für photobiostimulation. */ #include // ===== KONFIGURATION ===== #define LED_PIN GPIO6 // Datenleitung zum WS2812B-Streifen #define NUM_LEDS 30 // Anzahl LEDs im Streifen #define DATA_RESISTOR 330 // Vorwiderstand in Ohm an der Datenleitung // ===== FREQUENZ-PRESETS ===== // Jeder Preset definiert: Name, Grundfrequenz (Hz), Farbe (R,G,B), Helligkeit (0-255) struct LaserPreset { const char* name; float frequencyHz; // Puls-Frequenz in Hz uint8_t colorR; uint8_t colorG; uint8_t colorB; uint8_t brightness; // 0-255, 255 = maximal uint32_t durationMs; // Wie lange das Preset läuft (0 = unbegrenzt) }; // Therapeutische Frequenzen für Rotlichttherapie (Peptide / Laser Therapy) // Diese Frequenzen sind typisch für die Behandlung von Gewebe/Regeneration const LaserPreset presets[] = { // [0] Niedrigfrequenz - Entspannung & Regeneration {"Regeneration", 40.0, 255, 15, 0, 200, 60000}, // [1] Mittelfrequenz - Durchblutung {"Durchblutung", 60.0, 255, 30, 0, 255, 60000}, // [2] Hochfrequenz - Tiefenwirksam {"Tiefenwirkung", 80.0, 255, 60, 0, 230, 45000}, // [3] Gepulst Rot - Geweberegeneration {"Gepulst Rot", 50.0, 255, 0, 0, 180, 60000}, // [4] Warmes Rot-Orange - Oberflächliche Heilung {"Oberflächenlicht", 30.0, 255, 80, 10, 210, 45000}, // [5] Infrarot-Nachbildung (tiefes Rot) - Muskeln & Gelenke {"Muskeln/Gelenke", 65.0, 200, 10, 0, 240, 60000}, // [6] Wechselnd - Ganzkörperbehandlung {"Ganzkörper", 45.0, 255, 0, 0, 190, 90000}, }; #define NUM_PRESETS (sizeof(presets) / sizeof(presets[0])) // ===== GLOBAL STATE ===== Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800); uint8_t currentR = 0, currentG = 0, currentB = 0; float currentBrightness = 0.0; uint32_t pulsePhase = 0; unsigned long lastPulseUpdate = 0; unsigned long presetStartTime = 0; int currentPresetIndex = -1; bool running = false; // ===== SETUP ===== void setup() { Serial.begin(115200); while (!Serial && millis() < 3000) {} strip.begin(); strip.clear(); strip.show(); // Starte mit erstem Preset selectPreset(0); Serial.println("========================================"); Serial.println(" LED-Laser-Frequenzsteuerung (ESP32-C6)"); Serial.println(" WS2812B x 30 LEDs"); Serial.println("========================================"); printPresets(); running = true; presetStartTime = millis(); } // ===== LOOP ===== void loop() { if (!running) { // Warte auf seriellen Befehl zum Neustart if (Serial.available()) { String cmd = Serial.readStringUntil('\n'); handleCommand(cmd); } yield(); return; } const LaserPreset& preset = presets[currentPresetIndex]; unsigned long now = millis(); // Prüfe ob Preset-Zeit abgelaufen (wenn durationMs > 0) if (preset.durationMs > 0 && (now - presetStartTime) >= preset.durationMs) { nextPreset(); } // Berechne pulsende Helligkeit basierend auf Frequenz calculatePulse(preset, now); // Update alle LEDs uint32_t color = strip.Color( (uint8_t)(currentR * currentBrightness / 255.0), (uint8_t)(currentG * currentBrightness / 255.0), (uint8_t)(currentB * currentBrightness / 255.0) ); for (int i = 0; i < NUM_LEDS; i++) { strip.setPixelColor(i, color); } strip.show(); // Seriellen Input prüfen für Befehle if (Serial.available()) { String cmd = Serial.readStringUntil('\n'); handleCommand(cmd.trim()); } yield(); } // ===== PULSBERECHNUNG ===== void calculatePulse(const LaserPreset& preset, unsigned long now) { float dutyCycle = 0.5; // Standard: 50% Tastgrad // Sinusbasiertes Pulsieren für sanfte Übergänge float periodMs = 1000.0 / preset.frequencyHz; float phase = fmodf((float)(now % (uint32_t)periodMs), periodMs) / periodMs; // Sanfter An- und Abstieg currentBrightness = (uint8_t)(preset.brightness * sinf(phase * 3.14159f)); } // ===== PRESET SELEKTION ===== void selectPreset(int index) { if (index < 0 || index >= NUM_PRESETS) return; currentPresetIndex = index; presetStartTime = millis(); currentBrightness = 0.0; Serial.printf("\n>>> Preset: %s\n", presets[index].name); Serial.printf(" Frequenz: %.1f Hz\n", presets[index].frequencyHz); Serial.printf(" Farbe: R=%d, G=%d, B=%d\n", presets[index].colorR, presets[index].colorG, presets[index].colorB); Serial.printf(" Helligkeit: %d/255\n", presets[index].brightness); if (presets[index].durationMs > 0) { Serial.printf(" Dauer: %lu ms (%.1f min)\n", presets[index].durationMs, presets[index].durationMs / 60000.0); } else { Serial.println(" Dauer: unbegrenzt (⏹ zum Stoppen)"); } } void nextPreset() { int next = (currentPresetIndex + 1) % NUM_PRESETS; selectPreset(next); } // ===== PRESETS DRUCKEN ===== void printPresets() { Serial.println("\nVerfügbare Presets:"); for (int i = 0; i < NUM_PRESETS; i++) { const char* durStr; if (presets[i].durationMs > 0) { durStr = ""; } else { durStr = " (⏹ stopp)"; } Serial.printf(" [%d] %s %.1fHz R:%d G:%d B:%d hell:%d - Zeit: %s\n", i, presets[i].name, presets[i].frequencyHz, presets[i].colorR, presets[i].colorG, presets[i].colorB, presets[i].brightness, durStr); } } // ===== SERIELLE BEFEHLE ===== void handleCommand(String cmd) { if (cmd.equalsIgnoreCase("stop")) { running = false; strip.clear(); strip.show(); Serial.println("LEDs ausgeschaltet. Serieller Befehl zum Neustart."); return; } if (cmd.equalsIgnoreCase("next")) { nextPreset(); return; } if (cmd.equalsIgnoreCase("list") || cmd.equalsIgnoreCase("presets")) { printPresets(); return; } // Number parsing: "0", "1", etc. → Preset-Index int idx = cmd.toInt(); if (idx >= 0 && idx < NUM_PRESETS) { selectPreset(idx); } else if (cmd.length() > 0 && !cmd[0].isdigit()) { Serial.printf("Unbekannter Befehl: '%s'\n", cmd.c_str()); Serial.println("Verfügbare Befehle: 0-6, stop, next, list"); } }