Blaulicht

Manchmal braucht man ein blinkendes Rundumlicht. Sei es für einen grandiosen „roten“ Alarm, einen importierten Polizeieinsatz mit rot/blauem Rundumlicht oder dem gelben Blinken eines Baufahrzeuges. Hier sind zwei Modelle zu finden. Eines für den Tischeinsatz und eines für die exponierte Position auf einem EN32er Kabelrohr. Damit kann man sich das passende Modell für seine Anwendung zusammenstellen. Eine passende Soundausgabe fehlt noch, da die passenden Verstärker mit dem notwendigen Wums bei der Paketvergabe falsch abgebogen sind. Aber, das kann noch kommen. Aber auch so schon einmal viel Spaß beim Polizei spielen…

Und hier die Druckdateien für die Modelle. Dabei steht „Boden“ für das Tischmodell und „Boden2“ für die Version auf dem EN32 Kabelrohr.

Der folgende Quellcode ist noch nicht perfekt. Vor allem der Aufruf der einzelnen Farben ist noch mit unnötigen Doppellungen versehen. Und die HTML-Seiten wurden flux von ChatGPT (oder war es Claude) erstellt, was sich nicht unbedingt positiv gestaltet. Aber, es funktioniert schon einmal so…

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <Adafruit_NeoPixel.h>

#define PIN D1

int pause = 40;
int hell = 255; // 0 bis 100

// WiFi Zugangsdaten
const char* ssid = "Deine-SSID";
const char* password = "Dein-PW";
const char* hostname = "Blinklicht";

Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(32, 8, PIN,
NEO_MATRIX_TOP + NEO_MATRIX_LEFT +
NEO_MATRIX_COLUMNS + NEO_MATRIX_ZIGZAG,
NEO_GRB + NEO_KHZ800);

uint16_t color = matrix.Color(0, 0, 255);  // Rot

const uint16_t colors[] = {
  matrix.Color(255, 0, 0), matrix.Color(0, 255, 0), matrix.Color(0, 0, 255) 
};

int x = matrix.width();
int pass = 0;

// Webserver auf Port 80
ESP8266WebServer server(80);

// Variablen für die Werte
int sliderValue = 0;
int redValue = 100;
int greenValue = 100;
int blueValue = 100;
String textValue = "";
bool button1State = false;
bool button2State = false;

