""" ____ _ ____ _ ____ _ ____ _ / ___| / \ | _ \ / \ / ___| ___ _ __ __| | | _ \ __ _| |_ __ _ \___ \ / _ \ | |_) | / _ \ \___ \ / _ \ '_ \ / _` | | | | |/ _` | __/ _` | ___) / ___ \| _ < / ___ \ ___) | __/ | | | (_| | | |_| | (_| | || (_| | |____/_/ \_\_| \_\/_/ \_\ |____/ \___|_| |_|\__,_| |____/ \__,_|\__\__,_| Main loop to gather data from sensor inside SQLite database: * NPM * Envea * I2C BME280 * Noise sensor and send it to AirCarto servers via SARA R4 HTTP post requests also send the timestamp (already stored inside the DB) ! /usr/bin/python3 /var/www/nebuleair_pro_4g/loop/SARA_send_data_v2.py ATTENTION: # This script is triggered every minutes by /var/www/nebuleair_pro_4g/master.py (as a service) CSV PAYLOAD (AirCarto Servers) Endpoint: data.nebuleair.fr /pro_4G/data.php?sensor_id={device_id}×tamp={rtc_module_time} ATTENTION : do not change order ! CSV size: 18 {PM1},{PM25},{PM10},{temp},{hum},{press},{avg_noise},{max_noise},{min_noise},{envea_no2},{envea_h2s},{envea_o3},{4g_signal_quality} 0 -> PM1 (μg/m3) 1 -> PM25 (μg/m3) 2 -> PM10 (μg/m3) 3 -> temp 4 -> hum 5 -> press 6 -> avg_noise 7 -> max_noise 8 -> min_noise 9 -> envea_no2 10 -> envea_h2s 11 -> envea_o3 12 -> 4G signal quality, 13 -> PM 0.2μm to 0.5μm quantity (Nb/L) 14 -> PM 0.5μm to 1.0μm quantity (Nb/L) 15 -> PM 1.0μm to 2.5μm quantity (Nb/L) 16 -> PM 2.5μm to 5.0μm quantity (Nb/L) 17 -> PM 5.0μm to 10μm quantity (Nb/L) JSON PAYLOAD (Micro-Spot Servers) Same as NebuleAir wifi Endpoint: api-prod.uspot.probesys.net nebuleair?token=2AFF6dQk68daFZ port 443 {"nebuleairid": "82D25549434", "software_version": "ModuleAirV2-V1-042022", "sensordatavalues": [ {"value_type":"NPM_P0","value":"1.54"}, {"value_type":"NPM_P1","value":"1.54"}, {"value_type":"NPM_P2","value":"1.54"}, {"value_type":"NPM_N1","value":"0.02"}, {"value_type":"NPM_N10","value":"0.02"}, {"value_type":"NPM_N25","value":"0.02"}, {"value_type":"MHZ16_CO2","value":"793.00"}, {"value_type":"SGP40_VOC","value":"29915.00"}, {"value_type":"samples","value":"134400"}, {"value_type":"min_micro","value":"137"}, {"value_type":"max_micro","value":"155030"}, {"value_type":"interval","value":"145000"}, {"value_type":"signal","value":"-80"}, {"value_type":"latitude","value":"43.2964"}, {"value_type":"longitude","value":"5.36978"}, {"value_type":"state_npm","value":"State: 00000000"}, {"value_type":"BME280_temperature","value":"28.47"}, {"value_type":"BME280_humidity","value":"28.47"}, {"value_type":"BME280_pressure","value":"28.47"}, {"value_type":"CAIRSENS_NO2","value":"54"}, {"value_type":"CAIRSENS_H2S","value":"54"}, {"value_type":"CAIRSENS_O3","value":"54"} ] } """ import board import json import serial import time import busio import re import os import traceback import sys import sqlite3 import RPi.GPIO as GPIO from threading import Thread # Record the start time of the script start_time_script = time.time() # Check system uptime with open('/proc/uptime', 'r') as f: uptime_seconds = float(f.readline().split()[0]) # Skip execution if uptime is less than 2 minutes (120 seconds) if uptime_seconds < 120: print(f"System just booted ({uptime_seconds:.2f} seconds uptime), skipping execution.") sys.exit() #Payload CSV to be sent to data.nebuleair.fr payload_csv = [None] * 20 #Payload JSON to be sent to uSpot payload_json = { "nebuleairid": "XXX", "software_version": "ModuleAirV2-V1-042022", "sensordatavalues": [] # Empty list to start with } # SARA R4 UHTTPC profile IDs aircarto_profile_id = 0 uSpot_profile_id = 1 # database connection conn = sqlite3.connect("/var/www/nebuleair_pro_4g/sqlite/sensors.db") cursor = conn.cursor() def blink_led(pin, blink_count, delay=1): """ Blink an LED on a specified GPIO pin. Args: pin (int): GPIO pin number (BCM mode) to which the LED is connected. blink_count (int): Number of times the LED should blink. delay (float): Time in seconds for the LED to stay ON or OFF (default is 1 second). """ # GPIO setup GPIO.setwarnings(False) GPIO.setmode(GPIO.BCM) # Use BCM numbering GPIO.setup(pin, GPIO.OUT) # Set the specified pin as an output try: for _ in range(blink_count): GPIO.output(pin, GPIO.HIGH) # Turn the LED on #print(f"LED on GPIO {pin} is ON") time.sleep(delay) # Wait for the specified delay GPIO.output(pin, GPIO.LOW) # Turn the LED off #print(f"LED on GPIO {pin} is OFF") time.sleep(delay) # Wait for the specified delay finally: GPIO.cleanup(pin) # Clean up the specific pin to reset its state print(f"GPIO {pin} cleaned up") #get data from config def load_config(config_file): try: with open(config_file, 'r') as file: config_data = json.load(file) return config_data except Exception as e: print(f"Error loading config file: {e}") return {} #Fonction pour mettre à jour le JSON de configuration def update_json_key(file_path, key, value): """ Updates a specific key in a JSON file with a new value. :param file_path: Path to the JSON file. :param key: The key to update in the JSON file. :param value: The new value to assign to the key. """ try: # Load the existing data with open(file_path, "r") as file: data = json.load(file) # Check if the key exists in the JSON file if key in data: data[key] = value # Update the key with the new value else: print(f"Key '{key}' not found in the JSON file.") return # Write the updated data back to the file with open(file_path, "w") as file: json.dump(data, file, indent=2) # Use indent for pretty printing print(f"updating '{key}' to '{value}'.") except Exception as e: print(f"Error updating the JSON file: {e}") # Define the config file path config_file = '/var/www/nebuleair_pro_4g/config.json' # Load the configuration data config = load_config(config_file) baudrate = config.get('SaraR4_baudrate', 115200) #baudrate du sara R4 device_id = config.get('deviceID', '').upper() #device ID en maj need_to_log = config.get('loop_log', False) #inscription des logs send_aircarto = config.get('send_aircarto', True) #envoi sur AirCarto (data.nebuleair.fr) send_uSpot = config.get('send_uSpot', False) #envoi sur MicroSpot () selected_networkID = config.get('SARA_R4_neworkID', '') #update device id in the payload json payload_json["nebuleairid"] = device_id ser_sara = serial.Serial( port='/dev/ttyAMA2', baudrate=baudrate, #115200 ou 9600 parity=serial.PARITY_NONE, #PARITY_NONE, PARITY_EVEN or PARITY_ODD stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS, timeout = 2 ) def read_complete_response(serial_connection, timeout=2, end_of_response_timeout=2, wait_for_line=None, debug=True): ''' Fonction très importante !!! ''' response = bytearray() serial_connection.timeout = timeout end_time = time.time() + end_of_response_timeout start_time = time.time() while True: elapsed_time = time.time() - start_time # Time since function start if serial_connection.in_waiting > 0: data = serial_connection.read(serial_connection.in_waiting) response.extend(data) end_time = time.time() + end_of_response_timeout # Reset timeout on new data # Decode and check for the specific line if wait_for_line: decoded_response = response.decode('utf-8', errors='replace') if wait_for_line in decoded_response: if debug: print(f"[DEBUG] 🔎Found target line: {wait_for_line}") break elif time.time() > end_time: if debug: print(f"[DEBUG] Timeout reached. No more data received.") break time.sleep(0.1) # Short sleep to prevent busy waiting # Final response and debug output total_elapsed_time = time.time() - start_time if debug: print(f"[DEBUG] ⏱️ elapsed time: {total_elapsed_time:.2f}s. ⏱️") # Check if the elapsed time exceeded 10 seconds if total_elapsed_time > 10 and debug: print(f"[ALERT] 🚨 The operation took too long🚨") print(f'[ALERT] ⚠️{total_elapsed_time:.2f}s⚠️') return response.decode('utf-8', errors='replace') try: print('
') print(response2) print("
") match = re.search(r'\+CSQ:\s*(\d+),', response2) if match: signal_quality = int(match.group(1)) payload_csv[12]=signal_quality time.sleep(0.1) # On vérifie si le signal n'est pas à 99 pour déconnexion # si c'est le cas on essaie de se reconnecter if signal_quality == 99: print('⚠️ATTENTION: Signal Quality indicates no signal (99)⚠️') print("TRY TO RECONNECT:") command = f'AT+COPS=1,2,"{selected_networkID}"\r' ser_sara.write(command.encode('utf-8')) responseReconnect = read_complete_response(ser_sara, timeout=20, end_of_response_timeout=20) print('') print(responseReconnect) print("
") print('🛑STOP LOOP🛑') print("