Dies ist eine alte Version des Dokuments!
Inhaltsverzeichnis
in Planung: Codeweek 2025
Codeweek 2025
Workshop
- Anmeldung zum Workshop an der SAS am Montag - 2025-10-13 in Form eine Mail an Makerspace@gymsas.de mit dem Betreff: Codeweek2025
- Wir melden uns zeitnah
Vollversion
/**********************************************************************
 * ESP32 - Mobiles Messgerät (Workshop-ready, 2025)
 * 
 * Sensoren:
 *   - BMP180 (Temperatur & Luftdruck)
 *   - MAX4466 (Lautstärke)
 *   - LDR (Helligkeit)
 * 
 * Anzeige:
 *   - OLED (128x64)
 *   - Webinterface mit Live-Werten, Ampeln & Lärmanzeige
 * 
 * Funktionen:
 *   - Verkehrsampel (3s grün, 1s gelb, 3s rot, 1s rot+gelb)
 *   - Lärmampe (9-Stufen Skala aus Pegel, reagiert auf Raumlärm)
 *   - „AUS“-Button schaltet alles ab
 *   - Visuelle Warnung (rote LED blinkt bei MIC ≥ 8)
 *********************************************************************/
// === WLAN / Geräte-Setup ===========================================
#define DEVICE_NAME "Code-W"
#define WIFI_PASSWORD "12345678"
#define MEASUREMENT_INTERVAL_SEC 1
// === Hardware-Pins =================================================
#define OLED_SDA 23
#define OLED_SCL 22
#define ADC_PIN_LDR 34
#define ADC_PIN_MIC 35
#define LED_GREEN 15
#define LED_YELLOW 2
#define LED_RED 4
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_BMP085.h>
#include <WiFi.h>
#include <WebServer.h>
#include <time.h>
#include <math.h>
// === Objekte =======================================================
Adafruit_SSD1306 display(128, 64, &Wire, -1);
Adafruit_BMP085 bmp;
WebServer server(80);
// === Globale Messdaten =============================================
volatile int lastLdr = 0;
volatile int lastMic = 0;
volatile float lastTemp = 0.0;
volatile float lastPres = 0.0;
// === Ampelstatus ===================================================
bool autoAmpelMode = false;   // Verkehrsampel aktiv?
bool lärmAmpelMode = false;   // Lärmampe aktiv?
int ampelState = 0;           // interner Zustand der Verkehrsampel
unsigned long ampelTimer = 0; // Timer für Ampelphasen
// === Zeit / Uhr ====================================================
bool timeSynced = false;
uint64_t startEpoch = 0;
uint64_t startMillis = 0;
// === Zeit-Funktionen ===============================================
String formatTime(uint64_t epochSec) {
  time_t t = (time_t)epochSec;
  struct tm *tm_info = localtime(&t);
  char buffer[25];
  strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", tm_info);
  return String(buffer);
}
String getCurrentTimeString() {
  if (!timeSynced) return "--:--:--";
  uint64_t elapsedMs = millis() - startMillis;
  uint64_t epochNow = startEpoch + (elapsedMs / 1000ULL);
  return formatTime(epochNow);
}
// === LDR lesen =====================================================
int readLDR(int pin, int samples = 10) {
  long sum = 0;
  for (int i = 0; i < samples; i++) { sum += analogRead(pin); delay(2); }
  return sum / samples;
}
// === Lautstärke mit automatischer Kalibrierung =====================
int readMicLevel(int pin) {
  const int samples = 250; // Messzeit ~0,25s
  long sum = 0;
  for (int i = 0; i < samples; i++) { sum += analogRead(pin); delayMicroseconds(400); }
  float mean = (float)sum / samples;
  long sumSq = 0;
  for (int i = 0; i < samples; i++) {
    int val = analogRead(pin);
    float diff = val - mean;
    sumSq += diff * diff;
    delayMicroseconds(400);
  }
  float rms = sqrt((float)sumSq / samples);
  static float noiseFloor = 10;  // Grundrauschen
  static float noiseCeil  = 120; // Max-Pegel
  // Sanfte Anpassung an Raumgeräusche
  noiseFloor = 0.995f * noiseFloor + 0.005f * min(noiseFloor, rms);
  noiseCeil  = max(noiseCeil * 0.98f, rms);
  if (noiseCeil - noiseFloor < 30) noiseCeil = noiseFloor + 30;
  float norm = (rms - noiseFloor) / (noiseCeil - noiseFloor);
  if (norm < 0) norm = 0; if (norm > 1) norm = 1;
  int level = 1 + roundf(norm * 8.0f); // Pegel 1..9
  // Debug-Ausgabe zur Kontrolle
  Serial.printf("MIC RMS=%.1f | floor=%.1f | ceil=%.1f | level=%d\n",
                rms, noiseFloor, noiseCeil, level);
  return level;
}
// === Sensoren einlesen =============================================
void readSensors() {
  lastLdr = readLDR(ADC_PIN_LDR);
  lastMic = readMicLevel(ADC_PIN_MIC);
  if (bmp.begin()) {
    lastTemp = bmp.readTemperature();
    lastPres = bmp.readPressure() / 100.0;
  } else { lastTemp = NAN; lastPres = NAN; }
}
// === OLED-Ausgabe ==================================================
void updateOLED() {
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0);
  if (!timeSynced) {
    display.print(DEVICE_NAME);
    display.print(" "); display.println(WiFi.softAPIP().toString());
  } else display.println(getCurrentTimeString());
  display.setTextSize(2);
  display.setCursor(0, 12);
  display.printf("T %.1f C\n", isnan(lastTemp)?0:lastTemp);
  display.setCursor(0, 34);
  display.printf("P %.0f hPa", isnan(lastPres)?0:lastPres);
  display.setCursor(0, 54);
  display.setTextSize(1);
  display.printf("LDR:%4d  MIC:%d", lastLdr, lastMic);
  display.display();
}
// === LED-Steuerung =================================================
void setLEDs(bool g, bool y, bool r) {
  digitalWrite(LED_GREEN, g);
  digitalWrite(LED_YELLOW, y);
  digitalWrite(LED_RED, r);
}
// === Lärmampe-Logik ================================================
void updateLärmAmpel(int level) {
  if (!lärmAmpelMode) return;
  if (level <= 2) setLEDs(true, false, false);
  else if (level <= 4) setLEDs(true, true, false);
  else if (level <= 6) setLEDs(false, true, false);
  else if (level <= 8) setLEDs(false, true, true);
  else setLEDs(false, false, true);
}
// === Lärmwarnung (Blinksignal) =====================================
void checkLärmWarnung() {
  static unsigned long lastWarn = 0;
  static bool blinking = false;
  static unsigned long blinkStart = 0;
  // Blinkphase aktiv
  if (blinking) {
    if (millis() - blinkStart < 100) setLEDs(false, false, true);   // Rot an
    else if (millis() - blinkStart < 250) setLEDs(false, false, false); // aus
    else blinking = false;
    return;
  }
  // Bei Pegel >= 8 und nach 2s Pause
  if (lärmAmpelMode && lastMic >= 8 && millis() - lastWarn > 2000) {
    blinking = true;
    blinkStart = millis();
    lastWarn = millis();
    Serial.println("🚨 Lärmwarnung ausgelöst (MIC >= 8)");
  }
}
// === Verkehrsampel-Logik ===========================================
void updateVerkehrsAmpel() {
  if (!autoAmpelMode) return;
  unsigned long now = millis(), diff = now - ampelTimer;
  switch (ampelState) {
    case 0: setLEDs(true,false,false); if(diff>3000){ampelState=1;ampelTimer=now;} break;
    case 1: setLEDs(false,true,false); if(diff>1000){ampelState=2;ampelTimer=now;} break;
    case 2: setLEDs(false,false,true); if(diff>3000){ampelState=3;ampelTimer=now;} break;
    case 3: setLEDs(false,true,true);  if(diff>1000){ampelState=0;ampelTimer=now;} break;
  }
}
// === Webinterface (HTML + JS) ======================================
String generateHTML() {
  // Weboberfläche mit allen Buttons und Balkenanzeige
  String html = R"rawliteral(
<!DOCTYPE html><html><head><meta charset="utf-8">
<title>)rawliteral"+String(DEVICE_NAME)+R"rawliteral(</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body{font-family:Arial;background:#111;color:#e0e0e0;text-align:center}
h1{font-size:1.4em}
.vals{margin:10px}
button{padding:12px 18px;margin:6px;font-size:1em;border-radius:6px;border:0;cursor:pointer}
.g{background:#0a0;color:#fff}.y{background:#aa0;color:#000}
.r{background:#a00;color:#fff}.a{background:#06c;color:#fff}
.b{background:#444;color:#fff}
.status{margin:10px;padding:6px 10px;border-radius:8px;display:inline-block;font-weight:bold;}
.green{background:#060;color:#fff}.blue{background:#06c;color:#fff}.gray{background:#555;color:#fff}
.bar-container{width:80%;height:20px;background:#333;margin:10px auto;border-radius:10px;overflow:hidden;}
.bar{height:100%;width:0%;background:#0f0;border-radius:10px;transition:width 0.3s,background 0.3s;}
@keyframes glow{0%{box-shadow:0 0 5px #f00;}50%{box-shadow:0 0 20px #ff4040;}100%{box-shadow:0 0 5px #f00;}}
.glow{animation:glow 0.8s infinite alternate;}
</style></head>
<body>
<h1>)rawliteral"+String(DEVICE_NAME)+R"rawliteral(</h1>
<p class="status gray" id="modeStatus">Modus: unbekannt</p>
<p>Zeit: <span id="zeit">--:--:--</span></p>
<div class="vals">
 <div>T: <span id="temp">-</span> °C</div>
 <div>P: <span id="pres">-</span> hPa</div>
 <div>LDR: <span id="ldr">-</span></div>
 <div>MIC: <span id="mic">-</span></div>
 <div class="bar-container"><div id="micBar" class="bar"></div></div>
</div>
<div>
 <button onclick="led('green')" class="g">GRÜN</button>
 <button onclick="led('yellow')" class="y">GELB</button>
 <button onclick="led('red')" class="r">ROT</button>
 <button onclick="led('redyellow')" class="r">ROT+GELB</button>
 <button onclick="led('ampel')" class="a">VERKEHRSAMPEL</button>
 <button onclick="led('lärm')" class="a">LÄRMAMPEL</button>
 <button onclick="led('off')" class="b">AUS</button>
</div>
<script>
function sendTime(){fetch("/settime?epoch="+Date.now());}
function updateValues(){
fetch('/value').then(r=>r.json()).then(obj=>{
 document.getElementById('zeit').innerText=obj.time;
 document.getElementById('temp').innerText=obj.temp?.toFixed(1)||'-';
 document.getElementById('pres').innerText=obj.pres?.toFixed(1)||'-';
 document.getElementById('ldr').innerText=obj.ldr;
 document.getElementById('mic').innerText=obj.mic+" ("+obj.micText+")";
 let s=document.getElementById('modeStatus');
 s.className="status "+obj.modeColor; s.innerText="Modus: "+obj.modeText;
 let bar=document.getElementById('micBar');
 let width=(obj.mic/9*100);
 let color=obj.mic<=3?'#0f0':(obj.mic<=6?'#ff0':'#f00');
 bar.style.width=width+'%'; bar.style.background=color;
 if(obj.mic>=7) bar.classList.add("glow"); else bar.classList.remove("glow");
});
}
function led(cmd){fetch('/led?cmd='+cmd);}
window.onload=()=>{sendTime();updateValues();setInterval(updateValues,1000);}
</script></body></html>)rawliteral";
  return html;
}
// === Server-Endpunkte ==============================================
void handleRoot(){ server.send(200,"text/html",generateHTML()); }
void handleValue(){
  String timeStr=getCurrentTimeString();
  String micText;
  if(lastMic<=2) micText="sehr leise";
  else if(lastMic<=3) micText="leise";
  else if(lastMic<=5) micText="normal";
  else if(lastMic<=7) micText="laut";
  else if(lastMic<=8) micText="sehr laut";
  else micText="extrem laut";
  String modeText,modeColor;
  if(autoAmpelMode){modeText="Verkehrsampel";modeColor="green";}
  else if(lärmAmpelMode){modeText="Lärmampe";modeColor="blue";}
  else{modeText="Manuell / Aus";modeColor="gray";}
  char buf[400];
  snprintf(buf,sizeof(buf),
   "{\"time\":\"%s\",\"temp\":%.2f,\"pres\":%.2f,\"ldr\":%d,\"mic\":%d,"
   "\"micText\":\"%s\",\"modeText\":\"%s\",\"modeColor\":\"%s\"}",
   timeStr.c_str(),lastTemp,lastPres,lastLdr,lastMic,
   micText.c_str(),modeText.c_str(),modeColor.c_str());
  server.send(200,"application/json",buf);
}
void handleLed(){
  String cmd=server.arg("cmd"); cmd.toLowerCase();
  if(cmd=="green"){setLEDs(1,0,0);autoAmpelMode=false;lärmAmpelMode=false;}
  else if(cmd=="yellow"){setLEDs(0,1,0);autoAmpelMode=false;lärmAmpelMode=false;}
  else if(cmd=="red"){setLEDs(0,0,1);autoAmpelMode=false;lärmAmpelMode=false;}
  else if(cmd=="redyellow"){setLEDs(0,1,1);autoAmpelMode=false;lärmAmpelMode=false;}
  else if(cmd=="ampel"){autoAmpelMode=true;lärmAmpelMode=false;ampelState=0;ampelTimer=millis();}
  else if(cmd=="lärm"){lärmAmpelMode=true;autoAmpelMode=false;}
  else if(cmd=="off"){setLEDs(0,0,0);autoAmpelMode=false;lärmAmpelMode=false;}
  server.send(200,"text/plain","OK");
}
void handleSetTime(){
  if(server.hasArg("epoch")){
    double epochMs=server.arg("epoch").toDouble();
    startEpoch=(uint64_t)(epochMs/1000ULL);
    startMillis=millis(); timeSynced=true;
    server.send(200,"text/plain","Time synced");
    Serial.printf("Zeit gesetzt: %s\n",formatTime(startEpoch).c_str());
    updateOLED();
  } else server.send(400,"text/plain","Missing epoch");
}
// === Setup =========================================================
void setup(){
  Serial.begin(115200);
  Serial.println("=== ESP32 Messgerät Start ===");
  pinMode(LED_GREEN,OUTPUT); pinMode(LED_YELLOW,OUTPUT); pinMode(LED_RED,OUTPUT);
  analogReadResolution(12); analogSetAttenuation(ADC_11db);
  Wire.begin(OLED_SDA,OLED_SCL);
  display.begin(SSD1306_SWITCHCAPVCC,0x3C);
  display.clearDisplay(); display.setTextColor(SSD1306_WHITE);
  display.setCursor(0,0); display.println("Start..."); display.display();
  bmp.begin();
  setenv("TZ","CET-1CEST,M3.5.0,M10.5.0/3",1); tzset();
  WiFi.softAP(DEVICE_NAME,WIFI_PASSWORD);
  Serial.printf("AP: %s  IP: %s\n",DEVICE_NAME,WiFi.softAPIP().toString().c_str());
  server.on("/",handleRoot);
  server.on("/value",handleValue);
  server.on("/led",handleLed);
  server.on("/settime",handleSetTime);
  server.begin();
  readSensors(); updateOLED();
}
// === Loop ==========================================================
void loop(){
  server.handleClient();
  static unsigned long lastMillis=0;
  if(millis()-lastMillis>=MEASUREMENT_INTERVAL_SEC*1000UL){
    lastMillis=millis(); readSensors(); updateOLED();
  }
  if(autoAmpelMode) updateVerkehrsAmpel();
  if(lärmAmpelMode){ updateLärmAmpel(lastMic); checkLärmWarnung(); }
}
Möglichkeiten
- 3D-Druck - Projektumsetzung
- ARDUINO - Projektumsetzung- Software
- Hardware
 
- UNO
- NANO
- (D1-mini)
Linkliste
Geschichte
Weiter Informationen
- intern-ostsee-aus-30km-hoehe.jpg?direct&400|}} Zweiter Flug: 30 km über Hamburg Juni 2025
Workshop
- Anmeldung zum Workshop an der SAS am Montag - 2024-10-13 in Form eine Mail an Makerspace@gymsas.de mit dem Betreff: Codeweek2025
- Wir melden uns zeitnah
Möglichkeiten
- 3D-Druck - Projektumsetzung
- ARDUINO - Projektumsetzung- Software
- Hardware
 
- UNO
- NANO
- (D1-mini)
Linkliste
Geschichte
Workshop
- Anmeldung zum Workshop an der SAS am Montag - 2024-10-13 in Form eine Mail an Makerspace@gymsas.de mit dem Betreff: Codeweek2025
- Wir melden uns zeitnah
Möglichkeiten
- 3D-Druck - Projektumsetzung====== Codeweek 2025 ======
Workshop
- Anmeldung zum Workshop an der SAS am Montag - 2024-10-13 in Form eine Mail an Makerspace@gymsas.de mit dem Betreff: Codeweek2025
- Wir melden uns zeitnah
Möglichkeiten
- 3D-Druck - Projektumsetzung
- ARDUINO - Projektumsetzung- Software
- Hardware
 
- UNO
- NANO
- (D1-mini)
Linkliste
Geschichte
Weiter Informationen
- ARDUINO - Projektumsetzung- Software
- Hardware
 
- UNO
- NANO
- (D1-mini)

















