Python Serial Class for GM862 module

In case you want to communicate via serial connection to a telit GM862 module, this class might be useful for you. Modern GM862 modules have a python interpreter included. This Pyhon class is meant for a PC running Python and communicating with the module. If you want to send/ receive SMS or check GPS location, this might be for you. Though GPRS has not been looked into very much.

# Python Serial Class for GM862
# To communicate with a GM862 mobile device via AT commands 
# http://www.gm862.com/
# modern gm862 modules have a python interpreter on board
#
# http://iad.zhdk.ch
# http://iad.projects.zhdk.ch/physicalcomputing/
# Interaction Design, Zurich University of the Arts ZHdK
# Free to use non commercially - Provided as is, use at own risk
#
# You need the pySerial Library by Chris Liechti 
# http://pyserial.wiki.sourceforge.net/pySerial
#
# Used with a Sparkfun GM862 Evaluation Board USB EVK V3
# http://www.sparkfun.com/commerce/product_info.php?products_id=281
# settings:    SELINT = 2; we communicate over the selint 2 interface
#             SMSMODE = 1; improved SMS commands operation is on

import serial
import time as zeit

# Start GM862 Class Definition --------------------------------------

class GM862(object):
        def __init__(self, interface):
            self.interface = interface 
            self.ser = None
            self.openSerial()
            self.started = False

        # Serial Methods -------------------------------------------------------------    
        def openSerial(self):
            self.ser = serial.Serial(self.interface, 9600, 8,'N',1, rtscts=1, timeout=2)
            self.ser.open()
            print "GM862 "+ str(self.interface) +" open -------------------------\n"
        
        def closeSerial(self):
            self.ser.close()
            print "GM862 "+ str(self.interface) +" close ------------------------\n"
            
            
        def start(self, pin):
            """
            pin -> 4 digit int to activate SIM, string
            """
            
            print ":: begin startup routine :: ------------------------------------" + "\n"
            
            # enter pin  -----------------------------------------------------------            
            print "entering pin:"
            # always terminate commands with carriage return ascii 13
            self.ser.timeout = 5
            self.ser.write('AT+CPIN=' + pin + chr(13))
            self.ser.readline(size=None, eol=chr(13))
            self.ser.readline(size=None, eol=chr(13))
            print "PIN response: " + self.ser.readline(size=None, eol=chr(13)) + "\n"
            self.ser.flushInput()
            zeit.sleep(0.03)
             
            # network registration ------------------------------------------------- 
            print 'enable network registration result code:'
            self.ser.timeout = 2
            self.ser.write('AT+CREG=1' + chr(13))
            self.ser.readline(size=None, eol=chr(13))
            self.ser.readline(size=None, eol=chr(13))
            print "CREG response: " + self.ser.readline(size=None, eol=chr(13)) + "\n"
            self.ser.flushInput()
            zeit.sleep(0.03)


            self.ser.timeout = 30
            print 'request network registration report:'
            self.ser.write('AT+CREG?' + chr(13))
            self.ser.readline(size=None, eol=chr(13))
            self.ser.readline(size=None, eol=chr(13))
            resultCode = self.ser.readline(size=None, eol=chr(13))
            print resultCode

            if resultCode[-2] == "0":
                print "not registered, searching for operator" + "\n"
            elif resultCode[-2] == "1":
                print "registered to home network" + "\n"
            elif resultCode[-2] == "2":
                print "not registered, searching for operator" + "\n"
            elif resultCode[-2] == "3":
                print "registration denied" + "\n"
            elif resultCode[-2] == "4":
                print "unknown" + "\n"
            elif resultCode[-2] == "5":
                print "registered, roaming"
            self.ser.flushInput()
            zeit.sleep(0.03)


            # automatic band selection ----------------------------------------------
            print 'set automatic band selection:'
            self.ser.timeout = 10
            self.ser.write('AT#AUTOBND=2' + chr(13))            
            self.ser.readline(size=None, eol=chr(13))
            str(self.ser.readline(size=None, eol=chr(13)))
            print "AUTOBND response:" + self.ser.readline(size=None, eol=chr(13)) + "\n"
            self.ser.flushInput()
            zeit.sleep(0.03)
         
            
            # enable error reporting ------------------------------------------------
            print 'enable error reporting:'
            self.ser.timeout = 5
            self.ser.write('AT+CMEE=2' + chr(13))
            self.ser.readline(size=None, eol=chr(13))            
            self.ser.readline(size=None, eol=chr(13)) + "\n"
            print "CMEE response: "+self.ser.readline(size=None, eol=chr(13)) + "\n"
            self.ser.flushInput()
            zeit.sleep(0.03)
            
            
            # ready for input? ------------------------------------------------------
            print 'test at command'
            self.ser.write('AT' + chr(13))
            self.ser.readline(size=None, eol=chr(13))
            self.ser.readline(size=None, eol=chr(13))
            print "AT response: " + self.ser.readline(size=None, eol=chr(13)) + "\n"
            self.ser.readline(size=None, eol=chr(13))
            self.ser.flushInput()
            zeit.sleep(0.03)
            
            self.started = True
            print ":: end startup routine :: ------------------------------------" + "\n"
       
            
            
            
        def sendSMS(self, number, message):
            """
            number -> string, cell number in intl. format +41791234567 
            message -> string, message text 
            -> returns message Id as string
            """
            
            print 'sending sms to ' + str(number) + '---------- --- -- -\n'
            
            # send SMS ----------------------------------------------------------------
            print 'send SMS to ' + str(number)
            self.ser.timeout = 10
            self.ser.write('AT+CMGS=' + number +',145'+ chr(13))
            self.ser.readline(size=None, eol=chr(13))
            self.ser.readline(size=None, eol=chr(13))
            cmgsResponse = self.ser.readline(size=None, eol=chr(13))
            
            print cmgsResponse
            if cmgsResponse == "\n> ":
                print "Ready to send message:"
                self.ser.timeout = 60
                self.ser.write(message)
                self.ser.write(chr(0x1A))
            
                # read message reference or error
                self.ser.readline(size=None, eol=chr(13))
                messageId = self.ser.readline(size=None, eol=chr(13))
                self.ser.readline(size=None, eol=chr(13))
                ok = self.ser.readline(size=None, eol=chr(13))
                
                self.ser.flushInput()
                zeit.sleep(0.03)
                
                if ok == '\nOK\r': 
                    print "SMS sent " + ok.lstrip('\n') + " SMS Id: " + messageId.lstrip('\n+CMGS: ')
                    return messageId.lstrip('\n+CMGS: ')  # return only id number as string
                else: 
                    print "ERROR: " + ok
                    return ok
                
            else: 
                print "ERROR no > prompt: " + cmgsResponse
                                    
            
            
        def saveSMStoSIM(self, number, message):
            """
            number -> string, cell number in intl. format +41791234567
            message -> string to be stored
            returns message id
            """
 
            print "save sms to sim --------------------------------------"
            
            # write SMS to SIM
            self.ser.timeout = 2
            self.ser.write('AT+CMGW=' + number + '145' + chr(13))
            
            self.ser.readline(size=None, eol=chr(32))  # waiting for chr(32) > 
            self.ser.timeout = 5
            self.ser.write(message + chr(0x1A))
            
            self.ser.readline(size=None, eol=chr(13))
            messageId = self.ser.readline(size=None, eol=chr(13))
            self.ser.readline(size=None, eol=chr(13))
            ok = self.ser.readline(size=None, eol=chr(13))
            
            self.ser.flushInput()
            zeit.sleep(0.03)
                
            if ok == '\nOK\r': 
                print "SMS saved " + ok.lstrip('\n') + "SMS Id: " + messageId.lstrip('\n+CMGW: ')
                return messageId.lstrip('\n+CMGW: ')  # return only id number as string
            else: 
                print "ERROR: " + ok
                return ok
            
                             
        def getSMSspace(self):
            """
            returns list[used sms, max sms]
            """
            
            print "request sms space on sim -------------------------------------------"

            # request free space ------------------------------------------------------
            print 'request free sms space'
            self.ser.timeout = 2
            self.ser.write('AT+CPMS?' + chr(13))
            self.ser.readline(size=None, eol=chr(13))
            self.ser.readline(size=None, eol=chr(13))
            simResponse = self.ser.readline(size=None, eol=chr(13))
            self.ser.readline(size=None, eol=chr(13))
            ok = self.ser.readline(size=None, eol=chr(13))
            
            self.ser.flushInput()
            zeit.sleep(0.03)
            
            if ok == '\nOK\r':
                simResponseList = simResponse.split(',')
                print "SMS on SIM: " + simResponseList[1] + " used out of " + simResponseList[2] + "\n"
                return [simResponseList[1], simResponseList[2]]
            else: 
                print "ERROR \n" 
                return "ERROR"

        
        def getSMSindex(self):
            """
            -> returns list with used SMS indices on SIM
            [0,1,2,3,7,10]
            """

            print "Get SMS indices on SIM card --------------------------------------"
            
            # get SMS indices -----------------------------------------------------
            print "requesting indices"
            self.ser.timeout = 2
            self.ser.write('AT+CMGD=?' + chr(13))
            #print self.ser.readlines(sizehint=None, eol=chr(13))
            
            self.ser.readline(size=None, eol=chr(13))
            self.ser.readline(size=None, eol=chr(13))
            indexResponse = self.ser.readline(size=None, eol=chr(13))
            self.ser.readline(size=None, eol=chr(13))
            ok = self.ser.readline(size=None, eol=chr(13))

            if ok == '\nOK\r':
                print indexResponse
                indexResponse = indexResponse.replace('\n+CMGD: (', '')
                indexResponse = indexResponse.replace('),(0-4)\r','')
                indexResponse = indexResponse.split(',')
                
                self.ser.flushInput()
                zeit.sleep(0.03)

                print str(indexResponse) + "\n"
                return indexResponse
            
            else: 
                print "ERROR \n"
                return "ERROR"
                
                
            
        def deleteSMSonSIM(self, id):
            """
            id -> SMS index on SIM card, string
            """
            
            print "Delete SMS on SIM card --------------------------------------"
            
            # delete sms at index -----------------------------------------------------
            print 'delete SMS at index: ' + id
            self.ser.timeout = 5
            self.ser.write('AT+CMGD=' + id + chr(13))
            self.ser.readline(size=None, eol=chr(13))
            self.ser.readline(size=None, eol=chr(13))
            ok = self.ser.readline(size=None, eol=chr(13))

            self.ser.flushInput()
            zeit.sleep(0.03)

            if ok == '\nOK\r':
                print ok.lstrip('\n') + '\n'
                return "OK"
            else: 
                print "ERROR: "  + ok
                return "ERROR"
            
            
        def deleteAllSMS(self):
            """
            simply delete all stored SMS            
            """
            
            print "Delete all SMS from SIM card -------------------------------------"
            
            # delete all SMS x --------------------------------------------------------
            print 'delete all SMS on SIM '
            self.ser.timeout = 25
            self.ser.write('AT+CMGD=1,4' + chr(13))
            
            self.ser.readline(size=None, eol=chr(13))
            self.ser.readline(size=None, eol=chr(13))
            ok = self.ser.readline(size=None, eol=chr(13))

            self.ser.flushInput()
            zeit.sleep(0.03)

            if ok == '\nOK\r':
                print ok.lstrip('\n') + '\n'
                return "OK"
            else: 
                print "ERROR: "  + ok
                return "ERROR"
            
         
         
        def SMSsetup(self):
            """
            general SMS settings
            -> returns "OK" or "ERROR"
            """
            
            # SMS command operation mode ----------------------------------------------
            print 'set sms command operation mode'
            self.ser.timeout = 5
            self.ser.write('AT#SMSMODE=1' + chr(13))
            self.ser.readline(size=None, eol=chr(13))
            self.ser.readline(size=None, eol=chr(13))
            ok1 = self.ser.readline(size=None, eol=chr(13))

            self.ser.flushInput()
            zeit.sleep(0.03)

            if ok1 == '\nOK\r':
                print ok1.lstrip('\n') + '\n'
            else: 
                print "ERROR: "  + ok1
                return "ERROR"            
       
            # SMS Text mode ----------------------------------------------------------
            print 'set TEXT mode not PDU: '
            self.ser.timeout = 5
            self.ser.write('AT+CMGF=1' + chr(13))
            self.ser.readline(size=None, eol=chr(13))
            self.ser.readline(size=None, eol=chr(13))
            ok2 = self.ser.readline(size=None, eol=chr(13))

            self.ser.flushInput()
            zeit.sleep(0.03)   
            
            if ok2 == '\nOK\r':
                print ok2.lstrip('\n') + '\n'
                return "OK"
            else: 
                print "ERROR: "  + ok2
                return "ERROR"
            


        def listSMS(self, flag):
            """
            flag -> what to read, string
                
                "REC UNREAD" -  new message 
                "REC READ" - read message 
                "STO UNSENT" - stored message not yet sent 
                "STO SENT" - stored message already sent 
                "ALL" - all messages
            
            -> returns list of requested messages 
            ['header','message']
            ['index, flag, sender, phonebook, time','Message Text Here']
            ['3,"REC READ","+41791234567","","09/08/05,16:30:03+08"\r', '\nMessage Text Here\r']
            """
            
            # read messages ------------------------------------------------------
            print 'listing messages with flag: ' + flag
            self.ser.timeout = 5
            self.ser.write('AT+CMGL='+ flag + chr(13))
            
            # read until an 'OK' arrives 
            ok = 0
            readlist = []
            while ok == 0:
                line = self.ser.readline(size=None, eol=chr(13))
                #print line
                
                # stop reading when OK arrives
                if line.startswith('\nOK\r'):
                        ok = 1
                
                # add only interesting messages into a list
                if line.startswith('AT+CMGL') == False and \
                line.startswith('\nOK\r') == False and \
                line.startswith('\r') == False and \
                line.startswith('\n\r') == False:
                    # take out '\n+CMGL: ' at beginning of message
                    line = line.replace('\n+CMGL: ', '')
                    line = line.replace('\r', '')
                    line = line.replace('\n', '')
                    readlist.append(line)
                
                    
            self.ser.flushInput()
            zeit.sleep(0.03)   

            #print readlist
            return readlist
            
        
        # GPRS functions have not been fully tested, more later ----------------------------    
        def GPRSsetup(self, apn, conId, cid, pktSz, maxTo, connTo, txTo):
            """
            apn: depending on your provider, string
            APN = Access Point Name
                
                switzerland:    sunrise = "internet"
                                orange = "internet"
                                swisscom = "gprs.swisscom.com"
            
            conId: socket connection identifier 1-6, string
            cid:   pdp context identifier 1-5, string
            pktSz: packet size 0, automatic; 1-1500 bytes, string
            maxTo: inactivity timeout 0, no timeout; 1-65535 seconds, string
            connTo: connection timeout 10-1200 milliseconds, string
            txTo: data is sent if less bytes than packet size 0, no timeout 1-255 hundred milliseconds 
            
                                
            -> returns OK or ERROR
            """
            # define PDP packet data protocol context -----------------------------------------
            print 'setting pdp context '
            self.ser.timeout = 5
            self.ser.write('AT+CGDCONT=1,IP,internet' + chr(13))

            self.ser.readline(size=None, eol=chr(13))
            self.ser.readline(size=None, eol=chr(13))
            ok1 = self.ser.readline(size=None, eol=chr(13))

            self.ser.flushInput()
            zeit.sleep(0.03)   
            
            if ok1 == '\nOK\r':
                print ok1.lstrip('\n') + '\n'
            else: 
                print "ERROR: "  + ok1
                return "ERROR"          
                
            # socket configuration ------------------------------------------------------------
            print 'socket configuration'
            self.ser.timeout = 5
            self.ser.write('AT#SCFG='+ conId + ',' + cid + ',' + pktSz +',' + maxTo +',' + connTo + ',' + txTo + chr(13))

            self.ser.readline(size=None, eol=chr(13))
            self.ser.readline(size=None, eol=chr(13))
            ok2 = self.ser.readline(size=None, eol=chr(13))

            self.ser.flushInput()
            zeit.sleep(0.03)   
            
            if ok2 == '\nOK\r':
                print ok2.lstrip('\n') + '\n'
                return "OK"
            else: 
                print "ERROR: "  + ok1
                return "ERROR"          

            
        
        def GPRSstatus(self, cid, stat):
            """
            de/ activate gprs context
                        
            cid -> context identifier
                0; GSM context
                1-5; pdp context 
                
            stat -> 0; deactivate
                  1; activate
                  
            returns ->  IP after activation
                        ERROR when already activated
                        OK when deactivated
            """
            
            # gprs status ------------------------------------------------------------
            print 'de/ activate context'
            self.ser.timeout = 150
            self.ser.write('AT#SGACT='+ cid + ','+ stat + chr(13))

            self.ser.readline(size=None, eol=chr(13))
            self.ser.readline(size=None, eol=chr(13))
            ok = self.ser.readline(size=None, eol=chr(13))
            
            self.ser.flushInput()
            zeit.sleep(0.03)
            
            if ok == '\nOK\r':
                print ok.lstrip('\n') + 'context deactivated' +'\n'
                return "OK"
            elif ok.startswith('\n+CME ERROR'): 
                print ok
                return "ERROR"
            elif ok.startswith('\n#SGACT'):
                print 'IP ' + ok.lstrip('\n#SGACT: ')
                return ok.lstrip('\n#SGACT: ')
                
            
        def GPRSdial(self, connId, txProt, rPort, IPaddr, closureType, IPort, connMode):
            """
            open socket connection
            connId -> socket 1-6
            txProt -> 0: TCP, 1: UDP
            rPort -> remote host port 0 - 65535
            IPaddr -> IP address xxx.xxx.xxx.xxx or host name www.google.com
            closureType -> 0: local host closes when remote has closed
                           255: local host closes after escape sequence (+++)
            IPort -> UDP connection local port 0-65535
            connMode -> 0 online, 1 command mode 
            
            -> returns CONNECT, OK, or ERROR according to connMode
            """
            
            # socket dial ------------------------------------------------------------
            print 'opening socket connection to ' + IPaddr
            self.ser.timeout = 140
            self.ser.write('AT#SD='+ connId + ','+ txProt +','+ rPort +','+ IPaddr +','+ closureType +','+ IPort +','+ connMode+ chr(13))
            
            self.ser.readline(size=None, eol=chr(13))
            self.ser.readline(size=None, eol=chr(13))
            ok = self.ser.readline(size=None, eol=chr(13))
    
            self.ser.flushInput()
            zeit.sleep(0.03)
            
            print ok
            return ok
                
        
        def GPRSsuspend(self):
            """
            suspend open socket connection
            if closureType was set 255 in AT#SD
            """
            # suspend socket ---------------------------------------------------------
            print 'suspend open socket '
            self.ser.timeout = 5
            self.ser.write('+++'+ chr(13))
            
            print "1 "+ self.ser.readline(size=None, eol=chr(13))
            print "2 "+ self.ser.readline(size=None, eol=chr(13))
            print "3 "+ self.ser.readline(size=None, eol=chr(13))
    
            self.ser.flushInput()
            zeit.sleep(0.03)
                
            
        def GPRSrestore(self, connId):
            """
            restore suspended connection
            connId -> 1-6, string
            """
             # restore socket connection ----------------------------------------------
            print 'restore socket connection'
            self.ser.timeout = 5
            self.ser.write('AT#SO'+ connId + chr(13))
            
            print "1 "+ self.ser.readline(size=None, eol=chr(13))
            print "2 "+ self.ser.readline(size=None, eol=chr(13))
            print "3 "+ self.ser.readline(size=None, eol=chr(13))
    
            self.ser.flushInput()
            zeit.sleep(0.03)
            
            
        """
        # resume suspended connection
        AT#SO=1

        # close connection without deactivating gprs context
        AT#SH=1

        # socket listen
        AT#SL

        # queryDNS
        AT#QDNS="google.com"
        """    
        
        # GPS --------------------------------------------------------------------------
        def GPSreset(self, type):
            """
            reset GPS controller
            flag 0 - 3, string
                 0; Hardware reset
                 1; Coldstart (No Almanach, No Ephemeris)
                 2; Warmstart (No Ephemeris)
                 3; Hotstart (With stored Almanach and Ephemeris)
            -> returns OK
            """
            # reset GPS ----------------------------------------------------------------
            print 'resetting GPS controller'
            self.ser.timeout = 5
            self.ser.write('AT$GPSR=' + type + chr(13))
            
            self.ser.readline(size=None, eol=chr(13))
            self.ser.readline(size=None, eol=chr(13))
            ok = self.ser.readline(size=None, eol=chr(13))

                
            self.ser.flushInput()
            zeit.sleep(0.03)
            
            if ok == '\nOK\r':
                print ok.lstrip('\n') + '\n'
                return "OK"
            else: 
                print ok
                return "ERROR"
        

        def GPSgetPos(self):
            """
            get acquired position
            -> returns list 
            [UTC time, latitude, longitude, horizontal dilution, fix, speed Kmh, speed knots, date, satellites]
            """
            # reset GPS ----------------------------------------------------------------
            print 'resetting GPS controller'
            self.ser.timeout = 5
            self.ser.write('AT$GPSACP' + chr(13))
            
            self.ser.readline(size=None, eol=chr(13))
            self.ser.readline(size=None, eol=chr(13))
            data = self.ser.readline(size=None, eol=chr(13))   # data

            self.ser.flushInput()
            zeit.sleep(0.03)
            
            data = data.strip('\n$GPSACP: ')
            data = data.strip('\r')
            data = data.split(',')
            print data
            return data
        

        
