import network import ntptime import time import socket import machine from dht20 import DHT20 from bme280 import BME280 # ========== USER CONFIGURATION ========== WIFI_SSID = "your_ssid" WIFI_PASSWORD = "your_password" # I2C pins for both sensors I2C_SDA_PIN = 4 # GP4 = physical pin 6 I2C_SCL_PIN = 5 # GP5 = physical pin 7 # CSV file name and maximum number of data rows (excluding header) CSV_FILE = "log.csv" MAX_ENTRIES = 1000 # Web server port HTTP_PORT = 80 # NTP settings NTP_RETRY_INTERVAL = 3600 # 1 hour – retry if sync fails NTP_SYNC_INTERVAL = 86400 # 24 hours – re-sync after successful sync # ======================================== # ---------- I2C Setup ---------- i2c = machine.I2C(0, sda=machine.Pin(I2C_SDA_PIN), scl=machine.Pin(I2C_SCL_PIN), freq=400000) # ---------- Sensor Initialisation ---------- dht20 = DHT20(0x38, i2c) bme280 = BME280(i2c, addr=0x76) # use 0x77 if your sensor has that address # ---------- Time helpers (UTC only) ---------- def utc_time_tuple(): return time.gmtime() # ---------- Wi‑Fi connection ---------- def connect_wifi(): wlan = network.WLAN(network.STA_IF) wlan.active(True) if not wlan.isconnected(): print("Connecting to Wi-Fi...") wlan.connect(WIFI_SSID, WIFI_PASSWORD) while not wlan.isconnected(): time.sleep(1) print("Wi-Fi connected:", wlan.ifconfig()) # ---------- Time synchronisation ---------- last_ntp_attempt = 0 last_ntp_success = 0 def sync_time(force=False): global last_ntp_attempt, last_ntp_success if not force: if time.time() - last_ntp_attempt < NTP_RETRY_INTERVAL: return False last_ntp_attempt = time.time() try: ntptime.settime() print("NTP sync successful. RTC updated to UTC:", time.gmtime()) last_ntp_success = time.time() return True except Exception as e: print("NTP sync failed:", e) return False def maintain_time(): global last_ntp_success if last_ntp_success == 0: sync_time() else: if time.time() - last_ntp_success >= NTP_SYNC_INTERVAL: sync_time(force=True) # ---------- CSV file management ---------- def ensure_csv_header(): try: with open(CSV_FILE, 'r') as f: pass except OSError: with open(CSV_FILE, 'w') as f: f.write("datetime_utc,temperature_c,humidity_pct,pressure_hPa\n") def append_csv_row(dt_str, temp_c, hum_pct, pressure_hpa): ensure_csv_header() with open(CSV_FILE, 'r') as f: lines = f.readlines() data_rows = lines[1:] if len(data_rows) >= MAX_ENTRIES: data_rows.pop(0) new_row = f"{dt_str},{temp_c:.2f},{hum_pct:.2f},{pressure_hpa:.2f}\n" data_rows.append(new_row) with open(CSV_FILE, 'w') as f: f.write(lines[0]) # header for row in data_rows: f.write(row) # ---------- Sensor reading ---------- def read_sensors(): """ Returns (temperature, humidity, pressure) If a sensor fails, its value is None. """ temp_dht = None hum = None pressure = None # Read DHT20 try: meas = dht20.measurements if meas['crc_ok']: temp_dht = meas['t'] hum = meas['rh'] else: print("DHT20 CRC error") except Exception as e: print("DHT20 read error:", e) # Read BME280 try: bme_temp, bme_press = bme280.read() # Note: we ignore BME280 temperature, only use pressure pressure = bme_press except Exception as e: print("BME280 read error:", e) return temp_dht, hum, pressure # ---------- Web server ---------- def start_web_server(): addr = socket.getaddrinfo('0.0.0.0', HTTP_PORT)[0][-1] server = socket.socket() server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind(addr) server.listen(1) server.settimeout(1) return server def handle_client(server): try: cl, addr = server.accept() print("Client from", addr) request = cl.recv(1024) try: with open(CSV_FILE, 'rb') as f: csv_data = f.read() response = ( "HTTP/1.1 200 OK\r\n" "Content-Type: text/csv\r\n" f"Content-Disposition: attachment; filename={CSV_FILE}\r\n" "Connection: close\r\n\r\n" ).encode() + csv_data cl.send(response) except Exception: cl.send(b"HTTP/1.1 404 Not Found\r\n\r\n") cl.close() except OSError: pass # timeout # ---------- Logging helper ---------- def log_now(): """Take readings and log them with current UTC time.""" temp, hum, pressure = read_sensors() if temp is not None and hum is not None and pressure is not None: t = utc_time_tuple() year, month, day, hour, minute, second = t[0:6] dt_str = f"{year:04d}-{month:02d}-{day:02d} {hour:02d}:{minute:02d}:{second:02d}" append_csv_row(dt_str, temp, hum, pressure) print(f"Logged (UTC): {dt_str} T={temp:.2f}°C H={hum:.2f}% P={pressure:.2f}hPa") return (year, month, day, hour) # return hour identifier else: print("Sensor read failed, no data logged.") return None # ---------- Main loop ---------- def main(): connect_wifi() maintain_time() # initial sync attempt (may fail) server = start_web_server() print("HTTP server on port", HTTP_PORT) # Log on boot time.sleep(1) # brief delay for RTC to settle if NTP just updated boot_hour = log_now() last_logged = boot_hour while True: handle_client(server) maintain_time() t = utc_time_tuple() year, month, day, hour, minute, second = t[0:6] if minute == 0 and second == 0: current_hour = (year, month, day, hour) if current_hour != last_logged: temp, hum, pressure = read_sensors() if temp is not None and hum is not None and pressure is not None: dt_str = f"{year:04d}-{month:02d}-{day:02d} {hour:02d}:{minute:02d}:{second:02d}" append_csv_row(dt_str, temp, hum, pressure) print(f"Logged (UTC): {dt_str} T={temp:.2f}°C H={hum:.2f}% P={pressure:.2f}hPa") last_logged = current_hour else: print("Sensor read failed, skipping this hour.") time.sleep(0.5) if __name__ == "__main__": main()