222 lines
7.0 KiB
Python
222 lines
7.0 KiB
Python
|
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
|
||
|
|
||
|
# 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
|
||
|
try:
|
||
|
gps_ser = serial.Serial('/dev/ttyUSB3', 115200)
|
||
|
gps_ser.flushInput()
|
||
|
except serial.SerialException as e:
|
||
|
logging.error(f"Failed to open GPS serial port: {e}")
|
||
|
exit(1)
|
||
|
|
||
|
# 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: {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: {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: {e}")
|
||
|
|
||
|
def send_at(command, back, timeout):
|
||
|
try:
|
||
|
gps_ser.write((command + '\r\n').encode())
|
||
|
time.sleep(timeout)
|
||
|
if gps_ser.inWaiting():
|
||
|
time.sleep(0.01)
|
||
|
rec_buff = gps_ser.read(gps_ser.inWaiting()).decode()
|
||
|
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: {e}")
|
||
|
return 0
|
||
|
|
||
|
def get_gps_position():
|
||
|
return send_at('AT+CGPSINFO', '+CGPSINFO:', 1)
|
||
|
|
||
|
def initialize_gps():
|
||
|
logging.info('Starting GPS')
|
||
|
if send_at('AT+CGPS=1', 'OK', 1):
|
||
|
logging.info("GPS module initialized successfully")
|
||
|
else:
|
||
|
logging.error("Failed to initialize GPS module")
|
||
|
time.sleep(2)
|
||
|
|
||
|
# Initialize GPS
|
||
|
initialize_gps()
|
||
|
|
||
|
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
|
||
|
temp = data[1] / 10
|
||
|
|
||
|
# 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}\u00B0C")
|
||
|
logging.info(f"Relative humidity = {hum}%")
|
||
|
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():
|
||
|
logging.warning("Failed to get GPS position")
|
||
|
|
||
|
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")
|