Merge origin/main, keep esp-idf setup
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* 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 <Adafruit_NeoPixel.h>
|
||||
|
||||
// ===== 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");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user