173 lines
4.7 KiB
Python
Executable File
173 lines
4.7 KiB
Python
Executable File
'''
|
|
Loop to run every minutes
|
|
|
|
* * * * * /usr/bin/python3 /var/www/nebuleair_pro_4g/NPM/get_data_modbus_loop.py ttyAMA5
|
|
|
|
saves data (avaerage) to a json file /var/www/nebuleair_pro_4g/NPM/data/data.json
|
|
saves data (all) to a sqlite database
|
|
|
|
first time running the script?
|
|
sudo mkdir /var/www/nebuleair_pro_4g/NPM/data
|
|
sudo touch /var/www/nebuleair_pro_4g/NPM/data/data.json
|
|
'''
|
|
|
|
import serial
|
|
import sys
|
|
import crcmod
|
|
import time
|
|
import json
|
|
import os
|
|
import sqlite3
|
|
import smbus2 # For RTC DS3231
|
|
from datetime import datetime
|
|
|
|
|
|
# Ensure a port argument is provided
|
|
if len(sys.argv) < 2:
|
|
print("Usage: python3 get_data_modbus.py <serial_port>")
|
|
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 SQLite database
|
|
conn = sqlite3.connect("/var/www/nebuleair_pro_4g/sqlite/sensors.db")
|
|
cursor = conn.cursor()
|
|
|
|
# RTC Module (DS3231) Setup
|
|
RTC_I2C_ADDR = 0x68 # DS3231 I2C Address
|
|
bus = smbus2.SMBus(1)
|
|
|
|
def bcd_to_dec(bcd):
|
|
return (bcd // 16 * 10) + (bcd % 16)
|
|
|
|
def get_rtc_time():
|
|
"""Reads time from RTC module (DS3231)"""
|
|
try:
|
|
data = bus.read_i2c_block_data(RTC_I2C_ADDR, 0x00, 7)
|
|
seconds = bcd_to_dec(data[0] & 0x7F)
|
|
minutes = bcd_to_dec(data[1])
|
|
hours = bcd_to_dec(data[2] & 0x3F)
|
|
day = bcd_to_dec(data[4])
|
|
month = bcd_to_dec(data[5])
|
|
year = bcd_to_dec(data[6]) + 2000
|
|
return datetime(year, month, day, hours, minutes, seconds).strftime("%Y-%m-%d %H:%M:%S")
|
|
except Exception as e:
|
|
print(f"RTC Read Error: {e}")
|
|
return "N/A"
|
|
|
|
# Initialize storage for averaging
|
|
num_samples = 6
|
|
channel_sums = [0] * 5 # 5 channels
|
|
|
|
#do not start immediately to prevent multiple write/read over NPM serial port
|
|
time.sleep(3)
|
|
|
|
# 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)
|
|
rtc_timestamp = get_rtc_time()
|
|
|
|
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(9)
|
|
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
|
|
print(f"Timestamp (RTC): {rtc_timestamp}")
|
|
for j in range(5):
|
|
print(f"Channel {j+1}: {channels[j]}")
|
|
|
|
|
|
# Save the individual reading to the database
|
|
cursor.execute('''
|
|
INSERT INTO data (timestamp, PM_ch1, PM_ch2, PM_ch3, PM_ch4, PM_ch5)
|
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
''', (rtc_timestamp, channels[0], channels[1], channels[2], channels[3], channels[4]))
|
|
|
|
conn.commit()
|
|
|
|
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()
|