jdz/modbus_gps_with_reset.py
2024-08-03 19:16:20 -06:00

258 lines
8.5 KiB
Python

#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import RPi.GPIO as GPIO
import paho.mqtt.client as mqtt
import os
import minimalmodbus
import time
import serial
from datetime import datetime
from influxdb_client import InfluxDBClient, Point
from influxdb_client.client.write_api import SYNCHRONOUS
import logging
import subprocess
# Set up logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
# GPIO configuration
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.cleanup()
# MQTT Broker settings
mqttBroker = "65.108.199.212"
myhost = os.uname()[1]
mqtt_client = mqtt.Client(myhost)
mqtt_client.connect(mqttBroker, 1883)
# Modbus configuration
modbus_device = '/dev/modbus'
mb_address = 1
sensy_boi = minimalmodbus.Instrument(modbus_device, mb_address)
sensy_boi.serial.baudrate = 9600
sensy_boi.serial.bytesize = 8
sensy_boi.serial.parity = minimalmodbus.serial.PARITY_NONE
sensy_boi.serial.stopbits = 1
sensy_boi.serial.timeout = 0.5
sensy_boi.mode = minimalmodbus.MODE_RTU
sensy_boi.clear_buffers_before_each_transaction = True
sensy_boi.close_port_after_each_call = True
# GPS configuration
gps_device = '/dev/ttyUSB3'
# InfluxDB settings
INFLUXDB_URL = "http://100.64.0.24:8086"
INFLUXDB_TOKEN = "IPtqPXbaXuuMHvx_tUOt1cmIZfLHucd-9DcepXTVpQc-fNKBhp6pkhyTsq_XnoGXdxwILy5AFFgZ_QUZCE5Jhg=="
INFLUXDB_ORG = "juandiego"
INFLUXDB_BUCKET = "gpsdata"
# Initialize InfluxDB client
try:
influx_client = InfluxDBClient(url=INFLUXDB_URL, token=INFLUXDB_TOKEN, org=INFLUXDB_ORG)
write_api = influx_client.write_api(write_options=SYNCHRONOUS)
logging.info("Successfully connected to InfluxDB")
except Exception as e:
logging.error(f"Failed to connect to InfluxDB: {str(e)}")
exit(1)
# GPIO pin configuration
PIN_17 = 17
LuzPuerta = 18
LuzEncendido = 25
GPIO.setup(PIN_17, GPIO.IN)
GPIO.setup(LuzPuerta, GPIO.OUT)
GPIO.setup(LuzEncendido, GPIO.OUT)
# Keep LuzEncendido HIGH while the program is running
GPIO.output(LuzEncendido, GPIO.HIGH)
# List to store pending data
pending_data = []
def control_gpio():
status_17 = GPIO.input(PIN_17)
if status_17 == GPIO.LOW:
GPIO.output(LuzPuerta, GPIO.HIGH) # Red light on
else:
GPIO.output(LuzPuerta, GPIO.LOW)
return status_17
def parse_gps_data(gps_string):
parts = gps_string.split(',')
if len(parts) < 4:
logging.warning(f"Incomplete GPS data: {gps_string}")
return None
try:
lat = float(parts[0][:2]) + float(parts[0][2:]) / 60
if parts[1] == 'S':
lat = -lat
lon = float(parts[2][:3]) + float(parts[2][3:]) / 60
if parts[3] == 'W':
lon = -lon
return lat, lon
except Exception as e:
logging.error(f"Error parsing GPS data: {str(e)}")
return None
def write_to_influxdb(lat, lon):
timestamp = int(time.time() * 1000000000) # nanosecond precision
try:
gps_point = Point("gps_location") \
.tag("host", myhost) \
.field("latitude", lat) \
.field("longitude", lon) \
.field("geolocation", f"{lat},{lon}") \
.time(timestamp)
write_api.write(bucket=INFLUXDB_BUCKET, record=gps_point)
logging.info(f"GPS data written to InfluxDB: Lat: {lat}, Lon: {lon}")
except Exception as e:
logging.error(f"Failed to write GPS data to InfluxDB: {str(e)}")
def send_at(command, back, timeout, gps_ser):
try:
gps_ser.write(f"{command}\r\n".encode('utf-8'))
time.sleep(timeout)
if gps_ser.in_waiting:
time.sleep(0.01)
rec_buff = gps_ser.read(gps_ser.in_waiting).decode('utf-8')
if back in rec_buff:
gps_data = rec_buff.split('+CGPSINFO: ')[1].split('\r\n')[0]
logging.debug(f"Raw GPS data: {gps_data}")
parsed_data = parse_gps_data(gps_data)
if parsed_data:
lat, lon = parsed_data
logging.info(f"GPS: Lat: {lat:.6f}, Lon: {lon:.6f}")
write_to_influxdb(lat, lon)
return 1
else:
logging.warning(f"Expected GPS response not found. Received: {rec_buff}")
else:
logging.warning("No data received from GPS module")
except Exception as e:
logging.error(f"Error communicating with GPS module: {str(e)}")
return 0
def get_gps_position(gps_ser):
return send_at('AT+CGPSINFO', '+CGPSINFO:', 1, gps_ser)
def initialize_gps(gps_ser):
logging.info('Starting GPS')
if send_at('AT+CGPS=1', 'OK', 1, gps_ser):
logging.info("GPS module initialized successfully")
else:
logging.error("Failed to initialize GPS module")
time.sleep(2)
def reset_usb_ports():
logging.info("Resetting USB ports...")
try:
subprocess.run(['sudo', 'sh', '-c', 'echo 0 > /sys/devices/platform/soc/3f980000.usb/buspower'], check=True)
time.sleep(2)
subprocess.run(['sudo', 'sh', '-c', 'echo 1 > /sys/devices/platform/soc/3f980000.usb/buspower'], check=True)
time.sleep(5) # Give some time for devices to reinitialized
logging.info("USB ports reset completed")
except subprocess.CalledProcessError as e:
logging.error(f"Failed to reset USB ports: {str(e)}")
def reinitialize_devices():
global sensy_boi, gps_ser
try:
sensy_boi = minimalmodbus.Instrument(modbus_device, mb_address)
sensy_boi.serial.baudrate = 9600
sensy_boi.serial.bytesize = 8
sensy_boi.serial.parity = minimalmodbus.serial.PARITY_NONE
sensy_boi.serial.stopbits = 1
sensy_boi.serial.timeout = 0.5
sensy_boi.mode = minimalmodbus.MODE_RTU
sensy_boi.clear_buffers_before_each_transaction = True
sensy_boi.close_port_after_each_call = True
gps_ser = serial.Serial(gps_device, 9600)
gps_ser.reset_input_buffer()
initialize_gps(gps_ser)
logging.info("Devices reinitialized successfully")
except Exception as e:
logging.error(f"Failed to reinitialize devices: {str(e)}")
# Initialize GPS
gps_ser = serial.Serial(gps_device, 9600)
gps_ser.reset_input_buffer()
initialize_gps(gps_ser)
reset_counter = 0
try:
while True:
# Control GPIO without delay
status_17 = control_gpio()
# Read data from Modbus sensor
try:
data = sensy_boi.read_registers(0, 2)
hum = data[0] / 10.0
temp = data[1] / 10.0
# Data to publish
payload = {
"temperature": temp,
"humidity": hum,
"door_status": str(status_17)
}
try:
# Try to publish current data
mqtt_client.publish(f"iiot/{myhost}/temperature", temp)
mqtt_client.publish(f"iiot/{myhost}/humidity", hum)
mqtt_client.publish(f"iiot/{myhost}/door/pin17", str(status_17))
# Try to publish pending data
for item in pending_data:
mqtt_client.publish(f"iiot/{myhost}/temperature", item["temperature"])
mqtt_client.publish(f"iiot/{myhost}/humidity", item["humidity"])
mqtt_client.publish(f"iiot/{myhost}/door/pin17", item["door_status"])
pending_data.clear() # Clear the list if all data was published
except Exception as e:
logging.error(f"Error publishing data to MQTT: {str(e)}")
pending_data.append(payload) # Save data in memory
# Print processed data
logging.info("-------------------------------------")
logging.info(f"Temperature = {temp:.1f}°C")
logging.info(f"Relative humidity = {hum:.1f}%")
logging.info(f"Door status (PIN 17) = {status_17}")
logging.info("-------------------------------------")
except Exception as e:
logging.error(f"Error reading Modbus data: {str(e)}")
# Get GPS position
if not get_gps_position(gps_ser):
logging.warning("Failed to get GPS position")
reset_counter += 1
if reset_counter >= 60: # Reset every 30 minutes (60 * 30 seconds)
reset_usb_ports()
reinitialize_devices()
reset_counter = 0
time.sleep(30)
except KeyboardInterrupt:
logging.info("Program interrupted by user")
except Exception as e:
logging.error(f"Unexpected error: {str(e)}")
finally:
GPIO.cleanup()
gps_ser.close()
influx_client.close()
logging.info("Program finished")