refactor part 1
This commit is contained in:
159
sensors/main.py
159
sensors/main.py
@@ -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'])
|
||||
|
||||
Reference in New Issue
Block a user