diff --git a/.gitignore b/.gitignore index 3c3c4a1..66219af 100755 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,6 @@ config.json sound_meter/moving_avg_minute.txt wifi_list.csv envea/data/*.txt +NPM/data/*.txt +NPM/data/*.json *.lock \ No newline at end of file diff --git a/NPM/firmware_version.py b/NPM/firmware_version.py old mode 100644 new mode 100755 diff --git a/NPM/get_data_modbus.py b/NPM/get_data_modbus.py old mode 100644 new mode 100755 index 40c9a51..98838f7 --- a/NPM/get_data_modbus.py +++ b/NPM/get_data_modbus.py @@ -14,7 +14,7 @@ Request \x01 Slave Address (slave device address) \x03 Function code (read multiple holding registers) \x00\x80 Starting Address (The request starts reading from holding register address 0x80 or 128) -\x00\x0A Quantity of Registers (Requests to read 0x0A or 10 consecutive registers starting from address 50) +\x00\x0A Quantity of Registers (Requests to read 0x0A or 10 consecutive registers starting from address 128) \xE4\x1E Cyclic Redundancy Check (checksum ) ''' diff --git a/NPM/get_data_modbus_loop.py b/NPM/get_data_modbus_loop.py new file mode 100644 index 0000000..0882e1e --- /dev/null +++ b/NPM/get_data_modbus_loop.py @@ -0,0 +1,122 @@ +''' +Loop to run every minutes +* * * * * /usr/bin/python3 /var/www/nebuleair_pro_4g/NPM/get_data_modbus_loop.py + +saves data to a json file /var/www/nebuleair_pro_4g/NPM/data/data.json +''' + +import serial +import sys +import crcmod +import time +import json +import os + +# Ensure a port argument is provided +if len(sys.argv) < 2: + print("Usage: python3 get_data_modbus.py ") + sys.exit(1) + +port = '/dev/' + sys.argv[1] + +# Initialize serial communication +try: + ser = serial.Serial( + port=port, + baudrate=115200, + parity=serial.PARITY_EVEN, + stopbits=serial.STOPBITS_ONE, + bytesize=serial.EIGHTBITS, + timeout=0.5 + ) +except Exception as e: + print(f"Error opening serial port {port}: {e}") + sys.exit(1) + +# Define Modbus CRC-16 function +crc16 = crcmod.predefined.mkPredefinedCrcFun('modbus') + +# Request frame without CRC +data = b'\x01\x03\x00\x80\x00\x0A' + +# Calculate CRC +crc = crc16(data) +crc_low = crc & 0xFF +crc_high = (crc >> 8) & 0xFF + +# Append CRC to the frame +request = data + bytes([crc_low, crc_high]) + +# Log request frame +print(f"Request frame: {request.hex()}") + +# Initialize storage for averaging +num_samples = 6 +channel_sums = [0] * 5 # 5 channels + +# Loop 6 times to collect data every 10 seconds +for i in range(num_samples): + print(f"\nIteration {i+1}/{num_samples}") + ser.write(request) + + try: + byte_data = ser.readline() + formatted = ''.join(f'\\x{byte:02x}' for byte in byte_data) + print(f"Raw Response: {formatted}") + + if len(byte_data) < 23: + print("Incomplete response, skipping this sample.") + time.sleep(10) + continue + + # Extract and process the 5 channels + channels = [] + for j in range(5): + lsw = int.from_bytes(byte_data[3 + j*4 : 5 + j*4], byteorder='little') + msw = int.from_bytes(byte_data[5 + j*4 : 7 + j*4], byteorder='little') + raw_value = (msw << 16) | lsw + channels.append(raw_value) + + # Accumulate sum for each channel + for j in range(5): + channel_sums[j] += channels[j] + + # Print collected values + for j in range(5): + print(f"Channel {j+1}: {channels[j]}") + + except Exception as e: + print(f"Error reading data: {e}") + + # Wait 10 seconds before next reading + time.sleep(10) + +# Compute the average values +channel_means = [int(s / num_samples) for s in channel_sums] + +# Create JSON structure +data_json = { + "channel_1": channel_means[0], + "channel_2": channel_means[1], + "channel_3": channel_means[2], + "channel_4": channel_means[3], + "channel_5": channel_means[4] +} + +# Print final JSON data +print("\nFinal JSON Data (Averaged over 6 readings):") +print(json.dumps(data_json, indent=4)) + +# Define JSON file path +output_file = "/var/www/nebuleair_pro_4g/NPM/data/data.json" + +# Write results to a JSON file +try: + with open(output_file, "w") as f: + json.dump(data_json, f, indent=4) + print(f"\nAveraged JSON data saved to {output_file}") +except Exception as e: + print(f"Error writing to file: {e}") + +# Close serial connection +ser.close() diff --git a/SARA/SSL/test_22.py b/SARA/SSL/test_22.py old mode 100644 new mode 100755 diff --git a/SARA/SSL/test_33.py b/SARA/SSL/test_33.py old mode 100644 new mode 100755 diff --git a/config.json.dist b/config.json.dist index 8315cfb..c49d167 100755 --- a/config.json.dist +++ b/config.json.dist @@ -8,6 +8,7 @@ "NextPM_ports": [ "ttyAMA5" ], + "NextPM_5channels": false, "i2C_sound": false, "i2c_BME": false, "sshTunnel_port": 59228, diff --git a/envea/read_value_loop.py b/envea/read_value_loop.py index 7de814e..b097a01 100755 --- a/envea/read_value_loop.py +++ b/envea/read_value_loop.py @@ -1,6 +1,8 @@ """ Main loop to gather data from envea Sensors /usr/bin/python3 /var/www/nebuleair_pro_4g/envea/read_value_loop.py + +Save data to .txt file inside /var/www/nebuleair_pro_4g/envea/data/ """ import json import serial diff --git a/loop/1_NPM/send_data.py b/loop/1_NPM/send_data.py index 52a088b..45c8357 100755 --- a/loop/1_NPM/send_data.py +++ b/loop/1_NPM/send_data.py @@ -12,10 +12,11 @@ CSV PAYLOAD (AirCarto Servers) /pro_4G/data.php?sensor_id={device_id} 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 - 1 -> PM25 - 2 -> PM10 + 0 -> PM1 (μg/m3) + 1 -> PM25 (μg/m3) + 2 -> PM10 (μg/m3) 3 -> temp 4 -> hum 5 -> press @@ -25,7 +26,12 @@ CSV PAYLOAD (AirCarto Servers) 9 -> envea_no2 10 -> envea_h2s 11 -> envea_o3 - 12 -> 4G signal quality + 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 @@ -178,6 +184,7 @@ bme_280_config = config.get('i2c_BME', False) #présence du BME280 i2C_sound_config = config.get('i2C_sound', False) #présence du capteur son send_aircarto = config.get('send_aircarto', True) #envoi sur AirCarto (data.nebuleair.fr) send_uSpot = config.get('send_uSpot', False) #envoi sur MicroSpot () +npm_5channel = config.get('NextPM_5channels', False) #5 canaux du NPM envea_sondes = config.get('envea_sondes', []) connected_envea_sondes = [sonde for sonde in envea_sondes if sonde.get('connected', False)] @@ -307,7 +314,45 @@ try: payload_json["sensordatavalues"].append({"value_type": "BME280_humidity", "value": f"{round(bme280.humidity, 2)}"}) payload_json["sensordatavalues"].append({"value_type": "BME280_pressure", "value": f"{round(bme280.pressure, 2)}"}) - + # NPM sur 5 cannaux + if npm_5channel: + print("Getting NPM 5 Channels") + # Define the path to the JSON file + json_file_path_npm = "/var/www/nebuleair_pro_4g/NPM/data/data.json" + # Read the JSON file + try: + with open(json_file_path_npm, "r") as file: + data = json.load(file) # Load JSON into a dictionary + + # Extract values + channel_1 = data.get("channel_1", 0) + channel_2 = data.get("channel_2", 0) + channel_3 = data.get("channel_3", 0) + channel_4 = data.get("channel_4", 0) + channel_5 = data.get("channel_5", 0) + + # Print extracted values + print(f"Channel 1: {channel_1}") + print(f"Channel 2: {channel_2}") + print(f"Channel 3: {channel_3}") + print(f"Channel 4: {channel_4}") + print(f"Channel 5: {channel_5}") + + #add to CSV + payload_csv[13] = channel_1 + payload_csv[14] = channel_2 + payload_csv[15] = channel_3 + payload_csv[16] = channel_4 + payload_csv[17] = channel_5 + + + except FileNotFoundError: + print(f"Error: JSON file not found at {json_file_path_npm}") + except json.JSONDecodeError: + print("Error: JSON file is not formatted correctly") + except Exception as e: + print(f"Unexpected error: {e}") + # Sonde Bruit connected if i2C_sound_config: #on récupère les infos de sound_metermoving et on les ajoute au message @@ -324,7 +369,7 @@ try: payload_csv[8] = min_noise except FileNotFoundError: - print(f"Error: File {file_path} not found.") + print(f"Error: File {file_path_data_noise} not found.") except ValueError: print("Error: File content is not valid numbers.")