''' Wetterstation Server with Graph Create a webpage access point to show sensor data Hardware: ESP32 (sensor chip temperature) DS18B20 temperature sensor Sensirion SCD41 CO2 sensor Bosch BME280 MicroPython v1.26.1 on 2025-09-11; Generic ESP32 module with ESP32 External libraries: https://microdot.readthedocs.io/en/latest/intro.html https://github.com/peter-l5/MicroPython_SCD4X https://github.com/robert-hh/BME280 2026-01-23 mchris ''' import network import uasyncio as asyncio from microdot import Microdot import esp32 import machine import onewire import ds18x20 import scd4x import gc import time from bme280_float import * gc.collect() def stream_html(): """Stream HTML file in small chunks to avoid memory overflow""" try: with open('index.html', 'r') as f: while True: chunk = f.read(1024) # 1KB chunks if not chunk: break yield chunk except OSError: yield "

index.html not found

" async def ap_mode(ssid, password): print("Free heap before init:", gc.mem_free()) # DS18B20 on GPIO 4 dat = machine.Pin(4) ds = ds18x20.DS18X20(onewire.OneWire(dat)) ds_sensors = ds.scan() print('DS18B20 sensors:', ds_sensors) gc.collect() # SCD41 I2C i2c = machine.I2C(1, scl=machine.Pin(22), sda=machine.Pin(21), freq=100000) scd = scd4x.SCD4X(i2c) print('SCD41 init...') scd.start_periodic_measurement() await asyncio.sleep(5) print('SCD41 ready') gc.collect() bme280 = BME280(i2c=i2c) print(bme280.values) print('bme280 initialized') gc.collect() print("Free heap before WiFi:", gc.mem_free()) # WiFi AP ap = network.WLAN(network.AP_IF) ap.active(True) ap.config(essid=ssid, password=password) while not ap.active(): await asyncio.sleep(0.1) print('AP IP:', ap.ifconfig()[0]) print("Free heap after WiFi:", gc.mem_free()) app = Microdot() app.ds_sensors = ds_sensors app.ds_device = ds app.scd_device = scd app.bme208 = bme280 # Fixed typo: was bme208=bme280 @app.route('/') async def index(request): return stream_html(), 200, {'Content-Type': 'text/html'} @app.route('/data') async def get_data(request): gc.collect() # Free memory before heavy operations # Internal temp temp_raw = esp32.raw_temperature() internal = round((temp_raw - 32) * 5/9, 1) # DS18B20 ds18b20 = -99 if app.ds_sensors: try: app.ds_device.convert_temp() await asyncio.sleep_ms(750) ds18b20 = round(app.ds_device.read_temp(app.ds_sensors[0]), 1) except: pass gc.collect() # SCD41 scd_temp = scd_co2 = scd_rh = -99 try: if app.scd_device.data_ready: scd_temp = round(app.scd_device.temperature, 1) scd_co2 = int(app.scd_device.CO2) scd_rh = round(app.scd_device.relative_humidity, 1) except: pass # BME280 - FIXED: values were swapped! values = app.bme208.values bme_t = values[0] # Temperature bme_p = values[1] # Pressure bme_rh = values[2] # Humidity gc.collect() t = time.localtime() timestamp = "{:02d}:{:02d}:{:02d}".format(t[3], t[4], t[5]) return { 'internal': internal, 'ds18b20': ds18b20, 'scd_temp': scd_temp, 'scd_co2': scd_co2, 'scd_rh': scd_rh, 'bme_t': bme_t, # FIXED: was bme_p 'bme_p': bme_p, # FIXED: was bme_t 'bme_rh': bme_rh, 'time': timestamp }, 200, {'Content-Type': 'application/json'} print("Starting server... Free heap:", gc.mem_free()) await app.start_server(port=80) gc.collect() asyncio.run(ap_mode('pipico', '12345678'))