""" _____ _ ___ _______ _ | ____| \ | \ \ / / ____| / \ | _| | \| |\ \ / /| _| / _ \ | |___| |\ | \ V / | |___ / ___ \ |_____|_| \_| \_/ |_____/_/ \_\ Gather data from envea Sensors and store them to the SQlite table Use the RTC time for the timestamp This script is run by a service nebuleair-envea-data.service /usr/bin/python3 /var/www/nebuleair_pro_4g/envea/read_value_v2.py -d """ import json import serial import time import traceback import sqlite3 from datetime import datetime import sys # Set DEBUG to True to enable debug prints, False to disable DEBUG = False # Change this to False to disable debug output # You can also control debug via command line argument if len(sys.argv) > 1 and sys.argv[1] in ['--debug', '-d']: DEBUG = True elif len(sys.argv) > 1 and sys.argv[1] in ['--quiet', '-q']: DEBUG = False def debug_print(message): """Print debug messages only if DEBUG is True""" if DEBUG: timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") print(f"[{timestamp}] {message}") debug_print("=== ENVEA Sensor Reader Started ===") # Connect to the SQLite database try: conn = sqlite3.connect("/var/www/nebuleair_pro_4g/sqlite/sensors.db") cursor = conn.cursor() except Exception as e: debug_print(f"✗ Failed to connect to database: {e}") sys.exit(1) # GET RTC TIME from SQlite try: cursor.execute("SELECT * FROM timestamp_table LIMIT 1") row = cursor.fetchone() # Get the first (and only) row rtc_time_str = row[1] # '2025-02-07 12:30:45' except Exception as e: debug_print(f"✗ Failed to get RTC time: {e}") rtc_time_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S") debug_print(f" Using system time instead: {rtc_time_str}") # Fetch connected ENVEA sondes from SQLite config table try: cursor.execute("SELECT port, name, coefficient FROM envea_sondes_table WHERE connected = 1") connected_envea_sondes = cursor.fetchall() # List of tuples (port, name, coefficient) debug_print(f"✓ Found {len(connected_envea_sondes)} connected ENVEA sensors") for port, name, coefficient in connected_envea_sondes: debug_print(f" - {name}: port={port}, coefficient={coefficient}") except Exception as e: debug_print(f"✗ Failed to fetch connected sensors: {e}") connected_envea_sondes = [] serial_connections = {} if connected_envea_sondes: debug_print("\n--- Opening Serial Connections ---") for port, name, coefficient in connected_envea_sondes: try: serial_connections[name] = serial.Serial( port=f'/dev/{port}', baudrate=9600, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS, timeout=1 ) debug_print(f"✓ Opened serial port for {name} on /dev/{port}") except serial.SerialException as e: debug_print(f"✗ Error opening serial port for {name}: {e}") else: debug_print("! No connected ENVEA sensors found in configuration") # Initialize sensor data variables global data_h2s, data_no2, data_o3, data_co, data_nh3, data_so2 data_h2s = 0 data_no2 = 0 data_o3 = 0 data_co = 0 data_nh3 = 0 data_so2 = 0 try: if connected_envea_sondes: debug_print("\n--- Reading Sensor Data ---") for port, name, coefficient in connected_envea_sondes: if name in serial_connections: serial_connection = serial_connections[name] try: debug_print(f"Reading from {name}...") # Flush input buffer to clear any stale data serial_connection.reset_input_buffer() # Send command to sensor command = b"\xFF\x02\x13\x30\x01\x02\x03\x04\x05\x06\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x12\xAF\x88\x03" serial_connection.write(command) debug_print(f" → Sent command: {command.hex()}") # Wait for sensor response time.sleep(0.5) # Read fixed number of bytes (not readline which stops at 0x0A) data_envea = serial_connection.read(32) # Read fixed 32 bytes debug_print(f" ← Received {len(data_envea)} bytes: {data_envea.hex() if data_envea else 'empty'}") # Validate frame: should start with 0xFF 0x02 and have at least 20 bytes if len(data_envea) >= 20: # Check frame header (0xFF 0x02 expected for valid CAIRSENS response) if data_envea[0] == 0xFF and data_envea[1] == 0x02: byte_20 = data_envea[19] raw_value = byte_20 calculated_value = byte_20 * coefficient debug_print(f" → Byte 20 value: {raw_value} (0x{raw_value:02X})") debug_print(f" → Calculated value: {raw_value} × {coefficient} = {calculated_value}") if name == "h2s": data_h2s = calculated_value elif name == "no2": data_no2 = calculated_value elif name == "o3": data_o3 = calculated_value elif name == "co": data_co = calculated_value elif name == "nh3": data_nh3 = calculated_value elif name == "so2": data_so2 = calculated_value debug_print(f" ✓ {name.upper()} = {calculated_value}") else: debug_print(f" ✗ Invalid frame header: expected FF 02, got {data_envea[0]:02X} {data_envea[1]:02X}") else: debug_print(f" ✗ Response too short ({len(data_envea)} bytes, expected ≥20)") except serial.SerialException as e: debug_print(f"✗ Error communicating with {name}: {e}") else: debug_print(f"! No serial connection available for {name}") except Exception as e: debug_print(f"\n✗ An error occurred while gathering data: {e}") traceback.print_exc() # Display all collected data debug_print(f"\n--- Collected Sensor Data ---") debug_print(f"H2S: {data_h2s} ppb") debug_print(f"NO2: {data_no2} ppb") debug_print(f"O3: {data_o3} ppb") debug_print(f"CO: {data_co} ppb") debug_print(f"NH3: {data_nh3} ppb") debug_print(f"SO2: {data_so2} ppb") # Save to sqlite database try: cursor.execute(''' INSERT INTO data_envea (timestamp, h2s, no2, o3, co, nh3, so2) VALUES (?,?,?,?,?,?,?)''' , (rtc_time_str, data_h2s, data_no2, data_o3, data_co, data_nh3, data_so2)) # Commit and close the connection conn.commit() except Exception as e: debug_print(f"✗ Database error: {e}") traceback.print_exc() # Close serial connections if serial_connections: for name, connection in serial_connections.items(): try: connection.close() except: pass conn.close() debug_print("\n=== ENVEA Sensor Reader Finished ===\n")