update
This commit is contained in:
312
loop/1_NPM/send_data_mqtt.py
Normal file
312
loop/1_NPM/send_data_mqtt.py
Normal file
@@ -0,0 +1,312 @@
|
||||
"""
|
||||
Main loop to gather data from sensor:
|
||||
* NPM
|
||||
* I2C BME280
|
||||
* Noise sensor
|
||||
and send it to AirCarto servers via SARA R4 MQTT requests
|
||||
"""
|
||||
import board
|
||||
import json
|
||||
import serial
|
||||
import time
|
||||
import busio
|
||||
|
||||
import RPi.GPIO as GPIO
|
||||
from adafruit_bme280 import basic as adafruit_bme280
|
||||
|
||||
# Record the start time of the script
|
||||
start_time = time.time()
|
||||
|
||||
url="data.nebuleair.fr"
|
||||
|
||||
# Set up GPIO mode (for Blue LED: network status)
|
||||
GPIO.setwarnings(False)
|
||||
GPIO.setmode(GPIO.BCM) # Use Broadcom pin numbering
|
||||
GPIO.setup(23, GPIO.OUT) # Set GPIO23 as an output pin
|
||||
|
||||
# Add yellow color to the output
|
||||
yellow_color = "\033[33m" # ANSI escape code for yellow
|
||||
red_color = "\033[31m" # ANSI escape code for red
|
||||
reset_color = "\033[0m" # Reset color to default
|
||||
green_color = "\033[32m" # Green
|
||||
|
||||
#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 {}
|
||||
|
||||
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"Successfully updated '{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)
|
||||
# Access the shared variables
|
||||
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
|
||||
bme_280_config = config.get('i2c_BME', False) #présence du BME280
|
||||
i2C_sound_config = config.get('i2C_sound', False) #présence du BME280
|
||||
|
||||
ser = 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
|
||||
)
|
||||
|
||||
ser_NPM = serial.Serial(
|
||||
port='/dev/ttyAMA5',
|
||||
baudrate=115200,
|
||||
parity=serial.PARITY_EVEN,
|
||||
stopbits=serial.STOPBITS_ONE,
|
||||
bytesize=serial.EIGHTBITS,
|
||||
timeout = 1
|
||||
)
|
||||
|
||||
def read_complete_response(serial_connection, timeout=2, end_of_response_timeout=2):
|
||||
response = bytearray()
|
||||
serial_connection.timeout = timeout
|
||||
end_time = time.time() + end_of_response_timeout
|
||||
|
||||
while True:
|
||||
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
|
||||
elif time.time() > end_time:
|
||||
break
|
||||
time.sleep(0.1) # Short sleep to prevent busy waiting
|
||||
|
||||
# Decode the response and filter out empty lines
|
||||
decoded_response = response.decode('utf-8')
|
||||
non_empty_lines = "\n".join(line for line in decoded_response.splitlines() if line.strip())
|
||||
|
||||
# Add yellow color to the output
|
||||
|
||||
colored_output = f"{yellow_color}{non_empty_lines}\n{reset_color}"
|
||||
|
||||
return colored_output
|
||||
|
||||
# Open and read the JSON file
|
||||
try:
|
||||
# Send the command to request data (e.g., data for 60 seconds)
|
||||
ser_NPM.write(b'\x81\x12\x6D')
|
||||
|
||||
# Read the response
|
||||
byte_data = ser_NPM.readline()
|
||||
|
||||
#if npm is disconnected byte_data is empty
|
||||
|
||||
# Extract the state byte and PM data from the response
|
||||
state_byte = int.from_bytes(byte_data[2:3], byteorder='big')
|
||||
state_bits = [int(bit) for bit in bin(state_byte)[2:].zfill(8)]
|
||||
|
||||
PM1 = int.from_bytes(byte_data[9:11], byteorder='big') / 10
|
||||
PM25 = int.from_bytes(byte_data[11:13], byteorder='big') / 10
|
||||
PM10 = int.from_bytes(byte_data[13:15], byteorder='big') / 10
|
||||
|
||||
# Create a dictionary with the parsed data
|
||||
data = {
|
||||
'sondeID': device_id,
|
||||
'PM1': PM1,
|
||||
'PM25': PM25,
|
||||
'PM10': PM10
|
||||
}
|
||||
|
||||
message = f"{data['PM1']},{data['PM25']},{data['PM10']}"
|
||||
|
||||
if bme_280_config:
|
||||
#on récupère les infos du BME280 et on les ajoute au message
|
||||
i2c = busio.I2C(board.SCL, board.SDA)
|
||||
bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c, address=0x76)
|
||||
bme280.sea_level_pressure = 1013.25 # Update this value for your location
|
||||
data['temp'] = round(bme280.temperature, 2)
|
||||
data['hum'] = round(bme280.humidity, 2)
|
||||
data['press'] = round(bme280.pressure, 2)
|
||||
message += f",{data['temp']},{data['hum']},{data['press']}"
|
||||
|
||||
if i2C_sound_config:
|
||||
#on récupère les infos de sound_metermoving et on les ajoute au message
|
||||
file_path_data_noise = "/var/www/nebuleair_pro_4g/sound_meter/moving_avg_minute.txt"
|
||||
# Read the file and extract the numbers
|
||||
try:
|
||||
with open(file_path_data_noise, "r") as file:
|
||||
content = file.read().strip()
|
||||
avg_noise, max_noise, min_noise = map(int, content.split())
|
||||
|
||||
# Append the variables to the JSON and to the message
|
||||
data['avg_noise'] = avg_noise
|
||||
data['max_noise'] = max_noise
|
||||
data['min_noise'] = min_noise
|
||||
|
||||
#get BME280 data (SAFE: it returns none if the key do not exist)
|
||||
|
||||
message = f"{data.get('PM1', '')},{data.get('PM25', '')},{data.get('PM10', '')},{data.get('temp', '')},{data.get('hum', '')},{data.get('press', '')},{avg_noise},{max_noise},{min_noise}"
|
||||
|
||||
print(message) # Display the message or send it further
|
||||
|
||||
except FileNotFoundError:
|
||||
print(f"Error: File {file_path} not found.")
|
||||
except ValueError:
|
||||
print("Error: File content is not valid numbers.")
|
||||
|
||||
# Print the content of the JSON file
|
||||
if need_to_log:
|
||||
print("Data from sensors:")
|
||||
print(json.dumps(data, indent=4)) # Pretty print the JSON data
|
||||
|
||||
|
||||
|
||||
|
||||
#Write Data to saraR4
|
||||
|
||||
#1. MQTT profile configuration
|
||||
# Note: you need to logout first to change the config
|
||||
print("")
|
||||
|
||||
#print("1.PROFILE CONFIG")
|
||||
#print(" 1.A. READ CONFIG")
|
||||
#command = f'AT+UMQTT?\r'
|
||||
#ser.write((command + '\r').encode('utf-8'))
|
||||
#response_SARA_1 = read_complete_response(ser)
|
||||
#if need_to_log:
|
||||
# print(response_SARA_1)
|
||||
|
||||
# La config s'efface à chaque redémarrage!
|
||||
need_to_update_config = False
|
||||
if need_to_update_config:
|
||||
print("1.B. SET CONFIG")
|
||||
#command = f'AT+UMQTT=1,1883\r' #MQTT local TCP port number
|
||||
command = f'AT+UMQTT=2,"aircarto.fr"\r' #MQTT server name
|
||||
#command = f'AT+UMQTT=3,"193.252.54.10"\r' # MQTT server IP address
|
||||
#command = f'AT+UMQTT=12,1\r' # MQTT clean session
|
||||
ser.write((command + '\r').encode('utf-8'))
|
||||
response_SARA_1 = read_complete_response(ser)
|
||||
if need_to_log:
|
||||
print(response_SARA_1)
|
||||
lines = response_SARA_1.strip().splitlines()
|
||||
for line in lines:
|
||||
if line.startswith("+UMQTT"):
|
||||
# Split the line by commas and get the last number
|
||||
parts = line.split(',')
|
||||
last_number = parts[-1].strip() # Get the last part and strip any whitespace
|
||||
|
||||
if int(last_number) == 1:
|
||||
print(f"{green_color}MQTT profile configuration SUCCEDED{reset_color}")
|
||||
else:
|
||||
print(f"{green_color}ERROR: MQTT profile configuration fail{reset_color}")
|
||||
|
||||
|
||||
#2. MQTT login
|
||||
need_to_update_login = False
|
||||
if need_to_update_login:
|
||||
print("")
|
||||
print("2.MQTT LOGIN")
|
||||
#command = f'AT+UMQTTC=1\r' #MQTT login
|
||||
command = f'AT+UMQTTC=0\r' #MQTT logout
|
||||
|
||||
ser.write((command + '\r').encode('utf-8'))
|
||||
response_SARA_2 = read_complete_response(ser, 8, 8)
|
||||
if need_to_log:
|
||||
print(response_SARA_2)
|
||||
lines = response_SARA_2.strip().splitlines()
|
||||
for line in lines:
|
||||
|
||||
if line.startswith("+UMQTTC"):
|
||||
parts = line.split(',')
|
||||
first_number = parts[0].replace("+UMQTTC:", "").strip()
|
||||
last_number = parts[-1].strip() # Get the last part and strip any whitespace
|
||||
#print(f"Last number: {last_number}")
|
||||
if int(first_number) == 0:
|
||||
print("MQTT logout command ->")
|
||||
if int(first_number) == 1:
|
||||
print("MQTT login command ->")
|
||||
if int(last_number) == 1:
|
||||
print(f"{green_color}SUCCESS{reset_color}")
|
||||
else:
|
||||
print(f"{red_color}FAIL{reset_color}")
|
||||
|
||||
if line.startswith("+UUMQTTC"):
|
||||
parts = line.split(',')
|
||||
first_number = parts[0].replace("+UUMQTTC:", "").strip()
|
||||
last_number = parts[-1].strip() # Get the last part and strip any whitespace
|
||||
if int(first_number) == 1:
|
||||
print("MQTT login result ->")
|
||||
if int(last_number) == 0:
|
||||
print(f"{green_color}connection accepted{reset_color}")
|
||||
if int(last_number) == 1:
|
||||
print(f"{green_color}the server does not support the level of the MQTT protocol requested by the Client{reset_color}")
|
||||
if int(last_number) == 2:
|
||||
print(f"{green_color} the client identifier is correct UTF-8 but not allowed by the Server{reset_color}")
|
||||
if int(last_number) == 3:
|
||||
print(f"{green_color} the network connection has been made but the MQTT service is unavailable{reset_color}")
|
||||
|
||||
|
||||
#3. MQTT publish
|
||||
print("")
|
||||
print("3.MQTT PUBLISH")
|
||||
command = f'AT+UMQTTC=2,0,0,"nebuleair/pro/{device_id}/data","{message}"\r'
|
||||
ser.write((command + '\r').encode('utf-8'))
|
||||
response_SARA_3 = read_complete_response(ser)
|
||||
if need_to_log:
|
||||
print(response_SARA_3)
|
||||
lines = response_SARA_3.strip().splitlines()
|
||||
for line in lines:
|
||||
if line.startswith("+UMQTTC"):
|
||||
parts = line.split(',')
|
||||
first_number = parts[0].replace("+UMQTTC:", "").strip()
|
||||
last_number = parts[-1].strip() # Get the last part and strip any whitespace
|
||||
if int(first_number) == 2:
|
||||
print("MQTT Publish ->")
|
||||
if int(last_number) == 1:
|
||||
print(f"{green_color}SUCCESS{reset_color}")
|
||||
else:
|
||||
print(f"{red_color}FAIL{reset_color}")
|
||||
|
||||
|
||||
# Calculate and print the elapsed time
|
||||
elapsed_time = time.time() - start_time
|
||||
if need_to_log:
|
||||
print("")
|
||||
print(f"Elapsed time: {elapsed_time:.2f} seconds")
|
||||
print("----------------------------------------")
|
||||
print("----------------------------------------")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error reading the JSON file: {e}")
|
||||
Reference in New Issue
Block a user