====== in Planung: Codeweek 2025 ====== [[biebl:codeweek|Planung]] ====== Codeweek 2025 ====== * [[https://|Auftaktveranstaltung 2025]] {{:www-fuer-alle:wetterballo-blick-erde.png?400|}} Wetterballonprojekt Juni 2023 - Start vom Schulhof{{:www-fuer-alle:sketch1668705325936.png?200|}} {{:www-fuer-alle:sas-ballonii-blick-von-hamburg-auf-die-ostsee-aus-30km-hoehe.jpg?direct&400|}} Zweiter Flug: 30 km über Hamburg Juni 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 ;-) 2024 {{:www-fuer-alle:doc_-_12.10.24_-_14-23_-_p3.jpg?200|}} 2025 {{:www-fuer-alle:codeweek-2025-breadboardaufbau.jpeg?direct&400|}} ===== 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) * * CCC - Martin Biebl - Sankt-Ansgar-Schule Hamburg - CCC *********************************************************************/ // === 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 #include #include #include #include #include #include #include // === 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( )rawliteral"+String(DEVICE_NAME)+R"rawliteral(

)rawliteral"+String(DEVICE_NAME)+R"rawliteral(

Modus: unbekannt

Zeit: --:--:--

T: - °C
P: - hPa
LDR: -
MIC: -
)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 * CAD * Einstieg mit [[https://www.blockscad3d.com/editor/|BlocksCAD]] * Projektumsetzung mit [[https://www.onshape.com/de/|Onshape]] * Slicer * [[https://www.chip.de/downloads/Ultimaker-Cura_62335633.html|Ultimaker Cura]] * [[https://bambulab.com/de-de/download/studio|BambulabStudio]] * Druck * [[https://artillery3d.de/artillery-hornet/|Hornet]] * [[https://bambulab.com/de-de/x1e|Bambulab X1]] * Filamente * [[https://www.recyclingfabrik.com/collections/rpla|recyceltes PLA-Filament]] * ... * ARDUINO - Projektumsetzung * Software * [[https://funduino.de/openroberta-einrichtung|Open Roberta]] * [[https://www.arduino.cc/en/software|ARDUINO IDE]] * Hardware {{:www-fuer-alle:fehlende-teile.png?400|}} * UNO * NANO * (D1-mini) ===== Linkliste ===== ===== Geschichte ===== * ...[[www-fuer-alle:arduino|seit 2018 im Unterrichtseinsatz...]] {{:www-fuer-alle:2019-08-arduino-material-01.jpg?600|}} {{:www-fuer-alle:2019-08-arduino-material-02.jpg?600|}} {{:www-fuer-alle:2019-08-arduino-material-03.jpg?600|}} {{:www-fuer-alle:2019-08-arduino-material-04.jpg?600|}} {{:www-fuer-alle:2019-08-arduino-material-06.jpg?600|}} {{:www-fuer-alle:2019-08-arduino-material-07.jpg?600|}} {{:www-fuer-alle:2019-08-arduino-material-08.jpg?600|}} {{:www-fuer-alle:2019-08-arduino-kabel-01.jpg?600|}}