[Linux] Beagleboard GPS SMS Cellphone Module ready!
This board i made for the Space Camera Live project, as a backup GPS system, that can also send SMS messages and make some calls. Most importantly, it can get python scripts loaded to it. For instance, you can call it up (it takes a sim card) – then ofcourse it will not pick it up, but it will send you back an SMS message immediatelly containing its GPS coordinates!
This module is spaced such that it can be hooked up to the Beagle Board easily, it has the same hole dimensions and sizes as the board. Also, ofcourse, this board can be used with any machine that has USB (every machine); so it can also be used as an evaluation board. Not bad for just $3 production costs. If you want a bare PCB board (this white one) you can have it, i’ll send it over regular email.
Prototype without flaws, works 100%
This prototype worked out of the box. I had the PCB’s in, soldered them, plugged it in the computer, hey presto: worked.
Module design, schematics, internals, etc
I designed this module myself. You can see everything about how it i had it produced here: LINK.
Rembrandt is pleased
As i had some empty space, i decided to let an etch of rembrandt be etched on the bottom of the PCB. Worked out great.
Short Introduction Video
Pictures!
This modules takes python scripts
This module can operate autonomously if you give it a python script.
Here below is a script, that, when loaded to the module, sends anyone who textmessages “position” to the module, the GPS position back. Also, there is a built in function that resets the module when it fails when it reaches a GPS altitude of 24km.
There are two files: run.py and main.py. Download RSTERM.exe on windows. Make sure you set the baudrate to 115200 In the Python menu, choose your folder with these scripts. Press compile, then upload the two .pyo files. Then, set run.py as the active script, then select the boot after 10 seconds mode, then reboot and voila you’re done, the script will automatically load.
If you now send an sms to the module, i get an sms back with the GPS position!
WATCH OUT: When i was editing the code, it would NOT work if i included too many spaces or tabs (=indentations). So try to avoid those and just write your code on one level.
run.py
import main
main.py
# Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
# THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS
# PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR
# OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS
# LICENSE OR COPYRIGHT LAW IS PROHIBITED.
# Original code Alexei Karpenko
# Modified by Tim Zaman
##### Config #####
sms_password = 'pwd'
remote_phone_number = '+31612345678'
##### Constants #####
TRUE = 1
FALSE = 0
line_cut = FALSE
had_fix = FALSE
nofix_count = 0
##### Modules #####
#Use serial
import SER
#Use build in module
import MOD
#Use AT command interface
import MDM
#Use GPIO functions
import GPIO
###### General Functions ######
def cutoff():
GPIO.setIOvalue(8, 1)
#sms_send(remote_phone_number,'Cut line!')
def cutoff_cancel():
GPIO.setIOvalue(8, 0)
def getActualPosition():
MDM.send('AT$GPSACPr', 0)
pos = MDM.receive(1)
pos = pos.split('rn')
return pos[1]
def reset_system():
MDM.send('AT#SHDNr', 0)
temp = MDM.receive(1)
def reset_gps():
MDM.send('AT$GPSR=1r', 0)
temp = MDM.receive(1)
def reset_gps2():
MDM.send('AT$GPSR=0r', 0)
temp = MDM.receive(1)
#Debug message
def debugmsg(msgtext):
msgtext = msgtext.replace('r', 'r')
msgtext = msgtext.replace('n', 'n')
print msgtext
SER.send(msgtext + 'rn')
#f = open('log.txt','ab')
#f.write(msgtext + 'n')
#f.close()
#GPS status
def gps_status(gpspos):
global had_fix
debugmsg('Retrieving GPS status')
gpspos_parts = gpspos.split(',')
if ( (gpspos_parts[5] == '2') or (gpspos_parts[5] == '3') ): #2D or 3D fix
debugmsg('GPS fix "' + gpspos_parts[5] + '" ie valid');
status = TRUE
# GPS crash prevention:
if (gpspos_parts[10] == '00'):
if (had_fix == TRUE):
reset_gps()
had_fix = FALSE
else:
had_fix = TRUE
else:
debugmsg('GPS fix "' + gpspos_parts[5] + '" ie not valid');
status = FALSE
# GPS crash prevention:
if (had_fix == TRUE):
reset_gps()
had_fix = FALSE
return status
###### SMS Process Functions ######
#Process SMS messages
def sms_proceses(gpspos):
debugmsg('Process SMS')
#List SMS
smsmsgs = sms_list()
totalsms = len(smsmsgs)
#debugmsg('SMS total: %d' % totalsms)
#Go throguh SMS messages
for smsmsgindex in range(0, totalsms):
debugmsg('Message: %d' % smsmsgindex)
#Delete SMS (it is being processed now)
status = sms_delete(smsmsgs[smsmsgindex]['id'])
#If status deleted ok
if (status == TRUE):
#debugmsg('List SMS id: %d, from: %s, msg: %s' % (smsmsgs[smsmsgindex]['id'], smsmsgs[smsmsgindex]['from'], smsmsgs[smsmsgindex]['msg']))
#Split SMS message into parts
smsmsgparts = smsmsgs[smsmsgindex]['msg'].split(' ')
#If at least 2 parts
if (len(smsmsgparts) > 1):
#Check if password valid
if (smsmsgparts[0].lower() == sms_password):
debugmsg('Password valid')
#If position requested
if (smsmsgparts[1].lower() == 'pos') or (smsmsgparts[1].lower() == 'position'):
debugmsg('Action: Position requested')
#If GPS position fix valid
if (gps_status(gpspos) == TRUE):
#gpsdataparts = gpspos.split(',')
#senddata = 'Lat: ' + gpsdataparts[1] + ', Lon: ' + gpsdataparts[2] + ', Heading: ' + gpsdataparts[6] + ', Speed: ' + gpsdataparts[7] + ' km/hr'
senddata = gpspos;
else:
senddata = 'No GPS Fix'
#Send SMS
sms_send(smsmsgs[smsmsgindex]['from'], senddata)
elif (smsmsgparts[1].lower() == 'cut'):
cutoff()
elif (smsmsgparts[1].lower() == 'oops'):
cutoff_cancel()
elif (smsmsgparts[1].lower() == 'rgps'):
reset_gps()
elif (smsmsgparts[1].lower() == 'rgps2'):
reset_gps2()
elif (smsmsgparts[1].lower() == 'rsys'):
reset_system()
else:
debugmsg('Action: Unknown')
else:
debugmsg('Password invalid')
else:
debugmsg('Unable to delete')
###### SMS Library Functions ######
#Setup SMS
def sms_setup():
debugmsg('Setting up SMS')
MDM.send('AT+CMGF=1r', 0)
res = MDM.receive(50)#5 sec
MOD.sleep(1)#wait 0.1sec
debugmsg('SMS setup: ' + res)
#List SMS
def sms_list():
#Note: Command will not return anything if SIM is not ready
debugmsg('List SMS')
#MDM.send('AT+CMGL="REC UNREAD"r', 0) #ALL REC UNREAD STO SENT
MDM.send('AT+CMGL="REC UNREAD"r', 0)
#smslist = ''
#res = MDM.receive(50)#5 sec
#smslist = smslist + res
#while (res.find('rnOKrn') == -1):
# res = MDM.receive(10)
# smslist = smslist + res
smslist = ''
res = ''
timeout = MOD.secCounter() + 60
while ( (res.find('rnOKrn') == -1) and (MOD.secCounter() < timeout) ):
debugmsg('Timeout now: %d timeout: %d' % (MOD.secCounter(), timeout))
res = MDM.receive(50)
smslist = smslist + res
#debugmsg('get %s' % smslist)
MOD.sleep(1)#wait 0.1sec
smsparts = smslist.split('rn')
smspartslen = len(smsparts)
#debugmsg('SMS Parts %d' % smspartslen)
smsmsgs = []
if (smspartslen-2 > 0) and (smsparts[smspartslen-2] == 'OK'):
#If there is at least 1 message
if (smspartslen >= 6):
#Go through all messages
for partno in range(1, smspartslen):
#Find out if this is an cmgl line
cmglparts = smsparts[partno].split('CMGL: ')
#If at least 2 parts, leading cmgl and data
if (len(cmglparts) > 1) and (cmglparts[0] == '+') and (partno+1 <= smspartslen):
#debugmsg('Found CMGL %d: %s' % (partno, smsparts[partno]))
#debugmsg('Text %d: %s' % (partno+1, smsparts[partno+1]))
#Split msg info line into parts
msginfoparts = cmglparts[1].split(',')
#Check have all parts
#if (len(msginfoparts) > 5):
if (len(msginfoparts) > 4):
#int() may fail
try:
#Convert id to integer
msginfoparts[0] = int(msginfoparts[0])
#Remove quotes
msginfoparts[2] = msginfoparts[2].replace('"', '')
#msginfoparts[4] = msginfoparts[4].replace('"', '')
#msginfoparts[5] = msginfoparts[5].replace('"', '')
smsmsgs.append({'id': msginfoparts[0], 'from': msginfoparts[2], 'msg': smsparts[partno+1]})
debugmsg('SMS id: %d, from: %s, msg: %s' % (msginfoparts[0], msginfoparts[2], smsparts[partno+1]))
except Exception:
debugmsg('Error CMGL %d: %s' % (partno, smsparts[partno]))
debugmsg('SMS total: %d' % len(smsmsgs))
else:
debugmsg('No SMS messages available')
return smsmsgs
#SMS Delete
def sms_delete(delindex):
debugmsg('SMS Delete: %d' % delindex)
MDM.send('AT+CMGD=' + str(delindex) + 'r', 0)
res = MDM.receive(50)#5 sec
MOD.sleep(1)#wait 0.1sec
if (res == 'rnOKrn'):
status = TRUE
else:
status = FALSE
return status
#SMS Send
def sms_send(to, text):
debugmsg('Send SMS to: %s, MSG: %s' % (to, text))
MDM.send('AT+CMGS="' + to + '"r', 0)
res = MDM.receive(50)#5 sec
MOD.sleep(1)#wait 0.1sec
#Check for SMS prompt
if (res == 'rn> '):
#Send SMS message text
MDM.send(text, 0)
#End SMS
MDM.sendbyte(0x1A, 0)
res2 = MDM.receive(180)#5 sec
MOD.sleep(1)#wait 0.1sec
if (res2.find('rnOKrn') != -1):
debugmsg('SMS sent')
status = TRUE
else:
debugmsg('SMS Send: ' + res2)
debugmsg('Did not get SMS sent confirmation')
status = FALSE
MDM.receive(1); # may prevent +CMS ERROR: 331 crash
else:
debugmsg('SMS Send: ' + res)
debugmsg('Did not receive SMS prompt')
#Abort SMS (just in case)
MDM.sendbyte(0x1B, 0)
MOD.sleep(1)#wait 0.1sec
status = FALSE
return status
##################################################################################
def constraints(gpspos):
global line_cut
gpspos_parts = gpspos.split(',')
if ( (gpspos_parts[5] == '2') or (gpspos_parts[5] == '3') ): #2D or 3D fix
lat_temp = gpspos_parts[1].split('.')
lon_temp = gpspos_parts[2].split('.')
lat = int(lat_temp[0])
lon = int(lon_temp[0])
debugmsg('lat = ' + repr(lat) + ' lon = ' + repr(lon))
if ( (4320 < lat) and (8036 < lon) ):
debugmsg('Within constraints.')
elif ( line_cut == FALSE ):
debugmsg('Outside of constraints, cutting line!')
cutoff()
line_cut = TRUE
else:
debugmsg('Outside of constraints.')
###### Init ######
# configure GPIO pins
GPIO.setIOdir(8,0,1) # GPIO8 0 output
SER.set_speed('115200','8N1')
SER.send('rn--------------------rnrn')
debugmsg('Running...');
#Set verbose error reporting
MDM.send('AT+CMEE=2r', 0)
MDM.receive(50)#5 sec
MOD.sleep(1)#wait 0.1sec
#May be required in North America / Canada to switch the unit to 900/1900MHz frequency (uncomment if in those areas)
#MDM.send('AT#BND=3r', 0)
#MDM.receive(1)#0.1sec
#MOD.sleep(1)#wait 0.1sec
gpspos_lastvalid = ''
#Setup SMS
sms_setup()
#Main loop
while 1:
debugmsg('Entering loop')
#Retrieve current position
gpspos = getActualPosition()
debugmsg('Position: %s' % gpspos)
constraints(gpspos)
# A few tests:
#constraints('002317.908,4326.9364N,08027.2974W,4.0,286.4,2,152.30,10.94,5.90,110907,03')
#constraints('002317.908,4326.9364N,08211.2974W,4.0,286.4,2,152.30,10.94,5.90,110907,03')
#Retrieve GPS fix status
gps_statusnow = gps_status(gpspos)
# GPS crash prevention (reset GPS after ~10 minutes if no fix)
if (had_fix == FALSE):
nofix_count = nofix_count + 1
if (nofix_count == 60):
reset_gps()
nofix_count = 0
else:
nofix_count = 0
#Save last valid position
#If position fix valid, or none recorded already, use last retrieved
if ( (gps_statusnow == TRUE) or (gpspos_lastvalid == '') ):
gpspos_lastvalid = gpspos
#Check for / process new SMS messages
sms_proceses(gpspos_lastvalid)
debugmsg('Powersave for 10 seconds')
#Powersave for 10 seconds
MOD.powerSaving(10)
Hello Tim,
how can i order your board?
thank you in advance
How much for the module?
Hey Tim,
I am wondering if you could provide the eagle files for this project.
Or is it possible for me to order one of these boards of you.
Regards
Reuben Coelho
Can you provide the Eagle files for the PCB? Thanks!
Hoi Tim,
Ik heb hier een aantal GM862 modules liggen en een beagleboard is onderweg.
Om het wiel niet opnieuw uit te vinden zou ik graag gebruik maken van de pcb die jij hebt ontwikkeld. Heb je hier nog pcb’s van? Of het ontwerp? Dat zou mij snel op weg helpen. Uiteraard deel ik mijn bevindingen met je en zal ik onthouden dat jij de ontwikkelaar van het board bent.
Kunnen we hier een keer contact over hebben?
Met vriendelijke groet,
Tijs van Roon
Hoi Thijs, we kunnen overal contact over hebben, maar het schematic bijvoorbeeld staat hier http://www.timzaman.nl/?p=1165&lang=en . Als je een PCBtje wilt hebben kun je een euro of 5 paypallen, heb er nog een aantal liggen.
Hoi Tim,
bedankt voor je antwoord. Ik heb helemaal niet opgemerkt dat hier een antwoord stond. Had een mailtje van je blog verwacht. Stom! Excuus dus!
Het schema komt me goed van pas. Ik ga het (in de loop van de tijd) met de BeagleBone proberen die ik hier heb liggen. Die is voor mij wat praktischer.
Ik laat je mijn bevindingen weten. Bedankt voor je hulp!