#!/usr/bin/env python3
'''
Wetterstation Server
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-22 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 web_page():
try:
with open('index.html', 'r') as f:
return f.read()
except OSError:
# Fallback if file missing
return "
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
@app.route('/')
async def index(request):
return web_page(), 200, {'Content-Type': 'text/html'}
@app.route('/data')
async def get_data(request):
# 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
# 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
values=app.bme208.values
bme_t=values[0]
bme_p=values[1]
bme_rh=values[2]
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_p,
'bme_p':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'))