# Pico W / Wokwi : Sensor simulator + JSON pipeline (list window)
from machine import Pin, I2C
import time, math, json, sys
SOURCE = "wokwi-pico"
I2C_ADDR = 0x76
SDA_PIN, SCL_PIN = 4, 5
SAMPLE_EVERY_SECS = 2
AGG_BUCKET_SECS = 60
def safe_flush():
try:
sys.stdout.flush()
except Exception:
pass
# -------- Sensor layer --------
def try_import_bme280():
try:
import bme280 # noqa
return bme280
except ImportError:
return None
bme280 = try_import_bme280()
def read_bme280(i2c):
try:
sensor = bme280.BME280(i2c=i2c, address=I2C_ADDR)
except TypeError:
sensor = bme280.BME280(i2c=i2c)
t_str, p_str, h_str = sensor.values
def num(s): return float("".join(ch for ch in s if ch.isdigit() or ch in ".-"))
return round(num(t_str), 2), round(num(h_str), 2), round(num(p_str), 2)
def simulate_env():
now = time.time()
t = 22 + 4*math.sin(now/3600) + math.sin(now*3)*0.1
h = 48 + 8*math.sin(now/1800+1.1) + math.sin(now*2.7)*0.8
p = 1013 + 3*math.sin(now/5400+0.6) + math.sin(now*1.3)*0.3
return round(t,2), round(h,2), round(p,2)
def get_reading(i2c):
if bme280 is not None:
try:
return read_bme280(i2c)
except Exception as e:
print(json.dumps({"type":"sensor_error","msg":str(e)}))
return simulate_env()
return simulate_env()
# -------- Validation / Aggregation --------
def valid(t,h,p): return (-40<=t<=85) and (0<=h<=100) and (800<=p<=1200)
def floor_bucket(ts_ms, bucket_secs): return (ts_ms // (bucket_secs*1000)) * (bucket_secs*1000)
# -------- Main --------
i2c = I2C(0, sda=Pin(SDA_PIN), scl=Pin(SCL_PIN), freq=100000)
MODE = "BME280" if bme280 is not None else "SIMULATED"
print(json.dumps({"type":"health","ok":True,"mode":MODE,"source":SOURCE}))
safe_flush()
window = [] # [(ts_ms, t, h, p)]
current_bucket = floor_bucket(int(time.time()*1000), AGG_BUCKET_SECS)
while True:
ts_ms = int(time.time()*1000)
t,h,p = get_reading(i2c)
if valid(t,h,p):
print(json.dumps({
"type":"reading",
"ts_utc_ms": ts_ms,
"temperature_c": t,
"humidity_pct": h,
"pressure_hpa": p,
"source": SOURCE
}))
safe_flush()
window.append((ts_ms,t,h,p))
cutoff = ts_ms - AGG_BUCKET_SECS*1000
# drop old items
i = 0
while i < len(window) and window[i][0] < cutoff:
i += 1
if i:
window = window[i:]
new_bucket = floor_bucket(ts_ms,AGG_BUCKET_SECS)
if new_bucket != current_bucket:
# items belonging to the completed bucket
bucket_items = [x for x in window if floor_bucket(x[0],AGG_BUCKET_SECS)==current_bucket]
if bucket_items:
temps=[x[1] for x in bucket_items]
hums=[x[2] for x in bucket_items]
pres=[x[3] for x in bucket_items]
print(json.dumps({
"type":"agg_minute",
"bucket_start_utc_ms": current_bucket,
"temperature":{"avg":round(sum(temps)/len(temps),2),"min":round(min(temps),2),"max":round(max(temps),2)},
"humidity":{"avg":round(sum(hums)/len(hums),2),"min":round(min(hums),2),"max":round(max(hums),2)},
"pressure":{"avg":round(sum(pres)/len(pres),2),"min":round(min(pres),2),"max":round(max(pres),2)}
}))
safe_flush()
current_bucket = new_bucket
else:
print(json.dumps({"type":"validation_error","ts_utc_ms":ts_ms,"t":t,"h":h,"p":p}))
safe_flush()
time.sleep(SAMPLE_EVERY_SECS)
Loading
pi-pico-w
pi-pico-w