#!/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")