diff --git a/modbus_gps_with_reset.py b/modbus_gps_with_reset.py new file mode 100644 index 0000000..e061527 --- /dev/null +++ b/modbus_gps_with_reset.py @@ -0,0 +1,260 @@ +#!/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 +#preferred_device = '/dev/ttyUSB0' +#fallback_device = '/dev/ttyUSB5' +#device_path = preferred_device if os.path.exists(preferred_device) else fallback_device +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, 115200) + 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, 115200) +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")