# --------------------------------------------------------------------            
# Example Code -------------------------------------------------------
         
GM = GM862('/dev/tty.usbserial-A1000RiF')

#GM.openSerial()

# startup procedure with SIM Pin - set general settings
#GM.start('1234')

# SMS setup ------------------------------
#GM.SMSsetup()

# send SMS to number xy
#GM.sendSMS('+41791234567','testmessage')

# check how many SMS are saved on SIM
#GM.getSMSspace()

# save an SMS to SIM card
#GM.saveSMStoSIM('+41791234567', 'noch eine testmessage')

# delete SMS on SIM
#GM.deleteSMSonSIM('1')

# delete all SMS on SIM
#GM.deleteAllSMS()

# indices of saved SMS on SIM
#GM.getSMSindex()

# SMS settings to be noticed of incoming messages
#print GM.listSMS('ALL')

# GPRS ist not fully tested ... so please be careful and look closely ------------------
# GPRS settings ---------------------------
#GM.GPRSsetup('internet','1','1','100','0','500','1')

# GPRS status on/ off
# on
#GM.GPRSstatus('1','1')
# off
#GM.GPRSstatus('1','0')

# dial socket
#GM.GPRSdial('1', '0', '80', 'www.google.com', '255', '0', '1')
# suspend socket connection
#GM.GPRSsuspend()
# restore suspended socket connection
#GM.GPRSrestore('1')

# GPS ------------------------------------
# GPS controller reset
#GM.GPSreset('1')

# GPS get position
#GM.GPSgetPos()

GM.closeSerial()

Hi, I was wondering why you read three times from the serial when you are expecting a response. Two times seems to work for me… either that or I am missing something. I am using pyserial to send AT Commands to my mobile phone, so perhaps it could be something device dependent. When I try to read three times, just as you do, readline() stays waiting for something to read. My guess is that there is nothing and I should implement a timeout. The first time I get back the command I sent, the second time I get back the OK.

But yeah,the main question: why do you read three times? (See example of your code below).

Thanks!

self.ser.readline(size=None, eol=chr(13))
self.ser.readline(size=None, eol=chr(13))
print "CREG response: " + self.ser.readline(size=None, eol=chr(13)) + "\n"