Falls jemand auch eine oder mehrere PWM Pumpen hat, hier mein neues kleines Projekt: https://heissa.de/web1/control.php cat main0.py # ###################################################################### # # main.py – WILO PUMPE STEUERUNG (Mit Tacho-Timeout auf 0 µs korrigiert) # ###################################################################### import time import gc import network from machine import Pin, PWM, Timer, WDT, ADC from umqtt.simple import MQTTClient import sys import os import ujson import utime # ==================== KONFIGURATION ==================== WATCHDOG_ENABLED = True WATCHDOG_TIMEOUT = 8000 # ms PWM_MIN_HARD = 0 PWM_MAX = 64000 RAMP_DURATION = 16.0 # Sekunden für sanfte Rampe TARGET_PWM = 29000 INTERVAL_SECONDS = 900 # 15 Minuten BOOST_DURATION = 5 # 5 Sekunden # RPM-BERECHNUNG: Wilo Para ST (Annahme 8 Pulse/Umdrehung) PULSES_PER_REVOLUTION = 8 TACHO_TIMEOUT_MS = 50 # 50 ms Timeout. Setzt Tacho-Werte auf 0. mqtt_server = '192.168.178.23' client_id = 'picow2' topic_sub_pump = b'heatp/pump' topic_pub = b'heatp/pico120' topic_pins = b'heatp/pins' FIRMWARE_VERSION = "v2.3-final-tacho-fix" start_time = time.time() # ==================== GLOBALE VARIABLEN ==================== current_pwm = PWM_MAX target_pwm = PWM_MAX ramp_start_time = None ramp_start_value = None last_boost_start = 0 boost_active = False timers = [] client = None # GLOBALE VARIABLEN FÜR PIN 5 FLANKENMESSUNG last_pin5_time_us = utime.ticks_us() pin5_flank_time_us = 0 pin5_high_time_us = 0 pin5_low_time_us = 0 pump_frequency_hz = 0.0 pump_rpm = 0 last_pulse_time_us = utime.ticks_us() # Zeit des letzten Pulses # ==================== FLANKENZEIT-MESSUNG CALLBACK ==================== def pin5_callback(pin): """Speichert die Dauer der HIGH- und LOW-Flanke separat.""" global last_pin5_time_us, pin5_flank_time_us, pin5_high_time_us, pin5_low_time_us, last_pulse_time_us current_time_us = utime.ticks_us() time_diff_us = utime.ticks_diff(current_time_us, last_pin5_time_us) # Filtert Rauschen (kurze Pulse) if time_diff_us > 100: # Wenn der Pin JETZT HIGH ist, hat gerade der LOW-Zustand geendet if pin.value() == 1: pin5_low_time_us = time_diff_us # Wenn der Pin JETZT LOW ist, hat gerade der HIGH-Zustand geendet else: pin5_high_time_us = time_diff_us pin5_flank_time_us = time_diff_us last_pulse_time_us = current_time_us # Letzter Puls aufgezeichnet last_pin5_time_us = current_time_us # ==================== HARDWARE & PIN-DEFINITIONEN ==================== pwm0 = PWM(Pin(0), freq=150) pwm0.duty_u16(PWM_MAX) LED = Pin("LED", Pin.OUT) LED.on() feedback_pin7 = Pin(7, Pin.IN, Pin.PULL_UP) test_pin1 = Pin(1, Pin.IN, Pin.PULL_UP) pump_feedback_pin19 = Pin(19, Pin.IN) feedback_pin5 = Pin(5, Pin.IN, Pin.PULL_UP) # Interrupt-Konfiguration für PIN 5 (misst jede Flanke) feedback_pin5.irq(trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, handler=pin5_callback) # ADC Pins adc26 = ADC(Pin(26)) adc27 = ADC(Pin(27)) adc28 = ADC(Pin(28)) # WLAN Setup sta = network.WLAN(network.STA_IF) sta.active(True) sta.connect("f24", "9876543210") # Watchdog Setup if WATCHDOG_ENABLED: wdt = WDT(timeout=WATCHDOG_TIMEOUT) def feed_watchdog(): if WATCHDOG_ENABLED and wdt: wdt.feed() # ---------------------------------------------------------------------- ## 🔗 MQTT LOGGING und Hilfsfunktionen # ---------------------------------------------------------------------- def mqtt_log(msg): try: if client: ts = time.localtime() payload = f"{ts[3]:02d}:{ts[4]:02d}:{ts[5]:02d} - {msg}" client.publish(b'heatp/log', payload.encode()) except: pass feed_watchdog() def get_mem_percent(): try: gc.collect() return int(gc.mem_alloc() / (gc.mem_alloc() + gc.mem_free()) * 100) except: return 0.0 def read_adc_voltage(adc): try: raw = adc.read_u16() return round((raw / 65535) * 3.3, 3) except: return 0.0 # ---------------------------------------------------------------------- ## 📊 MQTT-Payload-Erzeugung (Mit Tacho-Timeout & JSON-Fix) # ---------------------------------------------------------------------- def publish_all_pins(t): global pump_frequency_hz, pump_rpm, last_pulse_time_us # ZUSÄTZLICH: Zugriff auf alle globalen Tacho-Variablen für Timeout-Reset global pin5_flank_time_us, pin5_high_time_us, pin5_low_time_us # --- TACHO TIMEOUT CHECK --- now_us = utime.ticks_us() time_since_last_pulse_ms = utime.ticks_diff(now_us, last_pulse_time_us) / 1000 # Wenn seit dem letzten Puls mehr Zeit vergangen ist als das Timeout erlaubt, ist die Pumpe AUS if time_since_last_pulse_ms > TACHO_TIMEOUT_MS: pump_frequency_hz = 0.0 pump_rpm = 0 # KORREKTUR: Globale Tacho-Variablen auf 0 setzen, um 0 µs im JSON zu senden. pin5_flank_time_us = 0 pin5_high_time_us = 0 pin5_low_time_us = 0 T_us_local = 0 # Local T_us for legacy short status # --- RPM-BERECHNUNG (NUR wenn kein Timeout) --- else: T_us_local = pin5_flank_time_us if T_us_local > 0: # Vermeide Division durch Null, falls T_us unerwartet Null ist try: freq = 1.0 / (T_us_local * 0.000001) pump_frequency_hz = round(freq, 2) divisor_factor = 60.0 / PULSES_PER_REVOLUTION rpm = int(divisor_factor / (T_us_local * 0.000001)) pump_rpm = rpm except: pump_frequency_hz = 0.0 pump_rpm = 0 else: # Fallback, sollte durch Timeout abgefangen werden pump_frequency_hz = 0.0 pump_rpm = 0 # --- ENDE RPM-BERECHNUNG --- try: ip = sta.ifconfig()[0] if sta.isconnected() else "0.0.0.0" wlan_status = 1 if sta.isconnected() else 0 mem_pct = get_mem_percent() uptime = int(time.time() - start_time) mp_version = f"{os.uname().sysname} v{os.uname().version.split()[0]}" build_date = os.uname().version.split(';')[1].strip() if ';' in os.uname().version else "unknown" machine = os.uname().machine full_fw = f"{FIRMWARE_VERSION} | {mp_version} | {machine} | {build_date}" pins = { "FW": full_fw, "UPTIME": uptime, "WLAN": wlan_status, "LED": LED.value(), "PWM": current_pwm, "PIN0": current_pwm, "PIN1": test_pin1.value(), "PIN7": feedback_pin7.value(), # TACHO / FEEDBACK (Jetzt 0 bei Timeout) "PIN5": feedback_pin5.value(), "PIN5_Flank_us": pin5_flank_time_us, "PIN5_HIGH_us": pin5_high_time_us, "PIN5_LOW_us": pin5_low_time_us, "PIN5_Freq_Hz": pump_frequency_hz, "PumpRPM": pump_rpm, "PIN19": pump_feedback_pin19.value(), "PumpFeedback": pump_feedback_pin19.value(), # ADC PINS "PIN26": read_adc_voltage(adc26), "PIN27": read_adc_voltage(adc27), "PIN28": read_adc_voltage(adc28), } # Schleife über die restlichen GPIOs for gp in [2,3,4,6,8,9,10,11,12,13,14,15,16,17,18,20,21]: if gp in (5, 19): continue try: p = Pin(gp, Pin.IN) val = p.value() p.deinit() pins[f"PIN{gp}"] = val except: pins[f"PIN{gp}"] = 0 # JSON-Serialisierung json_str = ujson.dumps(pins) client.publish(topic_pins, json_str.encode()) # Statusmeldung (kurze Form) pin7_state = "LOW" if feedback_pin7.value() == 0 else "HIGH" pin5_state = "LOW" if feedback_pin5.value() == 0 else "HIGH" pump_state = "LOW" if pump_feedback_pin19.value() == 0 else "HIGH" status = f"PWM:{current_pwm},IP:{ip},MEM:{mem_pct}%,PIN7:{pin7_state},PIN5:{pin5_ state},PUMP:{pump_state},T_US:{pin5_flank_time_us}" client.publish(topic_pub, status.encode()) except Exception as e: mqtt_log(f"publish_all_pins error: {e}") feed_watchdog() # ---------------------------------------------------------------------- ## ⏱️ Steuerung und MQTT-Logik # ---------------------------------------------------------------------- def update_pwm_ramp(t): global current_pwm, target_pwm, ramp_start_time, ramp_start_value if ramp_start_time is None: if current_pwm == target_pwm: pwm0.duty_u16(current_pwm) return ramp_start_time = time.time() ramp_start_value = current_pwm elapsed = time.time() - ramp_start_time if elapsed >= RAMP_DURATION: current_pwm = target_pwm ramp_start_time = None pwm0.duty_u16(current_pwm) return progress = elapsed / RAMP_DURATION new_pwm = int(ramp_start_value + (target_pwm - ramp_start_value) * progress) new_pwm = max(PWM_MIN_HARD, min(PWM_MAX, new_pwm)) if new_pwm != current_pwm: current_pwm = new_pwm pwm0.duty_u16(current_pwm) def boost_cycle(t): global target_pwm, ramp_start_time, last_boost_start, boost_active now = time.time() if not boost_active and now - last_boost_start >= INTERVAL_SECONDS: # BOOST START mqtt_log("15-Min-Boost: 5s auf 100%") target_pwm = PWM_MAX ramp_start_time = None pwm0.duty_u16(PWM_MAX) LED.on() last_boost_start = now boost_active = True if boost_active and now - last_boost_start >= BOOST_DURATION: # BOOST ENDE mqtt_log(f"Boost Ende → 16s Rampe auf {TARGET_PWM}") target_pwm = TARGET_PWM ramp_start_time = None boost_active = False def sub_cb(topic, msg): global target_pwm, ramp_start_time, last_boost_start, boost_active try: if topic == topic_sub_pump: cmd = msg.decode().strip().lower() if cmd == "reset": mqtt_log("REMOTE RESET → Watchdog läuft aus in 8s") for t in timers: try: t.deinit() except: pass timers.clear() try: sta.active(False) except: pass while True: pass if cmd == "off": target_pwm = 0 ramp_start_time = None pwm0.duty_u16(0) LED.off() boost_active = False mqtt_log("Pumpe AUS") elif cmd in ("auto", ""): boost_active = True last_boost_start = time.time() - INTERVAL_SECONDS + 10 LED.on() mqtt_log("Auto reaktiviert") elif cmd.isdigit(): val = int(cmd) target_pwm = max(0, val) ramp_start_time = None boost_active = False if target_pwm > 0: LED.on() else: LED.off() mqtt_log(f"Manuell → {val}") elif cmd == "on": target_pwm = PWM_MAX ramp_start_time = None boost_active = False LED.on() mqtt_log("Manuell → 100%") else: mqtt_log(f"Unbekannt: {cmd}") publish_all_pins(None) feed_watchdog() except Exception as e: mqtt_log(f"sub_cb error: {e}") def mqtt_connect(): try: # ANNAHME: Broker braucht keine Auth c = MQTTClient(client_id, mqtt_server, keepalive=300) c.set_callback(sub_cb) c.connect() mqtt_log("MQTT verbunden") c.subscribe(topic_sub_pump) return c except: return None def reconnect(): global client for _ in range(5): client = mqtt_connect() if client: return feed_watchdog() mqtt_log("FATAL → Reset") while True: pass def add_timer(period, callback): t = Timer() t.init(period=period, mode=Timer.PERIODIC, callback=callback) timers.append(t) # ---------------------------------------------------------------------- ## 🚀 INIT & HAUPTSCHLEIFE # ---------------------------------------------------------------------- client = mqtt_connect() LED.on() while not sta.isconnected(): feed_watchdog() ip = sta.ifconfig()[0] mqtt_log(f"Start – IP: {ip} | FW: {FIRMWARE_VERSION}") publish_all_pins(None) add_timer(200, update_pwm_ramp) add_timer(1000, boost_cycle) add_timer(5000, publish_all_pins) add_timer(240000, lambda t: client.ping() if client else None) add_timer(3600000, lambda t: (gc.collect(), mqtt_log(f"GC: {gc.mem_free()}"))) while True: try: if client: client.check_msg() feed_watchdog() except Exception as e: mqtt_log(f"Error: {e}") reconnect()
Als Anmerkung: Stell den Code als Datei hoch. Die Formatierung zerhaut alles. Und gerade bei Python wo es auf die Formatierung ankommt...
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.