// HTML-Seite
const char* htmlPage = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Blaulicht Steuerung</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 600px;
            margin: 50px auto;
            padding: 20px;
            background-color: #f0f0f0;
        }
        .container {
            background: white;
            padding: 30px;
            border-radius: 10px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        h1 {
            color: #333;
            text-align: center;
            margin-bottom: 30px;
        }
        .control-group {
            margin: 20px 0;
            padding: 15px;
            border: 1px solid #ddd;
            border-radius: 5px;
            background-color: #fafafa;
        }
        label {
            display: block;
            margin-bottom: 10px;
            font-weight: bold;
            color: #555;
        }
        input[type="range"] {
            width: 100%;
            margin: 10px 0;
        }
        .rgb-inputs {
            display: flex;
            gap: 10px;
        }
        .rgb-inputs input {
            flex: 1;
            padding: 8px;
            border: 1px solid #ccc;
            border-radius: 4px;
            text-align: center;
        }
        input[type="text"] {
            width: 100%;
            padding: 10px;
            border: 1px solid #ccc;
            border-radius: 4px;
            font-size: 16px;
        }
        .button-group {
            display: flex;
            gap: 15px;
            justify-content: center;
        }
        button {
            padding: 12px 25px;
            font-size: 16px;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            transition: all 0.3s;
        }
        .btn-primary {
            background-color: #007bff;
            color: white;
        }
        .btn-primary:hover {
            background-color: #0056b3;
        }
        .btn-secondary {
            background-color: #6c757d;
            color: white;
        }
        .btn-secondary:hover {
            background-color: #545b62;
        }
        .btn-active {
            background-color: #28a745 !important;
        }
        .value-display {
            margin-top: 10px;
            padding: 8px;
            background-color: #e9ecef;
            border-radius: 4px;
            font-family: monospace;
        }
        .color-preview {
            width: 100%;
            height: 50px;
            border: 1px solid #ccc;
            border-radius: 4px;
            margin-top: 10px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>Blaulicht Steuerung</h1>
        
        <!-- Schieberegler -->
        <div class="control-group">
            <label for="slider">Auswahl (0-9):</label>
            <input type="range" id="slider" min="0" max="9" value="0" oninput="updateSlider(this.value)">
            <div class="value-display">Wert: <span id="sliderValue">0</span></div>
        </div>
        
        <!-- RGB Eingabe -->
        <div class="control-group">
            <label>RGB Farbe einstellen (0-255):</label>
            <div class="rgb-inputs">
                <input type="number" id="red" min="0" max="255" value="0" placeholder="Rot" oninput="updateColor()">
                <input type="number" id="green" min="0" max="255" value="0" placeholder="Grün" oninput="updateColor()">
                <input type="number" id="blue" min="0" max="255" value="0" placeholder="Blau" oninput="updateColor()">
            </div>
            <div class="color-preview" id="colorPreview"></div>
            <div class="value-display">RGB: <span id="rgbValue">0, 0, 0</span></div>
        </div>
        
        <!-- Text Eingabe -->
        <div class="control-group">
            <label for="textInput">Text Eingabe:</label>
            <input type="text" id="textInput" placeholder="Geben Sie hier Ihren Text ein..." oninput="updateText(this.value)">
            <div class="value-display">Text: <span id="textValue">""</span></div>
        </div>
        
        <!-- Buttons -->
        <div class="control-group">
            <label>Steuerungsbuttons:</label>
            <div class="button-group">
                <button id="button1" class="btn-primary" onclick="toggleButton(1)">Button 1</button>
                <button id="button2" class="btn-secondary" onclick="toggleButton(2)">Button 2</button>
            </div>
            <div class="value-display">
                Button 1: <span id="button1Value">false</span> | 
                Button 2: <span id="button2Value">false</span>
            </div>
        </div>
        
        <!-- Status Anzeige -->
        <div class="control-group">
            <label>Aktuelle Werte:</label>
            <div class="value-display" id="allValues">
                Slider: 0 | RGB: 0,0,0 | Text: "" | Btn1: false | Btn2: false
            </div>
        </div>
    </div>

    <script>
        let button1Active = false;
        let button2Active = false;
        
        function updateSlider(value) {
            document.getElementById('sliderValue').textContent = value;
            sendData('slider', value);
            updateAllValues();
        }
        
        function updateColor() {
            const red = document.getElementById('red').value || 0;
            const green = document.getElementById('green').value || 0;
            const blue = document.getElementById('blue').value || 0;
            
            document.getElementById('rgbValue').textContent = red + ', ' + green + ', ' + blue;
            document.getElementById('colorPreview').style.backgroundColor = `rgb(${red}, ${green}, ${blue})`;
            
            sendData('red', red);
            sendData('green', green);
            sendData('blue', blue);
            updateAllValues();
        }
        
        function updateText(value) {
            document.getElementById('textValue').textContent = '"' + value + '"';
            sendData('text', value);
            updateAllValues();
        }
        
        function toggleButton(buttonNum) {
            if (buttonNum === 1) {
                button1Active = !button1Active;
                document.getElementById('button1').classList.toggle('btn-active');
                document.getElementById('button1Value').textContent = button1Active;
                sendData('button1', button1Active);
            } else {
                button2Active = !button2Active;
                document.getElementById('button2').classList.toggle('btn-active');
                document.getElementById('button2Value').textContent = button2Active;
                sendData('button2', button2Active);
            }
            updateAllValues();
        }
        
        function updateAllValues() {
            const slider = document.getElementById('sliderValue').textContent;
            const rgb = document.getElementById('rgbValue').textContent;
            const text = document.getElementById('textValue').textContent;
            const btn1 = document.getElementById('button1Value').textContent;
            const btn2 = document.getElementById('button2Value').textContent;
            
            document.getElementById('allValues').textContent = 
                `Slider: ${slider} | RGB: ${rgb} | Text: ${text} | Btn1: ${btn1} | Btn2: ${btn2}`;
        }
        
        function sendData(param, value) {
            const xhr = new XMLHttpRequest();
            xhr.open('POST', '/update', true);
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            xhr.send(param + '=' + encodeURIComponent(value));
        }
        
        // Initiale Farbvorschau
        updateColor();
    </script>
</body>
</html>
)rawliteral";

void setup() {
  Serial.begin(115200);
  Serial.println();
  Serial.println("ESP8266 Webserver startet...");

  // WiFi Verbindung herstellen
  WiFi.mode(WIFI_STA);
  WiFi.hostname(hostname);
  WiFi.begin(ssid, password);
  
  Serial.print("Verbinde zu ");
  Serial.println(ssid);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  
  Serial.println();
  Serial.println("WiFi verbunden!");
  Serial.print("IP-Adresse: ");
  Serial.println(WiFi.localIP());
  
  // mDNS starten
  if (MDNS.begin(hostname)) {
    Serial.print("mDNS gestartet. Erreichbar unter: http://");
    Serial.print(hostname);
    Serial.println(".local");
  }

  // Webserver Routen definieren
  server.on("/", HTTP_GET, handleRoot);
  server.on("/update", HTTP_POST, handleUpdate);
  server.on("/values", HTTP_GET, handleValues);
  
  // 404 Handler
  server.onNotFound(handleNotFound);
  
  // Server starten
  server.begin();
  Serial.println("Webserver gestartet!");
  Serial.print("Öffnen Sie http://");
  Serial.print(WiFi.localIP());
  Serial.println(" in Ihrem Browser");

  matrix.begin();
  matrix.setTextWrap(false);
  matrix.setBrightness(hell); // Brightness level
  //matrix.setTextColor(colors[0]);
}

void loop() {
  server.handleClient();
  matrix.fillScreen(0);
  matrix.show();
  switch(sliderValue){
  case 0: leer();break;
  case 1: rotlicht();break;
  case 2: gruenlicht();break;
  case 3: blaulicht();break;
  case 4: gelblicht();break;
  case 5: turkieslicht();break;
  case 6: freelicht();break;
  case 7: blaulicht2();break;
  case 8: myText();break;
  case 9: myText2();break;
  }
}

void leer(){
  delay(100);
}

void myText(){
  matrix.setTextColor(colors[1]);
  matrix.fillScreen(0);
matrix.setCursor(x, 0);
matrix.print(F("ReMuBa-Die Reaktionsmurmelbahn"));
if(--x < -200) { //84
x = matrix.width();

//if(++pass >= 3) pass = 0;
//matrix.setTextColor(colors[pass]);
}
//two spaces
matrix.show();
delay(100);
}

void myText2(){
  matrix.setTextColor(colors[1]);
  matrix.fillScreen(0);
matrix.setCursor(x, 0);
matrix.print(F("ReMuBa-Die Reaktionsmurmelbahn"));
if(x == matrix.width()) for (int p=0; p<3; p++) gruenlicht();
if(--x < -190) { //84
x = matrix.width();

//if(++pass >= 3) pass = 0;
//matrix.setTextColor(colors[pass]);
}
//two spaces
matrix.show();
delay(100);
}

void blaulicht2(){
  for (int p = 31; p > -1; p--){
    matrix.fillScreen(0);
    color = colors[0];
    spalten(p-2);
    spalten(p-1);
    spalten(p);
    spalten(p+1);
    spalten(p+2);
    color = colors[2];
    spalten((p + 16) % 32-2);
    spalten((p + 16) % 32-1);
    spalten((p + 16) % 32);
    spalten((p + 16) % 32+1);
    spalten((p + 16) % 32+2);
    matrix.show();
    delay(pause);
  }
}

void gelblicht(){
  for (int p = 31; p > -1; p--){
    matrix.fillScreen(0);
    color = matrix.Color(255, 155, 0);
    spalten(p-2);
    spalten(p-1);
    spalten(p);
    spalten(p+1);
    spalten(p+2);
    //color = colors[2];
    spalten((p + 16) % 32-2);
    spalten((p + 16) % 32-1);
    spalten((p + 16) % 32);
    spalten((p + 16) % 32+1);
    spalten((p + 16) % 32+2);
    matrix.show();
    delay(pause);
  }
}

void rotlicht(){
  for (int p = 31; p > -1; p--){
    matrix.fillScreen(0);
    color = matrix.Color(255, 0, 0);
    spalten(p-2);
    spalten(p-1);
    spalten(p);
    spalten(p+1);
    spalten(p+2);
    //color = colors[2];
    spalten((p + 16) % 32-2);
    spalten((p + 16) % 32-1);
    spalten((p + 16) % 32);
    spalten((p + 16) % 32+1);
    spalten((p + 16) % 32+2);
    matrix.show();
    delay(pause);
  }
}

void gruenlicht(){
  for (int p = 31; p > -1; p--){
    matrix.fillScreen(0);
    color = matrix.Color(0, 255, 0);
    spalten(p-2);
    spalten(p-1);
    spalten(p);
    spalten(p+1);
    spalten(p+2);
    //color = colors[2];
    spalten((p + 16) % 32-2);
    spalten((p + 16) % 32-1);
    spalten((p + 16) % 32);
    spalten((p + 16) % 32+1);
    spalten((p + 16) % 32+2);
    matrix.show();
    delay(pause);
  }
}

void turkieslicht(){
  for (int p = 31; p > -1; p--){
    matrix.fillScreen(0);
    color = matrix.Color(0, 255, 255);
    spalten(p-2);
    spalten(p-1);
    spalten(p);
    spalten(p+1);
    spalten(p+2);
    //color = colors[2];
    spalten((p + 16) % 32-2);
    spalten((p + 16) % 32-1);
    spalten((p + 16) % 32);
    spalten((p + 16) % 32+1);
    spalten((p + 16) % 32+2);
    matrix.show();
    delay(pause);
  }
}

void freelicht(){
  color = matrix.Color(redValue, greenValue, blueValue);
  for (int p = 31; p > -1; p--){
    matrix.fillScreen(0);
    spalten(p-2);
    spalten(p-1);
    spalten(p);
    spalten(p+1);
    spalten(p+2);
    //color = colors[2];
    spalten((p + 16) % 32-2);
    spalten((p + 16) % 32-1);
    spalten((p + 16) % 32);
    spalten((p + 16) % 32+1);
    spalten((p + 16) % 32+2);
    matrix.show();
    delay(pause);
  }
}

void blaulicht(){
  color = colors[2];
  for (int p = 31; p > -1; p--){
    matrix.fillScreen(0);
    spalten(p-2);
    spalten(p-1);
    spalten(p);
    spalten(p+1);
    spalten(p+2);
    spalten((p + 16) % 32-2);
    spalten((p + 16) % 32-1);
    spalten((p + 16) % 32);
    spalten((p + 16) % 32+1);
    spalten((p + 16) % 32+2);
    matrix.show();
    delay(pause);
  }
}

void spalten(int i){
  switch (i){
    case -4: for (int s=0; s < 8; s++) matrix.drawPixel(28, s, color); break;
    case -3: for (int s=0; s < 8; s++) matrix.drawPixel(29, s, color); break;
    case -2: for (int s=0; s < 8; s++) matrix.drawPixel(30, s, color); break;
    case -1: for (int s=0; s < 8; s++) matrix.drawPixel(31, s, color); break;
    case 0: for (int s=0; s < 8; s++) matrix.drawPixel(0, s, color); break;
    case 1: for (int s=0; s < 8; s++) matrix.drawPixel(1, s, color); break;
    case 2: for (int s=0; s < 8; s++) matrix.drawPixel(2, s, color); break;
    case 3: for (int s=0; s < 8; s++) matrix.drawPixel(3, s, color); break;
    case 4: for (int s=0; s < 8; s++) matrix.drawPixel(4, s, color); break;
    case 5: for (int s=0; s < 8; s++) matrix.drawPixel(5, s, color); break;
    case 6: for (int s=0; s < 8; s++) matrix.drawPixel(6, s, color); break;
    case 7: for (int s=0; s < 8; s++) matrix.drawPixel(7, s, color); break;
    case 8: for (int s=0; s < 8; s++) matrix.drawPixel(8, s, color); break;
    case 9: for (int s=0; s < 8; s++) matrix.drawPixel(9, s, color); break;
    case 10: for (int s=0; s < 8; s++) matrix.drawPixel(10, s, color); break;
    case 11: for (int s=0; s < 8; s++) matrix.drawPixel(11, s, color); break;
    case 12: for (int s=0; s < 8; s++) matrix.drawPixel(12, s, color); break;
    case 13: for (int s=0; s < 8; s++) matrix.drawPixel(13, s, color); break;
    case 14: for (int s=0; s < 8; s++) matrix.drawPixel(14, s, color); break;
    case 15: for (int s=0; s < 8; s++) matrix.drawPixel(15, s, color); break;
    case 16: for (int s=0; s < 8; s++) matrix.drawPixel(16, s, color); break;
    case 17: for (int s=0; s < 8; s++) matrix.drawPixel(17, s, color); break;
    case 18: for (int s=0; s < 8; s++) matrix.drawPixel(18, s, color); break;
    case 19: for (int s=0; s < 8; s++) matrix.drawPixel(19, s, color); break;
    case 20: for (int s=0; s < 8; s++) matrix.drawPixel(20, s, color); break;
    case 21: for (int s=0; s < 8; s++) matrix.drawPixel(21, s, color); break;
    case 22: for (int s=0; s < 8; s++) matrix.drawPixel(22, s, color); break;
    case 23: for (int s=0; s < 8; s++) matrix.drawPixel(23, s, color); break;
    case 24: for (int s=0; s < 8; s++) matrix.drawPixel(24, s, color); break;
    case 25: for (int s=0; s < 8; s++) matrix.drawPixel(25, s, color); break;
    case 26: for (int s=0; s < 8; s++) matrix.drawPixel(26, s, color); break;
    case 27: for (int s=0; s < 8; s++) matrix.drawPixel(27, s, color); break;
    case 28: for (int s=0; s < 8; s++) matrix.drawPixel(28, s, color); break;
    case 29: for (int s=0; s < 8; s++) matrix.drawPixel(29, s, color); break;
    case 30: for (int s=0; s < 8; s++) matrix.drawPixel(30, s, color); break;
    case 31: for (int s=0; s < 8; s++) matrix.drawPixel(31, s, color); break;
    case 32: for (int s=0; s < 8; s++) matrix.drawPixel(0, s, color); break;
    case 33: for (int s=0; s < 8; s++) matrix.drawPixel(1, s, color); break;
    case 34: for (int s=0; s < 8; s++) matrix.drawPixel(2, s, color); break;
    case 35: for (int s=0; s < 8; s++) matrix.drawPixel(3, s, color); break;
  }
  yield();
}

// Handler für die Hauptseite
void handleRoot() {
  server.send(200, "text/html", htmlPage);
}

// Handler für Updates von der Webseite
void handleUpdate() {
  if (server.hasArg("slider")) {
    sliderValue = server.arg("slider").toInt();
    Serial.println(String("Slider: ") + String(sliderValue));
  }
  
  if (server.hasArg("red")) {
    redValue = server.arg("red").toInt();
    Serial.println(String("Rot: ") + String(redValue));
  }
  
  if (server.hasArg("green")) {
    greenValue = server.arg("green").toInt();
    Serial.println(String("Grün: ") + String(greenValue));
  }
  
  if (server.hasArg("blue")) {
    blueValue = server.arg("blue").toInt();
    Serial.println(String("Blau: ") + String(blueValue));
  }
  
  if (server.hasArg("text")) {
    textValue = server.arg("text");
    Serial.println(String("Text: ") + textValue);
  }
  
  if (server.hasArg("button1")) {
    button1State = (server.arg("button1") == "true");
    Serial.println(String("Button 1: ") + String(button1State ? "true" : "false"));
  }
  
  if (server.hasArg("button2")) {
    button2State = (server.arg("button2") == "true");
    Serial.println(String("Button 2: ") + String(button2State ? "true" : "false"));
  }
  
  server.send(200, "text/plain", "OK");
  
  // Aktuelle Werte ausgeben
  printCurrentValues();
}

// Handler für Werte-Abfrage (optional für erweiterte Funktionen)
void handleValues() {
  String json = String("{");
  json += String("\"slider\":") + String(sliderValue) + String(",");
  json += String("\"red\":") + String(redValue) + String(",");
  json += String("\"green\":") + String(greenValue) + String(",");
  json += String("\"blue\":") + String(blueValue) + String(",");
  json += String("\"text\":\"") + textValue + String("\",");
  json += String("\"button1\":") + String(button1State ? "true" : "false") + String(",");
  json += String("\"button2\":") + String(button2State ? "true" : "false");
  json += String("}");
  
  server.send(200, "application/json", json);
}

// 404 Handler
void handleNotFound() {
  String message = String("Seite nicht gefunden\n\n");
  message += String("URI: ") + server.uri() + String("\n");
  message += String("Method: ") + String(server.method() == HTTP_GET ? "GET" : "POST") + String("\n");
  message += String("Arguments: ") + String(server.args()) + String("\n");
  
  for (uint8_t i = 0; i < server.args(); i++) {
    message += String(" ") + server.argName(i) + String(": ") + server.arg(i) + String("\n");
  }
  
  server.send(404, "text/plain", message);
}

// Hilfsfunktion zum Ausgeben aller aktuellen Werte
void printCurrentValues() {
  Serial.println("=== Aktuelle Werte ===");
  Serial.println(String("Slider: ") + String(sliderValue));
  Serial.println(String("RGB: ") + String(redValue) + String(", ") + String(greenValue) + String(", ") + String(blueValue));
  Serial.println(String("Text: ") + textValue);
  Serial.println(String("Button 1: ") + String(button1State ? "true" : "false"));
  Serial.println(String("Button 2: ") + String(button2State ? "true" : "false"));
  Serial.println("=====================");
}