Call and GPS functionality added

This commit is contained in:
Tarek
2020-09-03 17:04:39 +02:00
parent 32ba0c839f
commit 7c3a6ad886

543
gsmHat.py Executable file
View File

@@ -0,0 +1,543 @@
#!/usr/bin/python3
# Filename: gsmHat.py
import logging
import serial
import threading
import time
import math
import re
from datetime import datetime
import RPi.GPIO as GPIO
class SMS:
def __init__(self):
self.Message = ''
self.Sender = ''
self.Receiver = ''
self.Date = ''
class GPS:
EarthRadius = 6371e3 # meters
@staticmethod
def CalculateDeltaP(Position1, Position2):
phi1 = Position1.Latitude * math.pi / 180.0
phi2 = Position2.Latitude * math.pi / 180.0
deltaPhi = (Position2.Latitude - Position1.Latitude) * math.pi / 180.0
deltaLambda = (Position2.Longitude - Position1.Longitude) * math.pi / 180.0
a = math.sin(deltaPhi / 2) * math.sin(deltaPhi / 2) + math.cos(phi1) * math.cos(phi2) * math.sin(deltaLambda / 2) * math.sin(deltaLambda / 2)
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
d = GPS.EarthRadius * c # in meters
return d
def __init__(self):
self.GNSS_status = 0
self.Fix_status = 0
self.UTC = '' # yyyyMMddhhmmss.sss
self.Latitude = 0.0 # ±dd.dddddd [-90.000000,90.000000]
self.Longitude = 0.0 # ±ddd.dddddd [-180.000000,180.000000]
self.Altitude = 0.0 # in meters
self.Speed = 0.0 # km/h [0,999.99]
self.Course = 0.0 # degrees [0,360.00]
self.HDOP = 0.0 # [0,99.9]
self.PDOP = 0.0 # [0,99.9]
self.VDOP = 0.0 # [0,99.9]
self.GPS_satellites = 0 # [0,99]
self.GNSS_satellites = 0 # [0,99]
self.Signal = 0.0 # % max = 55 dBHz
class GSMHat:
"""GSM Hat Backend with SMS Functionality (for now)"""
regexGetSingleValue = r'([+][a-zA-Z\ ]+(:\ ))([\d]+)'
regexGetAllValues = r'([+][a-zA-Z:\s]+)([\w\",\s+\/:.]+)'
timeoutSerial = 5
timeoutGPSActive = 1
timeoutGPSInactive = 5
def __init__(self, SerialPort, Baudrate):
self.__baudrate = Baudrate
self.__port = SerialPort
self.__logger = logging.getLogger(__name__)
self.__logger.setLevel(logging.INFO)
self.__loggerFileHandle = logging.FileHandler('gsmHat.log')
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
self.__loggerFileHandle.setFormatter(formatter)
self.__loggerFileHandle.setLevel(logging.INFO )
self.__logger.addHandler(self.__loggerFileHandle)
self.__connect()
self.__startWorking()
def __connect(self):
self.__ser = serial.Serial(self.__port, self.__baudrate)
self.__ser.flushInput()
self.__serData = ''
self.__writeLock = False
self.__logger.info('Serial connection to '+self.__port+' established')
def __disconnect(self):
self.__ser.close()
def __startWorking(self):
self.__working = True
self.__state = 1
self.__smsToRead = 0
self.__init = False
self.__readRAW = False
self.__smsToBuild = None
self.__smsList = []
self.__smsSendList = []
self.__numberToCall = ''
self.__sendHangUp = False
self.__startGPS = False
self.__GPSstarted = False
self.__GPSstartSending = False
self.__GPSstopSending = False
self.__GPScollectData = False
self.__GPSactualData = GPS()
self.__GPStimeout = self.timeoutGPSInactive * 1000
self.__GPSwaittime = 0
self.__workerThread = threading.Thread(target=self.__workerThread, daemon=True)
self.__workerThread.start()
def __stopWorking(self):
self.__working = False
self.__workerThread.join(10.0) # Timeout = 10.0 Seconds
def __sendToHat(self, string):
if self.__writeLock == False:
self.__lastCommand = string
string = string + '\n'
self.__ser.write(string.encode('iso-8859-1'))
self.__writeLock = True
self.__sentTimeout = int(round(time.time())) + self.timeoutSerial
self.__logger.debug('Sent to hat: %s' % string)
return True
else:
self.__logger.debug('Wait for Lock...')
time.sleep(1)
return False
def __pressPowerKey(self):
GPIO.setmode(GPIO.BOARD)
GPIO.setup(7, GPIO.OUT)
while True:
GPIO.output(7, GPIO.LOW)
time.sleep(4)
GPIO.output(7, GPIO.HIGH)
break
GPIO.cleanup()
time.sleep(10)
def SMS_available(self):
return len(self.__smsList)
def SMS_read(self):
if self.SMS_available() > 0:
retSMS = self.__smsList[0]
del self.__smsList[0]
return retSMS
return None
def SMS_write(self, NumberReceiver, Message):
newSMS = SMS()
newSMS.Receiver = NumberReceiver
newSMS.Message = Message
self.__smsSendList.append(newSMS)
def Call(self, Number, Timeout = 15):
if self.__numberToCall == '':
self.__numberToCall = str(Number)
self.__callTimeout = Timeout
return True
return False
def HangUp(self):
self.__sendHangUp = True
def GetActualGPS(self):
return self.__GPSactualData
def __startGPSUnit(self):
self.__startGPS = True
def __startGPSsending(self):
self.__GPSstartSending = True
def __stopGPSsending(self):
self.__GPSstopSending = True
def __collectGPSData(self):
self.__GPScollectData = True
def ColData(self):
self.__collectGPSData()
def close(self):
self.__disconnect()
self.__logger.info('Serial connection to '+self.__port+' closed')
self.__stopWorking()
def __processData(self):
if self.__serData != '':
if self.__readRAW:
self.__logger.debug('Received Raw Data: %s' % self.__serData)
if self.__serData == 'OK\r\n':
self.__smsToBuild.Message = self.__smsToBuild.Message.rstrip('\r\n')
self.__smsList.append(self.__smsToBuild)
self.__readRAW = False
self.__writeLock = False
else:
#self.__smsToBuild.Message = bytearray.fromhex(self.__serData).decode()
self.__smsToBuild.Message = self.__smsToBuild.Message + self.__serData
else:
self.__logger.debug('Received Data: %s' % self.__serData)
if 'OK' in self.__serData:
self.__writeLock = False
self.__logger.debug('Lock Off')
elif '+CME ERROR:' in self.__serData:
self.__writeLock = False
match = re.findall(self.regexGetSingleValue, self.__serData)
self.__cmeErr = int(match[0][1])
self.__logger.info('Got CME ERROR: %s' % match[0][1])
elif '+CMS ERROR:' in self.__serData:
self.__writeLock = False
match = re.findall(self.regexGetSingleValue, self.__serData)
self.__cmsErr = int(match[0][1])
self.__logger.info('Got CMS ERROR: %s' % match[0][1])
elif '+CPMS:' in self.__serData:
match = re.findall(self.regexGetAllValues, self.__serData)
rawData = match[0][1].split(',')
self.__masSMSSpace = int(rawData[1])
numSMS = int(rawData[0])
if numSMS > 0:
self.__smsToRead = 1
elif '+CMGR:' in self.__serData:
# read SMS content
match = re.findall(self.regexGetAllValues, self.__serData)
rawData = match[0][1].split('","')
self.__readRAW = True
self.__smsToBuild = SMS()
#self.__smsToBuild.Sender = bytearray.fromhex(rawData[1]).decode()
self.__smsToBuild.Sender = rawData[1]
self.__smsToBuild.Date = rawData[3].replace('"', '')
self.__smsToBuild.Date = datetime.strptime(rawData[3].replace('"', '')[:-3], '%y/%m/%d,%H:%M:%S')
self.__smsToBuild.Message = ''
# unannounced data reception below (e.g. new SMS oder phone call)
elif '+CMTI:' in self.__serData:
self.__logger.info('Received new SMS')
match = re.findall(self.regexGetAllValues, self.__serData)
rawData = match[0][1].split(',')
storage = rawData[0]
numSMS = int(rawData[1])
self.__logger.debug('New SMS in memory ' + storage + ' at position ' + str(numSMS))
self.__smsToRead = numSMS
# GPS Data coming here
elif '+CGNSINF:' in self.__serData:
self.__logger.debug('New GPS Data:')
match = re.findall(self.regexGetAllValues, self.__serData)
rawData = match[0][1].split(',')
newGPS = GPS()
try:
newGPS.GNSS_status = int(rawData[0])
except:
pass
try:
newGPS.Fix_status = int(rawData[1])
except:
pass
try:
newGPS.UTC = datetime.strptime(rawData[2][:-4], '%Y%m%d%H%M%S')
except:
pass
try:
newGPS.Latitude = float(rawData[3])
except:
pass
try:
newGPS.Longitude = float(rawData[4])
except:
pass
try:
newGPS.Altitude = float(rawData[5])
except:
pass
try:
newGPS.Speed = float(rawData[6])
except:
pass
try:
newGPS.Course = float(rawData[7])
except:
pass
try:
newGPS.HDOP = float(rawData[10])
except:
pass
try:
newGPS.PDOP = float(rawData[11])
except:
pass
try:
newGPS.VDOP = float(rawData[12])
except:
pass
try:
newGPS.GPS_satellites = int(rawData[14])
except:
pass
try:
newGPS.GNSS_satellites = int(rawData[15])
except:
pass
try:
newGPS.Signal = float(rawData[18])/55.0
except:
pass
self.__GPSactualData = newGPS
self.__serData = ''
def __waitForUnlock(self):
actTime = int(round(time.time()))
if self.__sentTimeout > 0 and actTime > self.__sentTimeout:
# Timeout
self.__logger.error('Timeout during data reception')
if self.__state == 2:
# It might be that the gsm module is not powered on
# So let's try
self.__logger.error('Try to restart gsm module')
self.__pressPowerKey()
self.__state = 1
self.__writeLock = False
self.__sentTimeout = 0
return False
else:
raise 'Unhandled timeout during data reception'
if self.__writeLock:
return False
else:
self.__sentTimeout = 0
return True
def __workerThread(self):
self.__logger.info('Worker started')
self.__waitTime = 0
while self.__working:
# Check for incoming chars
while self.__ser.inWaiting() > 0:
newChar = self.__ser.read().decode('iso-8859-1')
if newChar == '\n':
if self.__readRAW == True:
self.__serData += newChar
self.__processData()
else:
if newChar == '\r':
if self.__readRAW == True:
self.__serData += newChar
else:
self.__serData += newChar
# Statemachine
actTime = int(round(time.time() * 1000))
if self.__state == 1:
if self.__sendToHat('AT+CMGF=1'):
self.__startGPSUnit()
self.__stopGPSsending()
self.__state = 2
elif self.__state == 2:
if self.__waitForUnlock():
if self.__sendToHat('AT+CPMS="SM"'):
self.__state = 3
elif self.__state == 3:
if self.__waitForUnlock():
self.__state = 97
self.__nextState = 2
self.__waitTime = actTime + 5000
elif self.__state == 20:
# Read SMS
if self.__sendToHat('AT+CMGR='+str(self.__smsToRead)):
self.__state = 21
elif self.__state == 21:
if self.__waitForUnlock():
if self.__smsToBuild == None:
# An der Stelle self.__smsToRead gab es keine SMS zu lesen
pass
else:
# Es gab eine neue SMS
self.__smsToBuild = None
# Lösche die behandelte SMS an der Stelle
if self.__sendToHat('AT+CMGD='+str(self.__smsToRead)):
self.__state = 22
elif self.__state == 22:
if self.__waitForUnlock():
if(self.__smsToRead == 20):
self.__smsToRead = 0
else:
self.__smsToRead = self.__smsToRead + 1
self.__state = 97
self.__nextState = 2
self.__waitTime = actTime + 5000
elif self.__state == 30:
# SMS versenden
retSMS = self.__smsSendList[0]
messageString = 'AT+CMGS="' + retSMS.Receiver + '"\n' + retSMS.Message + '\x1A'
self.timeoutSerial = 30
if self.__sendToHat(messageString):
self.__state = 31
elif self.__state == 31:
if self.__waitForUnlock():
retSMS = self.__smsSendList[0]
self.__logger.info('Message to ' + retSMS.Receiver + ' successfully sent')
del self.__smsSendList[0]
self.timeoutSerial = 5
self.__state = 97
self.__nextState = 2
self.__waitTime = actTime + 5000
elif self.__state == 40:
if self.__sendToHat('ATD' + self.__numberToCall + ';'):
self.__state = 41
elif self.__state == 41:
if self.__waitForUnlock():
self.__waitTime = actTime + self.__callTimeout * 1000
self.__state = 42
elif self.__state == 42:
# Wait x Seconds
if actTime > self.__waitTime or self.__sendHangUp == True:
self.__numberToCall = ''
self.__sendHangUp = True
self.__state = 97
self.__nextState = 2
self.__waitTime = actTime + 5000
elif self.__state == 43:
if self.__sendToHat('AT+CHUP'):
self.__state = 44
elif self.__state == 44:
if self.__waitForUnlock():
self.__sendHangUp = False
self.__state = 97
self.__nextState = 2
self.__waitTime = actTime + 5000
elif self.__state == 50:
if self.__sendToHat('AT+CGNSPWR=1'):
self.__state = 51
elif self.__state == 51:
if self.__waitForUnlock():
self.__logger.debug('GPS powered on')
self.__startGPS = False
self.__state = 97
self.__nextState = 2
self.__waitTime = actTime + 5000
elif self.__state == 52:
if self.__sendToHat('AT+CGNSTST=1'):
self.__state = 55
self.__logger.debug('GPS start sending')
self.__GPSstartSending = False
elif self.__state == 53:
if self.__sendToHat('AT+CGNSTST=0'):
self.__state = 55
self.__GPSstopSending = False
elif self.__state == 54:
if self.__sendToHat('AT+CGNSINF'):
self.__state = 55
self.__GPScollectData = False
elif self.__state == 55:
if self.__waitForUnlock():
self.__state = 97
self.__nextState = 2
self.__waitTime = actTime + 5000
elif self.__state == 97:
# Check if new SMS to send is there
if len(self.__smsSendList) > 0:
self.__state = 30
# Check if we have to Call somebody
elif self.__numberToCall != '':
self.__state = 40
# Should I Hang Up ?
elif self.__sendHangUp:
self.__state = 43
# Check if new SMS is there
elif self.__smsToRead > 0:
self.__state = 20
# Check if GPS Unit should start
elif self.__startGPS:
self.__state = 50
# Check if GPS Unit should start send
elif self.__GPSstartSending:
self.__state = 52
# Check if GPS Unit should stop send
elif self.__GPSstopSending:
self.__state = 53
# Check if Single GPS Data should be collected
elif self.__GPScollectData:
self.__state = 54
elif actTime > self.__GPSwaittime:
self.__GPScollectData = True
self.__GPSwaittime = actTime + self.__GPStimeout
# Wait x Seconds
elif actTime > self.__waitTime:
self.__state = self.__nextState
elif self.__state == 98:
#Check if alive
self.__logger.debug('Check if alive 98')
if self.__sendToHat('AT'):
self.__state = 99
elif self.__state == 99:
#Check if alive
if self.__waitForUnlock():
self.__state = 97
self.__nextState = 98
self.__waitTime = actTime + 5000
# Let other Threads also do their job
time.sleep(0.1)
self.__logger.info('Worker ended')