refactor part 1

This commit is contained in:
2026-06-23 22:17:01 -04:00
parent 214cf50908
commit d5bb7ff24f
18 changed files with 508 additions and 398 deletions

View File

@@ -1,4 +1,4 @@
import time, math, board, smbus2, serial, pynmea2, bme680
import time, math, board, smbus2, serial, psutil, pynmea2, bme680
import adafruit_ltr390, threading, os
from datetime import datetime, timezone
from collections import deque
@@ -13,15 +13,15 @@ def cfg():
load_dotenv(BASE_DIR / '.env', override=True)
load_dotenv(BASE_DIR / '.env.local', override=True)
return {
'LATITUDE': float(os.getenv('LATITUDE', '0.0')),
'LONGITUDE': float(os.getenv('LONGITUDE', '0.0')),
'ALTITUDE': float(os.getenv('ALTITUDE', '0.0')),
'INFLUX_URL': os.getenv('INFLUX_URL', 'http://localhost:8086'),
'INFLUX_TOKEN': os.getenv('INFLUX_TOKEN', ''),
'INFLUX_ORG': os.getenv('INFLUX_ORG', 'weather'),
'INFLUX_BUCKET': os.getenv('INFLUX_BUCKET', 'station'),
'GPS_PORT': os.getenv('GPS_PORT', '/dev/ttyS0'),
'GPS_BAUD': int( os.getenv('GPS_BAUD', '9600')),
'DEFAULT_LAT': float(os.getenv('DEFAULT_LAT', '0.0')),
'DEFAULT_LON': float(os.getenv('DEFAULT_LON', '0.0')),
'DEFAULT_ALT': float(os.getenv('DEFAULT_ALT', '0.0')),
'GAS_REFERENCE': int( os.getenv('GAS_REFERENCE', '250000')),
'PRESSURE_TREND_WINDOW':int(os.getenv('PRESSURE_TREND_WINDOW', '60')),
'LUNA_CAL_SAMPLES': int( os.getenv('LUNA_CAL_SAMPLES', '20')),
@@ -151,10 +151,10 @@ def get_pressure_trend(window):
def read_bme(c, sensor, alt_m=None):
null = {
'env_temp_c': None, 'env_temp_f': None, 'env_humidity': None,
'env_dew_point_c': None, 'env_pressure_hpa': None, 'env_pressure_slp': None,
'env_pressure_rate': None, 'env_abs_humidity': None, 'env_vpd_kpa': None,
'env_heat_index_c': None, 'env_gas_ohms': None, 'env_aqi_score': None,
'temperature': None, 'humidity': None, 'humidity_abs': None,
'dew_point': None, 'pressure_hpa': None, 'pressure_slp': None,
'pressure_rate': None, 'vapor_pressure_deficit': None,
'heat_index': None, 'air_quality_ohms': None, 'air_quality': None,
}
if not sensor:
write(c, 'environment', null)
@@ -168,22 +168,21 @@ def read_bme(c, sensor, alt_m=None):
p_rate, p_label = get_pressure_trend(c['PRESSURE_TREND_WINDOW'])
dp = round(t - (100-rh)/5.0, 2)
write(c, 'environment', {
'env_temp_c': round(t, 2),
'env_temp_f': round(t*9/5+32, 2),
'env_humidity': round(rh, 2),
'env_dew_point_c': dp,
'env_pressure_hpa': round(p, 2),
'env_pressure_slp': sea_level_pressure(p, alt_m),
'env_pressure_rate': p_rate,
'env_abs_humidity': absolute_humidity(t, rh),
'env_vpd_kpa': vpd(t, rh),
'env_heat_index_c': heat_index(t, rh),
'env_gas_ohms': gas,
'env_aqi_score': aqi_score,
'temperature': round(t, 2),
'humidity': round(rh, 2),
'dew_point': dp,
'pressure_hpa': round(p, 2),
'pressure_slp': sea_level_pressure(p, alt_m),
'pressure_rate': p_rate,
'humidity_abs': absolute_humidity(t, rh),
'vapor_pressure_deficit': vpd(t, rh),
'heat_index': heat_index(t, rh),
'air_quality_ohms': gas,
'air_quality': aqi_score,
}, tags={
'env_aqi_label': aqi_label or '',
'env_pressure_trend': p_label or '',
'env_frost_risk': frost_risk(t, dp),
'air_quality_label': aqi_label or '',
'pressure_trend': p_label or '',
'frost_risk': frost_risk(t, dp),
})
return t, rh
except Exception as e:
@@ -226,14 +225,14 @@ def mpu_loop():
ax = s16((d[0] << 8)|d[1]) / 16384.0 - o[0]
ay = s16((d[2] << 8)|d[3]) / 16384.0 - o[1]
az = s16((d[4] << 8)|d[5]) / 16384.0 - o[2]
mag = math.sqrt(ax**2 + ay**2 + az**2)
mag = math.sqrt(ax**2 + ay**2 + (az - 1.0)**2)
with _mpu_lock:
if _mpu_peak is None or mag > _mpu_peak['seismic_magnitude']:
_mpu_peak = {
'seismic_ax': round(ax, 3),
'seismic_ay': round(ay, 3),
'seismic_az': round(az, 3),
'seismic_magnitude': round(mag, 4),
'seismic_x': round(ax, 3),
'seismic_y': round(ay, 3),
'seismic_z': round(az, 3),
'magnitude': round(mag, 4),
}
time.sleep(0.01)
@@ -247,7 +246,7 @@ def flush_mpu(c):
# ── QMC5883L ──────────────────────────────────────────────────────────────────
def read_compass(c):
null = {'compass_x': None, 'compass_y': None, 'compass_z': None, 'compass_heading': None}
null = {'x': None, 'y': None, 'z': None, 'heading': None}
if not bus: write(c, 'compass', null); return
try:
addr = 0x1E
@@ -261,7 +260,7 @@ def read_compass(c):
if heading < 0: heading += 360
write(c, 'compass', {
'compass_x': x, 'compass_y': y, 'compass_z': z,
'compass_heading': round(heading, 1),
'heading': round(heading, 1),
})
except Exception as e:
print(f"[QMC5883L] {e}")
@@ -289,10 +288,9 @@ def read_ltr(c, sensor, solar_el, humidity=None):
global dli_lux_acc, dli_date, last_lux_time, daylight_start
null = {
'light_lux': None, 'light_uvi': None, 'light_solar_wm2': None,
'light_uv_dose_mj': None, 'light_burn_time_min': None,
'light_cloud_pct': None, 'light_dli': None,
'light_daylight_hours': None, 'light_visibility_km': None,
'lux': None, 'uv_index': None, 'solar_wm2': None,
'uv_dose': None, 'clouds': None, 'light_dli': None,
'daylight': None, 'visibility': None,
}
if not sensor: write(c, 'light', null); return
@@ -342,16 +340,15 @@ def read_ltr(c, sensor, solar_el, humidity=None):
)
write(c, 'light', {
'light_lux': lux,
'light_uvi': uvi,
'light_solar_wm2': round(lux / 120, 2),
'light_uv_dose_mj': round(uv_dose_mj, 2),
'light_burn_time_min': burn_min,
'light_cloud_pct': cloud_pct,
'lux': lux,
'uv_index': uvi,
'solar_wm2': round(lux / 120, 2),
'uv_dose': round(uv_dose_mj, 2),
'clouds': cloud_pct,
'light_dli': dli,
'light_daylight_hours':daylight_hours,
'light_visibility_km': visibility_estimate(lux, humidity) if humidity else None,
}, tags={'light_cloud_label': cloud_label or ''})
'daylight':daylight_hours,
'visibility': visibility_estimate(lux, humidity) if humidity else None,
}, tags={'clouds_label': cloud_label or ''})
# ── TF-Luna ───────────────────────────────────────────────────────────────────
@@ -376,17 +373,17 @@ def calibrate_luna(c):
print(" ❌ TF-Luna: calibration failed")
def read_luna(c, temp_c):
null = {'ground_distance_cm': None, 'ground_lidar_strength': None}
if not bus: write(c, 'ground', null); return
null = {'ground_distance': None, 'lidar_strength': None}
if not bus: write(c, 'accumulation', null); return
try:
d = try_read(lambda: bus.read_i2c_block_data(0x10, 0x00, 6))
if not d: write(c, 'ground', null); return
dist = d[0]|(d[1]<<8)
strength = d[2]|(d[3]<<8)
if strength < 100 or dist <= 0 or dist > c['LUNA_MAX_DIST_CM']:
write(c, 'ground', null); return
write(c, 'accumulation', null); return
fields = {'ground_distance_cm': dist, 'ground_lidar_strength': strength}
fields = {'ground_distance': dist, 'lidar_strength': strength}
if luna_baseline_cm and (luna_baseline_cm - dist) >= c['LUNA_MIN_DEPTH_CM']:
t = temp_c
@@ -396,14 +393,14 @@ def read_luna(c, temp_c):
'slush' if t is not None and t > c['SLUSH_TEMP_THRESHOLD'] else
'snow' if strength > c['SNOW_STRENGTH_MIN'] else 'ice'
)
fields['ground_calibrated_baseline_cm'] = luna_baseline_cm
fields['ground_accumulation_depth_cm'] = depth
write(c, 'accumulation', fields, tags={'ground_accumulation_type': kind})
fields['ground_calibration'] = luna_baseline_cm
fields['accumulation'] = depth
write(c, 'accumulation', fields, tags={'accumulation_type': kind})
else:
write(c, 'ground', fields)
write(c, 'accumulation', fields)
except Exception as e:
print(f"[TF-Luna] {e}")
write(c, 'ground', null)
write(c, 'accumulation', null)
# ── AS3935 ───────────────────────────────────────────────────────────────────
@@ -450,12 +447,12 @@ def read_as3935(c):
if avg_late < avg_early - 2: trend = 'Approaching'
elif avg_late > avg_early + 2: trend = 'Retreating'
write(c, 'lightning', {
'lightning_distance_km': dist,
'lightning_distance': dist,
'lightning_energy': energy,
'lightning_detector_sensitivity': c['AS3935_NOISE_FLOOR'],
'lightning_false_positive': 1 if interrupt == 0x04 else 0,
'lightning_strikes_per_hour': strike_rate,
}, tags={'lightning_storm_trend': trend})
}, tags={'storm_direction': trend})
except Exception as e:
print(f"[AS3935] {e}")
@@ -471,25 +468,16 @@ def read_gps(c):
if 'GGA' in line:
msg = pynmea2.parse(line)
gps_cache.update({
'gps_lat': msg.latitude,
'gps_lon': msg.longitude,
'gps_alt_m': msg.altitude,
'latitude': msg.latitude,
'longitude': msg.longitude,
'altitude': msg.altitude,
'gps_satellites': int(msg.num_sats),
})
elif 'VTG' in line:
msg = pynmea2.parse(line)
gps_cache['gps_speed_kmh'] = msg.spd_over_grnd_kmph
elif 'RMC' in line:
msg = pynmea2.parse(line)
gps_cache['gps_heading'] = msg.true_course
if gps_cache:
write(c, 'gps', {k: v for k, v in gps_cache.items() if v is not None})
except Exception as e:
print(f"[GPS] {e}")
write(c, 'gps', {
'gps_lat': None, 'gps_lon': None, 'gps_alt_m': None,
'gps_satellites': None, 'gps_speed_kmh': None, 'gps_heading': None,
})
write(c, 'gps', {'latitude': None, 'longitude': None, 'altitude': None,'gps_satellites': None})
return gps_cache
def solar_elevation(lat, lon):
@@ -504,6 +492,36 @@ def solar_elevation(lat, lon):
))
return round(el, 2)
# ── System ────────────────────────────────────────────────────────────────────
def read_system(c):
try:
# CPU temp (Linux hwmon/thermal)
cpu_temp = None
temps = psutil.sensors_temperatures()
for key in ('cpu_thermal', 'coretemp', 'k10temp', 'acpitz'):
if key in temps:
cpu_temp = round(temps[key][0].current, 1)
break
cpu_freq = psutil.cpu_freq()
vm = psutil.virtual_memory()
disk = psutil.disk_usage('/')
write(c, 'system', {
'cpu_temp': cpu_temp,
'cpu_usage': round(psutil.cpu_percent(interval=None), 1),
'cpu_freq_mhz': round(cpu_freq.current, 1) if cpu_freq else None,
'mem_used_mb': round(vm.used / 1024**2, 1),
'mem_total_mb': round(vm.total / 1024**2, 1),
'mem_percent': vm.percent,
'disk_used_gb': round(disk.used / 1024**3, 2),
'disk_total_gb': round(disk.total / 1024**3, 2),
'disk_percent': disk.percent,
})
except Exception as e:
print(f"[System] {e}")
# ── Main ──────────────────────────────────────────────────────────────────────
if __name__ == '__main__':
@@ -534,9 +552,9 @@ if __name__ == '__main__':
c = cfg()
gps = read_gps(c)
lat = gps.get('gps_lat') or c['DEFAULT_LAT']
lon = gps.get('gps_lon') or c['DEFAULT_LON']
alt = gps.get('gps_alt_m') or c['DEFAULT_ALT']
lat = gps.get('latitude') or c['LATITUDE']
lon = gps.get('longitude') or c['LONGITUDE']
alt = gps.get('altitude') or c['ALTITUDE']
sol = solar_elevation(lat, lon) if lat and lon else None
result = read_bme(c, bme_sensor, alt_m=alt)
@@ -548,5 +566,6 @@ if __name__ == '__main__':
read_ltr(c, ltr_sensor, sol, humidity=last_rh)
read_luna(c, last_temp)
read_as3935(c)
read_system(c)
time.sleep(c['LOOP_INTERVAL'])

View File

@@ -3,6 +3,7 @@ python-dotenv
smbus2
adafruit-circuitpython-ltr390
bme680
psutil
pyserial
pynmea2
RPi.GPIO