2018-08-26 13:40:01 -04:00
""" SIM800 library for the TiLDA MK4 """
___license___ = " MIT "
___dependencies___ = [ ]
2018-08-19 13:14:37 -04:00
import machine
import time
2018-08-26 12:37:19 -04:00
import micropython
2018-08-28 15:51:02 -04:00
import tilda
2018-08-26 12:37:19 -04:00
2018-08-19 13:14:37 -04:00
uart_port = 1
uart_default_baud = 115200
uart_timeout = 28
2018-08-26 07:20:33 -04:00
default_response_timeout = 2000
2018-08-19 13:14:37 -04:00
2018-08-26 12:37:19 -04:00
status_pin = machine . Pin ( machine . Pin . GPIO_SIM_STATUS , machine . Pin . IN )
ringer_pin = machine . Pin ( machine . Pin . GPIO_SIM_RI , machine . Pin . IN )
pwr_key_pin = machine . Pin ( machine . Pin . GPIO_SIM_PWR_KEY , machine . Pin . OUT )
2018-08-28 14:23:02 -04:00
amp_pin = machine . Pin ( machine . Pin . GPIO_AMP_SHUTDOWN , machine . Pin . OUT )
2018-08-28 15:08:41 -04:00
netlight_pin = machine . Pin ( machine . Pin . GPIO_SIM_NETLIGHT , machine . Pin . IN )
2018-08-19 13:14:37 -04:00
# Open the UART
2018-08-24 07:57:25 -04:00
uart = machine . UART ( uart_port , uart_default_baud , mode = machine . UART . BINARY , timeout = uart_timeout )
2018-08-26 07:20:33 -04:00
dirtybuffer = False # Flag if the buffer could have residual end of reresponsesponces line in it?
2018-08-19 13:14:37 -04:00
2018-08-28 07:56:18 -04:00
# A list of callback functions
2018-08-26 12:37:19 -04:00
callbacks = [ ]
2018-08-28 07:56:18 -04:00
# Globals for remembering callback data
2018-08-26 19:22:23 -04:00
clip = " "
2018-08-28 07:56:18 -04:00
btpairing = ' '
2018-08-28 17:49:14 -04:00
holdoffirq = False
2018-08-26 12:37:19 -04:00
2018-08-19 13:14:37 -04:00
# Check if the SIM800 is powered up
def ison ( ) :
return status_pin . value ( ) == 1
# Check if the SIM800 is ringing
def isringing ( ) :
return ringer_pin . value ( ) == 0
2018-08-26 12:37:19 -04:00
# Register for a callback
def registercallback ( call , function ) :
callbacks . append ( [ call , function ] )
# Deregitser for a callback
def deregistercallback ( function ) :
for entry in callbacks :
if entry [ 1 ] == function :
callbacks . remove ( entry )
2018-08-26 07:20:33 -04:00
# Identify if this was a positive response
def ispositive ( response ) :
return ( response == " OK " ) or response . startswith ( " CONNECT " ) or response . startswith ( " SEND OK " )
2018-08-19 13:14:37 -04:00
2018-08-26 07:20:33 -04:00
# Identify if this was a negative response
def isnegative ( response ) :
return ( response == " NO CARRIER " ) or ( response == " ERROR " ) or ( response == " NO DIALTONE " ) or ( response == " BUSY " ) or ( response == " NO ANSWER " ) or ( response == " SEND FAIL " ) or ( response == " TIMEOUT " ) or ( response == " TimeOut " )
2018-08-26 07:11:11 -04:00
2018-08-26 07:20:33 -04:00
# Identify if this is the completion of a response
def isdefinitive ( response , custom = None ) :
2018-08-26 07:11:11 -04:00
if custom is not None :
2018-08-26 07:20:33 -04:00
return ispositive ( response ) or isnegative ( response ) or response . startswith ( custom )
2018-08-26 07:11:11 -04:00
else :
2018-08-26 07:20:33 -04:00
return ispositive ( response ) or isnegative ( response )
2018-08-19 13:14:37 -04:00
2018-08-26 07:20:33 -04:00
# Extract the [first/only] parameter from a response
def extractval ( parameter , response , default = " " ) :
for entry in response :
2018-08-19 13:14:37 -04:00
if entry . startswith ( parameter ) :
return ( entry [ len ( parameter ) : ] ) . strip ( )
return default
2018-08-26 07:20:33 -04:00
# Extract all parameter from a response
def extractvals ( parameter , response ) :
2018-08-22 10:12:12 -04:00
result = [ ]
2018-08-26 07:20:33 -04:00
for entry in response :
2018-08-22 10:12:12 -04:00
if entry . startswith ( parameter ) :
result . append ( ( entry [ len ( parameter ) : ] ) . strip ( ) )
return result
2018-08-26 07:20:33 -04:00
# Read a lines of response from the UART
2018-08-19 13:14:37 -04:00
def readline ( ) :
stringin = " "
while ( True ) :
charin = uart . read ( 1 )
# If we time out we are at the end of a line
if charin is None :
return stringin
2018-08-22 08:48:42 -04:00
# We this the end of the line?
elif ( charin == b ' \r ' ) :
2018-08-19 13:14:37 -04:00
if ( stringin != " " ) :
return stringin
# This will be part of the string then
2018-08-22 08:48:42 -04:00
elif not ( charin == b ' \n ' ) :
2018-08-28 14:23:02 -04:00
stringin + = chr ( ord ( charin ) )
2018-08-26 19:22:23 -04:00
# Check if we have a callback hook for this line
2018-08-26 12:37:19 -04:00
def processcallbacks ( line ) :
2018-08-26 19:22:23 -04:00
global clip
2018-08-28 07:56:18 -04:00
global btpairing
# Check for the caller line information
2018-08-26 19:22:23 -04:00
if line . startswith ( " +CLIP: " ) :
clip = line [ 6 : ] . strip ( )
2018-08-28 07:56:18 -04:00
# Check for Bluetooth pairing request
if line . startswith ( " +BTPAIRING: " ) :
btpairing = line [ 11 : ] . strip ( )
2018-08-26 19:22:23 -04:00
# Check for app callbacks
for entry in callbacks :
if line . startswith ( entry [ 0 ] ) :
micropython . schedule ( entry [ 1 ] , line [ len ( entry [ 0 ] ) : ] . strip ( ) )
2018-08-26 12:37:19 -04:00
# Process the buffer for unsolicited result codes
def processbuffer ( ) :
while uart . any ( ) > 0 :
line = readline ( )
processcallbacks ( line )
2018-08-19 13:14:37 -04:00
# Execute a command on the module
2018-08-26 12:37:19 -04:00
# The same interface as and called by command() but without power so can be called from power()
def command_internal ( command = " AT " , response_timeout = default_response_timeout , required_response = None , custom_endofdata = None ) :
2018-08-22 08:48:42 -04:00
global dirtybuffer
2018-08-28 17:49:14 -04:00
global holdoffirq
# Don't let the interupt process the buffer mid command
holdoffirq = True
2018-08-26 12:37:19 -04:00
# Process anything remaining in the buffer
processbuffer ( )
2018-08-19 13:14:37 -04:00
# Send the command
2018-08-22 08:48:42 -04:00
uart . write ( command + " \r " )
2018-08-19 13:14:37 -04:00
# Read the results
result = [ ]
complete = False
2018-08-26 07:20:33 -04:00
customcomplete = required_response is None
timeouttime = time . time ( ) + ( response_timeout / 1000 )
2018-08-19 13:14:37 -04:00
while ( time . time ( ) < timeouttime ) :
line = readline ( )
2018-08-26 12:37:19 -04:00
processcallbacks ( line )
2018-08-19 13:14:37 -04:00
# Remember the line if not empty
if ( len ( line ) > 0 ) :
result . append ( line )
2018-08-26 07:20:33 -04:00
# Check if we have a standard end of response
2018-08-26 07:11:11 -04:00
if isdefinitive ( line , custom_endofdata ) :
2018-08-19 13:14:37 -04:00
complete = True
# Check if we have the data we are looking for
2018-08-26 07:20:33 -04:00
if ( required_response is not None ) and ( line . startswith ( required_response ) ) :
2018-08-19 13:14:37 -04:00
customcomplete = True
# Check if we are done
if complete and customcomplete :
2018-08-28 17:49:14 -04:00
holdoffirq = False
2018-08-19 13:14:37 -04:00
return result
# We ran out of time
# set the dirty buffer flag is an out of date end of responcs cound end up in the buffer
2018-08-26 07:20:33 -04:00
if required_response is None :
2018-08-19 13:14:37 -04:00
dirtybuffer = True
result . append ( " TIMEOUT " )
2018-08-28 17:49:14 -04:00
holdoffirq = False
2018-08-19 13:14:37 -04:00
return result
2018-08-28 15:51:02 -04:00
# Send the command to set the default configuration to the SIM800
def senddefaultconfig ( ) :
# Send a command to autonigotiate UART speed
command_internal ( " AT " )
# Turn on new SMS notificationn
command_internal ( " AT+CNMI=1,2 " )
# Turn on calling line identification notification
command_internal ( " AT+CLIP=1 " )
# Swith to text mode
command ( " AT+CMGF=1 " )
# Switch to ASCII(ish)
command ( " AT+CSCS= \" 8859-1 \" " )
# Enable DTMF detection
command ( " AT+DDET=1,0,1 " )
# Set up multichannel Bluetooth without transparent transmition
command ( " AT+BTSPPCFG= \" MC \" ,1 " )
command ( " AT+BTSPPCFG= \" TT \" ,0 " )
2018-08-22 08:48:42 -04:00
# Power on the SIM800 (True=on, False=off, returns true when on)
2018-08-28 14:23:02 -04:00
def power ( onoroff , asyncro ) :
2018-08-22 08:48:42 -04:00
# Get to a stable state if not async
2018-08-28 14:23:02 -04:00
if not asyncro and pwr_key_pin . value ( ) :
2018-08-19 13:14:37 -04:00
pwr_key_pin . off ( )
time . sleep ( 3 )
2018-08-28 17:49:14 -04:00
# Press the virtual power key if we are off
2018-08-22 08:48:42 -04:00
if not ( ison ( ) == onoroff ) :
pwr_key_pin . on ( )
2018-08-28 14:23:02 -04:00
if not asyncro :
2018-08-22 08:48:42 -04:00
time . sleep ( 3 )
# Have we just turned on?
isonnow = ison ( )
if ( isonnow == onoroff ) and pwr_key_pin . value ( ) :
# Stop pressing the virtual key
2018-08-19 13:14:37 -04:00
pwr_key_pin . off ( )
2018-08-22 08:48:42 -04:00
# Clear the buffer
2018-08-26 12:37:19 -04:00
processbuffer ( )
2018-08-19 13:14:37 -04:00
dirtybuffer = False
2018-08-26 12:37:19 -04:00
# We are now live
2018-08-22 08:48:42 -04:00
if isonnow :
2018-08-28 15:51:02 -04:00
# Set the deault configuration
senddefaultconfig ( )
2018-08-22 08:48:42 -04:00
return isonnow
# Power on the SIM800 (returns true when on)
2018-08-28 14:23:02 -04:00
def poweron ( asyncro = False ) :
return power ( True , asyncro )
2018-08-22 08:48:42 -04:00
2018-08-26 19:22:23 -04:00
# Power off the SIM800 (returns true when off)
2018-08-28 14:23:02 -04:00
def poweroff ( asyncro = False ) :
return not power ( False , asyncro )
2018-08-19 13:14:37 -04:00
# Change the speed on the communication
def uartspeed ( newbaud ) :
global uart
2018-08-22 08:48:42 -04:00
command ( " AT+IPR= " + str ( newbaud ) )
2018-08-19 13:14:37 -04:00
uart . deinit ( )
if ( newbaud == 0 ) :
uart = machine . UART ( uart_port , uart_default_baud , mode = UART . BINARY , timeout = uart_timeout )
else :
uart = machine . UART ( uart_port , newbaud , mode = UART . BINARY , timeout = uart_timeout )
2018-08-28 15:51:02 -04:00
# Netlight sheduled (called for polling uart)
def netlightscheduled_internal ( pinstate ) :
# Complete the setup procedure if needed
if pwr_key_pin . value ( ) and ison ( ) :
poweron ( )
2018-08-28 15:08:41 -04:00
# Check for incomming commands
2018-08-28 17:49:14 -04:00
if holdoffirq == False :
processbuffer ( )
2018-08-28 15:08:41 -04:00
2018-08-28 15:51:02 -04:00
# Netlight IRQ (called for polling uart)
def netlightirq_internal ( pinstate ) :
micropython . schedule ( netlightscheduled_internal , pinstate )
2018-08-28 15:08:41 -04:00
# Command is the AT command without the AT or CR/LF, response_timeout (in ms) is how long to wait for completion, required_response is to wait for a non standard response, custom_endofdata will finish when found
2018-08-26 12:37:19 -04:00
def command ( command = " AT " , response_timeout = default_response_timeout , required_response = None , custom_endofdata = None ) :
# Check we are powered on and set up
poweron ( False )
# Call the internal command() function
return command_internal ( command , response_timeout , required_response , custom_endofdata )
2018-08-19 13:14:37 -04:00
# Make a voice call
def call ( number ) :
2018-08-22 08:48:42 -04:00
command ( " ATD " + str ( number ) + " ; " , 20000 )
2018-08-19 13:14:37 -04:00
# Answer a voice call
2018-08-22 08:48:42 -04:00
def answer ( ) :
command ( " ATA " , 20000 )
2018-08-19 13:14:37 -04:00
2018-08-25 05:25:58 -04:00
# End/reject a voice call
2018-08-22 08:48:42 -04:00
def hangup ( ) :
command ( " ATH " )
# Redial the last number
def redial ( ) :
command ( " ATDL " )
2018-08-26 19:22:23 -04:00
# Get the current/latest number to call
def latestnumber ( ) :
if ison ( ) :
processbuffer ( )
return clip . split ( " , " ) [ 0 ] . strip ( " \" " )
2018-08-22 08:48:42 -04:00
# Play DTMF tone(s) on a call
def dtmf ( number ) :
validdigits = ' 1234567890#*ABCD '
for digit in str ( number ) . upper ( ) :
if ( digit in validdigits ) :
command ( " AT+VTS= " + digit )
elif ( digit == ' , ' ) :
time . sleep ( 1 )
# Send an SMS message
def sendsms ( number , message ) :
# Swith to text mode
command ( " AT+CMGF=1 " )
# Switch to ASCII(ish)
command ( " AT+CSCS= \" 8859-1 \" " )
# Send the message
2018-08-26 07:11:11 -04:00
command ( " AT+CMGS= \" " + str ( number ) + " \" " , 2000 , None , " > " )
2018-08-22 08:48:42 -04:00
return command ( message + " \x1a " , 60000 )
2018-08-22 10:12:12 -04:00
# List the summery of SMS messages (0=unread,1=read,2=saved unread,3=saved sent, 4=all)
def listsms ( stat = 0 ) :
statvals = [ " REC UNREAD " , " REC READ " , " STO UNSENT " , " STO SENT " , " ALL " ]
2018-08-22 11:18:59 -04:00
# Swith to text mode
command ( " AT+CMGF=1 " )
# Retrieve the list
2018-08-22 10:12:12 -04:00
return extractvals ( " +CMGL: " , command ( " AT+CMGL= \" " + statvals [ stat ] + " \" ,1 " , 8000 ) )
# Check if we have recived a new unread SMS message
def newsms ( ) :
return len ( listsms ( stat = 0 ) ) > 0
# Read an SMS message
def readsms ( index , leaveunread = False ) :
2018-08-22 11:18:59 -04:00
# Swith to text mode
command ( " AT+CMGF=1 " )
# Switch to ASCII(ish)
command ( " AT+CSCS= \" 8859-1 \" " )
# Retrieve the message
2018-08-26 07:20:33 -04:00
response = command ( " AT+CMGR= " + str ( index ) + " , " + str ( int ( leaveunread ) ) , 5000 )
if ( len ( response ) > = 3 ) :
return response [ - 2 ]
2018-08-22 10:12:12 -04:00
else :
return " "
# Delete an SMS message
def deletesms ( index ) :
command ( " AT+CMGD= " + str ( index ) , 5000 )
2018-08-22 08:48:42 -04:00
# Get the IMEI number of the SIM800
def imei ( ) :
2018-08-26 07:20:33 -04:00
response = command ( " AT+GSN " )
if ( len ( response ) > = 2 ) :
return response [ - 2 ]
2018-08-22 08:48:42 -04:00
else :
return " "
2018-08-24 03:57:59 -04:00
# Get the IMSI number of the Sim Card
def imsi ( ) :
2018-08-26 07:20:33 -04:00
response = command ( " AT+CIMI " )
if ( len ( response ) > = 2 ) :
return response [ - 2 ]
2018-08-24 03:57:59 -04:00
else :
return " "
2018-08-19 13:14:37 -04:00
2018-08-26 13:40:01 -04:00
# Get the ICCID of the Sim Card
def iccid ( ) :
response = command ( " AT+ICCID " )
return extractval ( " +ICCID: " , response )
2018-08-27 18:06:23 -04:00
# Get the received signal strength indication
2018-08-26 13:40:01 -04:00
def rssi ( ) :
response = command ( " AT+CSQ " )
return int ( extractval ( " +CSQ: " , response , " 0,0 " ) . split ( " , " ) [ 0 ] )
# Get the bit error rate
def ber ( ) :
response = command ( " AT+CSQ " )
return int ( extractval ( " +CSQ: " , response , " 0,0 " ) . split ( " , " ) [ 1 ] )
# Get the cell engineering information (True to include neighboring cell id)
def engineeringinfo ( neighbor = False ) :
command ( " AT+CENG=1, " + str ( int ( neighbor ) ) )
response = command ( " AT+CENG? " )
command ( " AT+CENG=0 " )
responselist = extractvals ( " +CENG: " , response ) [ 1 : ]
results = [ ]
for entry in responselist :
results . append ( [ entry [ 0 ] , entry . replace ( " \" " , " " ) . split ( " , " ) [ 1 : ] ] )
return results
# Get the cell id of the currently connected cell
def cellid ( ) :
return engineeringinfo ( ) [ 0 ] [ 1 ] [ 6 ]
2018-08-19 13:14:37 -04:00
# Get/Set ringer volume (0-100)
def ringervolume ( level = None ) :
# Set the new leve if we have one to set
if level is not None :
2018-08-22 08:48:42 -04:00
command ( " AT+CRSL= " + str ( level ) )
2018-08-19 13:14:37 -04:00
# Retieve the set level to report back
2018-08-26 07:20:33 -04:00
response = command ( " AT+CRSL? " )
return int ( extractval ( " +CRSL: " , response , 0 ) )
2018-08-19 13:14:37 -04:00
# Get/Set speaker volume (0-100)
def speakervolume ( level = None ) :
# Set the new leve if we have one to set
if level is not None :
2018-08-22 08:48:42 -04:00
command ( " AT+CLVL= " + str ( level ) )
2018-08-19 13:14:37 -04:00
# Retieve the set level to report back
2018-08-26 07:20:33 -04:00
response = command ( " AT+CLVL? " )
return int ( extractval ( " +CLVL: " , response , 0 ) )
2018-08-19 13:14:37 -04:00
2018-08-22 10:12:12 -04:00
# Get/Set/Preview and set the ringtone (alert is 0-19)
def ringtone ( alert = None , preview = False ) :
# Set/preview the new ringtone if we have one to set
if alert is not None :
command ( " AT+CALS= " + str ( alert ) + " , " + str ( int ( preview ) ) )
# Retieve the current/new setting
2018-08-26 07:20:33 -04:00
response = command ( " AT+CALS? " )
current = extractval ( " +CALS: " , response , 0 ) . split ( " , " ) [ 0 ]
2018-08-22 10:12:12 -04:00
# Stop the preview unless we started it
if alert is None :
command ( " AT+CALS= " + current + " ,0 " )
# Return the surrent setting
return int ( current )
2018-08-26 15:57:26 -04:00
# Play a tone though the SIM800 (MHz and ms)
2018-08-28 14:23:02 -04:00
def playtone ( freq = 0 , duration = 2000 , asyncro = True ) :
2018-08-22 10:12:12 -04:00
if freq > 0 :
command ( " AT+SIMTONE=1, " + str ( freq ) + " , " + str ( duration ) + " ,0, " + str ( duration ) )
2018-08-28 14:23:02 -04:00
if not asyncro :
2018-08-22 10:12:12 -04:00
time . sleep ( duration / 1000 )
else :
command ( " AT+SIMTONE=0 " )
2018-08-26 15:57:26 -04:00
# Record audio (id=1-10)
def startrecording ( id = 1 , length = None ) :
if length is None :
return ispositive ( command ( " AT+CREC=1, " + str ( id ) + " ,0 " ) [ - 1 ] )
else :
return ispositive ( command ( " AT+CREC=1, " + str ( id ) + " ,0, " + str ( int ( length / 1000 ) ) ) [ - 1 ] )
# Stop recording audio
def stoprecording ( ) :
return ispositive ( command ( " AT+CREC=2 " ) [ - 1 ] )
# Delete recording
def deleterecording ( id = 1 ) :
return ispositive ( command ( " AT+CREC=3, " + str ( id ) ) [ - 1 ] )
# Play recording
def startplayback ( id = 1 , channel = 0 , level = 100 , repeat = False ) :
return ispositive ( command ( " AT+CREC=4, " + str ( id ) + " , " + str ( channel ) + " , " + str ( level ) + " , " + str ( int ( repeat ) ) ) [ - 1 ] )
# Stop playback
def stopplayback ( ) :
return ispositive ( command ( " AT+CREC=5 " ) [ - 1 ] )
# List recordings (returns a list of ids and size)
def listrecordings ( ) :
response = command ( " AT+CREC=7 " )
responselist = extractvals ( " +CREC: " , response )
result = [ ]
for entry in responselist :
splitentry = entry . split ( " , " )
result . append ( [ splitentry [ 1 ] , splitentry [ 2 ] ] )
return result
2018-08-19 13:14:37 -04:00
# Is the battery charging (0=no, 1=yes, 2=full)
def batterycharging ( ) :
2018-08-26 07:20:33 -04:00
response = command ( " AT+CBC " )
vals = extractval ( " +CBC: " , response , " 0,0,0 " ) . split ( " , " )
2018-08-19 13:14:37 -04:00
return int ( vals [ 0 ] )
# How full is the battery (1-100)
def batterycharge ( ) :
2018-08-26 07:20:33 -04:00
response = command ( " AT+CBC " )
vals = extractval ( " +CBC: " , response , " 0,0,0 " ) . split ( " , " )
2018-08-19 13:14:37 -04:00
return int ( vals [ 1 ] )
2018-08-22 10:12:12 -04:00
2018-08-19 13:14:37 -04:00
# List the available operator (returns list of [0=?,1=available,2=current,3=forbidden], 0=long name, 1=short name, 2=GSMLAI )
def listoperators ( available_only = True ) :
delim = " |||| "
2018-08-26 07:20:33 -04:00
response = command ( " AT+COPS=? " , 45000 )
responsedata = extractval ( " +COPS: " , response , " " ) . split ( " ,, " ) [ 0 ]
responselist = responsedata . replace ( " ),( " , delim ) [ 1 : - 1 ] . split ( delim )
2018-08-19 13:14:37 -04:00
results = [ ]
2018-08-26 07:20:33 -04:00
for entry in responselist :
2018-08-19 13:14:37 -04:00
subresults = [ ]
for subentry in entry . split ( " , " ) :
subresults . append ( subentry . strip ( " \" " ) )
if ( not available_only ) or ( subresults [ 0 ] == " 1 " ) or ( subresults [ 0 ] == " 2 " ) :
results . append ( subresults )
return results
# Get the current operator (format 0=long name, 1=short name, 2=GSMLAI)
def currentoperator ( format = 0 ) :
2018-08-22 08:48:42 -04:00
command ( " AT+COPS=3, " + str ( format ) )
2018-08-26 07:20:33 -04:00
response = command ( " AT+COPS? " )
responsedata = extractval ( " +COPS: " , response , " " ) . split ( " , " )
if ( len ( responsedata ) > = 3 ) :
return responsedata [ 2 ] . strip ( " \" " )
2018-08-19 13:14:37 -04:00
else :
return " "
2018-08-22 18:51:11 -04:00
# Set the operator selection ([0=automatic,1=Manual,2=deregister,4=try manual then automatic])
def setoperator ( mode , format = None , operator = None ) :
2018-08-19 13:14:37 -04:00
params = " "
if format is not None :
params + = " , " + str ( format )
if operator is not None :
2018-08-28 17:49:14 -04:00
params + = " , \" " + str ( operator ) + " \" "
2018-08-22 08:48:42 -04:00
command ( " AT+COPS= " + str ( mode ) + params , 120000 )
2018-08-19 13:14:37 -04:00
# Get the activity status (returns 0=ready, 2=unknown, 3=ringing, 4=call in progress)
def getstatus ( ) :
2018-08-26 07:20:33 -04:00
response = command ( " AT+CPAS " )
return int ( extractval ( " +CPAS: " , response , " 2 " ) )
2018-08-22 08:48:42 -04:00
2018-08-22 11:02:32 -04:00
# Get the firmware revision
def getfirmwarever ( ) :
2018-08-26 07:20:33 -04:00
response = command ( " AT+CGMR " )
if ( len ( response ) > = 3 ) :
return response [ - 2 ]
2018-08-22 11:02:32 -04:00
else :
return " "
2018-08-22 08:48:42 -04:00
# Request Unstructured Supplementary Service Data from network
def ussd ( ussdstring , timeout = 8000 ) :
2018-08-26 07:20:33 -04:00
response = command ( " AT+CUSD=1, \" " + ussdstring + " \" " , timeout , " +CUSD: " )
return extractval ( " +CUSD: " , response , " " )
2018-08-19 13:14:37 -04:00
2018-08-22 08:48:42 -04:00
# Get my number (only works on some networks)
2018-08-19 13:14:37 -04:00
def getmynumber ( ) :
2018-08-26 07:20:33 -04:00
responsedata = ussd ( " *#100# " , 8000 ) . split ( " , " )
if ( len ( responsedata ) > = 2 ) :
2018-08-26 19:22:23 -04:00
num = responsedata [ 1 ] . strip ( ) . strip ( " \" " )
2018-08-31 14:54:40 -04:00
return num
2018-08-19 13:14:37 -04:00
else :
return " "
2018-08-22 10:53:57 -04:00
# Turn on or off Bluetooth
def btpower ( onoroff = True ) :
command ( " AT+BTPOWER= " + str ( int ( onoroff ) ) , 8000 )
# Turn on Bluetooth
def btpoweron ( ) :
btpower ( True ) ;
# Turn off Bluetooth
def btpoweroff ( ) :
btpower ( False ) ;
# Get the current status of Bluetooth (0=off,5=idel, other values docuemtned for "AT+BTSTATUS")
def btstatus ( ) :
2018-08-26 07:20:33 -04:00
response = command ( " AT+BTSTATUS? " )
return int ( extractval ( " +BTSTATUS: " , response , " 0 " ) )
2018-08-22 10:53:57 -04:00
# Is Bluetooth on?
def btison ( ) :
return btstatus ( ) > = 5
# Get/Set the Bluetooth host device name
def btname ( name = None ) :
if name is not None :
2018-08-26 07:20:33 -04:00
response = command ( " AT+BTHOST= " + str ( name ) )
2018-08-22 10:53:57 -04:00
# Retrieve the current name
2018-08-26 07:20:33 -04:00
response = command ( " AT+BTHOST? " )
responsedata = extractval ( " +BTHOST: " , response , " " ) . split ( " , " )
return responsedata [ 0 ]
2018-08-22 10:53:57 -04:00
# Get the Bluetooth address
def btaddress ( ) :
2018-08-26 07:20:33 -04:00
response = command ( " AT+BTHOST? " )
responsedata = extractval ( " +BTHOST: " , response , " " ) . split ( " , " )
if ( len ( responsedata ) > = 2 ) :
return responsedata [ - 1 ]
2018-08-22 10:53:57 -04:00
else :
return " "
2018-08-24 07:57:25 -04:00
2018-08-26 19:22:23 -04:00
# Get/Set Bluetooth visibility (True for on, False for off)
2018-08-25 05:25:58 -04:00
def btvisible ( visible = None ) :
2018-08-26 19:22:23 -04:00
# Power on if we want to be visible
2018-08-28 07:56:18 -04:00
if visible :
2018-08-26 19:22:23 -04:00
btpoweron ( )
2018-08-25 05:25:58 -04:00
# Set the new leve if we have one to set
if visible is not None :
2018-08-28 07:56:18 -04:00
command ( " AT+BTVIS= " + str ( int ( visible ) ) )
2018-08-25 05:25:58 -04:00
# Retieve the set gain to report back
2018-08-26 07:20:33 -04:00
response = command ( " AT+BTVIS? " )
return int ( extractval ( " +BTVIS: " , response , 0 ) )
2018-08-25 05:25:58 -04:00
2018-08-28 07:56:18 -04:00
# Get the Bluetooth address (timeout in ms from 10000 to 60000, returnd device ID, name, address, rssi)
2018-08-24 07:57:25 -04:00
def btscan ( timeout = 30000 ) :
2018-08-26 19:22:23 -04:00
btpoweron ( )
2018-08-28 07:56:18 -04:00
result = [ ]
2018-08-26 07:20:33 -04:00
response = command ( " AT+BTSCAN=1, " + str ( int ( timeout / 1000 ) ) , timeout + 8000 , " +BTSCAN: 1 " )
2018-08-28 07:56:18 -04:00
for entry in extractvals ( " +BTSCAN: 0, " , response ) :
splitentry = entry . split ( " , " )
result = [ int ( splitentry [ 0 ] ) , splitentry [ 1 ] . strip ( " \" " ) , splitentry [ 2 ] , int ( splitentry [ 3 ] ) ]
return result
2018-08-24 07:57:25 -04:00
2018-08-28 07:56:18 -04:00
# Get the requesting paring device name
def btparingname ( ) :
if ison ( ) :
processbuffer ( )
return btpairing . split ( " , " ) [ 0 ] . strip ( " \" " )
# Get the requesting paring passcode
def btparingpasscode ( ) :
if ison ( ) :
processbuffer ( )
splitdata = btpairing . split ( " , " )
if ( len ( splitdata ) > = 3 ) :
return splitdata [ 2 ]
else :
return " "
2018-08-24 10:03:24 -04:00
# Pair a Bluetooth device
def btpair ( device ) :
2018-08-28 07:56:18 -04:00
global btpairing
btpairing = ' '
2018-08-26 07:20:33 -04:00
response = command ( " AT+BTPAIR=0, " + str ( device ) , 8000 , " +BTPAIRING: " )
return extractval ( " +BTPAIRING: " , response , " " ) . split ( " , " )
2018-08-24 10:03:24 -04:00
# Confirm the pairing of a Bluetooth device
def btpairconfirm ( passkey = None ) :
2018-08-28 07:56:18 -04:00
global btpairing
btpairing = ' '
2018-08-24 10:03:24 -04:00
if passkey is None :
return command ( " AT+BTPAIR=1,1 " , 8000 )
else :
return command ( " AT+BTPAIR=2, " + str ( passkey ) , 8000 )
# Cancel/reject the pairing of a Bluetooth device
def btpairreject ( ) :
2018-08-28 07:56:18 -04:00
global btpairing
btpairing = ' '
2018-08-24 10:03:24 -04:00
return command ( " AT+BTPAIR=1,0 " , 8000 )
# Unpair a Bluetooth device (unpair everything when device is 0)
def btunpair ( device = 0 ) :
return command ( " AT+BTUNPAIR= " + str ( device ) , 8000 )
# List the paired Bluetooth devices
def btpaired ( ) :
2018-08-28 07:56:18 -04:00
result = [ ]
2018-08-26 07:20:33 -04:00
response = command ( " AT+BTSTATUS? " )
2018-08-28 07:56:18 -04:00
for entry in extractvals ( " P: " , response ) :
splitentry = entry . split ( " , " )
result = [ int ( splitentry [ 0 ] ) , splitentry [ 1 ] . strip ( " \" " ) , splitentry [ 2 ] ]
return result
2018-08-24 10:03:24 -04:00
2018-08-25 05:25:58 -04:00
# List profiles supported by a paired device
def btgetprofiles ( device ) :
2018-08-26 07:20:33 -04:00
response = command ( " AT+BTGETPROF= " + str ( device ) , 8000 )
responselist = extractvals ( " +BTGETPROF: " , response )
2018-08-25 05:25:58 -04:00
results = [ ]
2018-08-26 07:20:33 -04:00
for entry in responselist :
2018-08-27 18:06:23 -04:00
splitentry = entry . split ( " , " )
subresults = [ int ( splitentry [ 0 ] ) , splitentry [ 1 ] . strip ( " \" " ) ]
2018-08-25 05:25:58 -04:00
results . append ( subresults )
return results
2018-08-27 18:06:23 -04:00
# Is a paticula profile supported (usename=False for id, True for name)
def btprofilesupported ( device , usename ) :
profiles = btgetprofiles ( device )
for entry in profiles :
if ( type ( usename ) == int ) and ( entry [ 0 ] == usename ) :
return True
if ( type ( usename ) == str ) and ( entry [ 1 ] == usename ) :
return True
return False
2018-08-24 10:03:24 -04:00
# Connect a Bluetooth device
def btconnect ( device , profile ) :
2018-08-26 07:20:33 -04:00
response = command ( " AT+BTCONNECT= " + str ( device ) + " , " + str ( profile ) , 8000 , " +BTCONNECT: " )
return extractvals ( " +BTCONNECT: " , response )
2018-08-24 10:03:24 -04:00
2018-08-25 05:25:58 -04:00
# Disconnect a Bluetooth device
2018-08-24 10:03:24 -04:00
def btdisconnect ( device ) :
2018-08-26 07:20:33 -04:00
response = command ( " AT+BTDISCONN= " + str ( device ) , 8000 , " +BTDISCONN: " )
return extractvals ( " +BTDISCONN: " , response )
2018-08-24 10:03:24 -04:00
# List the Bluetooth connections
def btconnected ( ) :
2018-08-28 07:56:18 -04:00
result = [ ]
2018-08-26 07:20:33 -04:00
response = command ( " AT+BTSTATUS? " )
2018-08-28 07:56:18 -04:00
for entry in extractvals ( " C: " , response ) :
splitentry = entry . split ( " , " )
result = [ int ( splitentry [ 0 ] ) , splitentry [ 1 ] . strip ( " \" " ) , splitentry [ 2 ] ]
return result
2018-08-24 10:03:24 -04:00
2018-08-27 18:06:23 -04:00
# Push an OPP object/file over Bluetooth (must be paired for OPP, monitor +BTOPPPUSH: for sucsess / fail / server issue)
def btopppush ( device , filename ) :
response = command ( " AT+BTOPPPUSH= " + str ( device ) + " , " + filename , 45000 , " +BTOPPPUSH: " )
responce2 = extractval ( " +BTOPPPUSH: " , response , " " )
return responce2 == " 1 "
# Accept an OPP object/file from Bluetooth (monitor +BTOPPPUSHING: for offering, files stored in "\\User\\BtReceived")
def btoppaccept ( ) :
response = command ( " AT+BTOPPACPT=1 " , 45000 , " +BTOPPPUSH: " )
responce2 = extractval ( " +BTOPPPUSH: " , response , 0 )
return responce2 == " 1 "
2018-08-27 18:41:33 -04:00
# Send data over a Bluetooth serial connection
def btsppwrite ( connection , data ) :
response = command ( " AT+BTSPPSEND= " + str ( connection ) + " , " + str ( len ( data ) ) , 8000 , None , " > " )
if response [ - 1 ] . startswith ( " > " ) :
return ispositive ( command ( data ) [ - 1 ] )
else :
return False
# Receive data from a Bluetooth serial connection
def btsppread ( connection ) :
command ( )
2018-08-29 07:14:03 -04:00
global holdoffirq
2018-08-28 17:49:14 -04:00
# Don't let the interupt process the buffer mid command
holdoffirq = True
2018-08-27 18:41:33 -04:00
request = " AT+BTSPPGET=3, " + str ( connection ) + " \n "
uart . write ( request )
data = uart . read ( )
while True :
time . sleep ( uart_timeout / 1000 )
if uart . any ( ) == 0 :
break
data + = uart . read ( )
2018-08-28 17:49:14 -04:00
holdoffirq = False
2018-08-27 18:41:33 -04:00
if not data . endswith ( " ERROR \r \n " ) :
return data [ len ( request ) + 2 : - 6 ]
else :
return None
2018-08-27 18:06:23 -04:00
# Reject an OPP object/file transfer
def btoppreject ( ) :
command ( " AT+BTOPPACPT=0 " )
2018-08-25 05:25:58 -04:00
# Make a voice call
def btcall ( number ) :
2018-08-26 19:22:23 -04:00
btpoweron ( )
2018-08-25 05:25:58 -04:00
command ( " AT+BTATD " + str ( number ) , 20000 )
# Answer a voice call
def btanswer ( ) :
2018-08-26 19:22:23 -04:00
btpoweron ( )
2018-08-25 05:25:58 -04:00
command ( " AT+BTATA " , 20000 )
# End a voice call
def bthangup ( ) :
2018-08-26 19:22:23 -04:00
btpoweron ( )
2018-08-25 05:25:58 -04:00
command ( " AT+BTATH " )
# Redial the last number
def btredial ( ) :
2018-08-26 19:22:23 -04:00
btpoweron ( )
2018-08-25 05:25:58 -04:00
command ( " AT+BTATDL " )
# Play DTMF tone(s) on a Bluetooth call
def btdtmf ( number ) :
validdigits = ' 1234567890#*ABCD '
for digit in str ( number ) . upper ( ) :
if ( digit in validdigits ) :
command ( " AT+BTVTS= " + digit )
elif ( digit == ' , ' ) :
time . sleep ( 1 )
2018-08-24 10:03:24 -04:00
2018-08-25 05:25:58 -04:00
# Get/Set Bluetooth voice gain (0-15)
def btvoicevolume ( gain = None ) :
# Set the new leve if we have one to set
if gain is not None :
command ( " AT+BTVGS= " + str ( gain ) )
# Retieve the set gain to report back
2018-08-26 07:20:33 -04:00
response = command ( " AT+BTVGS? " )
return int ( extractval ( " +BTVGS: " , response , 0 ) )
2018-08-25 05:25:58 -04:00
# Get/Set microphone gain volume (0-15)
def btvoicevolume ( gain = None ) :
# Set the new leve if we have one to set
if gain is not None :
command ( " AT+BTVGM= " + str ( gain ) )
# Retieve the set gain to report back
2018-08-26 07:20:33 -04:00
response = command ( " AT+BTVGM? " )
return int ( extractval ( " +BTVGM: " , response , 0 ) )
2018-08-25 05:25:58 -04:00
# Get the Bluetooth signal quality for a device (-127-0)
def btrssi ( device ) :
2018-08-26 07:20:33 -04:00
response = command ( " AT+BTRSSI= " + str ( device ) )
return int ( extractval ( " +BTRSSI: " , response , 0 ) )
2018-08-25 05:25:58 -04:00
2018-08-26 07:11:11 -04:00
# Get available space on the flash storage
def fsfree ( ) :
2018-08-26 07:20:33 -04:00
response = command ( " AT+FSMEM " )
return extractval ( " +FSMEM: " , response , " ?:0bytes " ) . split ( " , " ) [ 0 ] . split ( " : " ) [ 1 ] [ : - 5 ]
2018-08-26 07:11:11 -04:00
# List the entries in directory on flash storage (returned directories end with "\\")
def fsls ( directory = " " ) :
if not directory . endswith ( " \\ " ) :
directory + = " \\ "
return command ( " AT+FSLS= " + str ( directory ) ) [ 1 : - 1 ]
# Get the size of a file on the flash storage
def fssize ( filename ) :
2018-08-26 07:20:33 -04:00
response = command ( " AT+FSFLSIZE= " + str ( filename ) )
return int ( extractval ( " +FSFLSIZE: " , response , " -1 " ) )
2018-08-26 07:11:11 -04:00
# Create a directory on flash storage
def fsmkdir ( directory ) :
return ispositive ( command ( " AT+FSMKDIR= " + str ( directory ) ) [ - 1 ] )
# Remove a directory on flash storage
def fsrmdir ( directory ) :
return ispositive ( command ( " AT+FSRMDIR= " + str ( directory ) ) [ - 1 ] )
# Create a file on flash storage
def fscreate ( filename ) :
return ispositive ( command ( " AT+FSCREATE= " + str ( filename ) ) [ - 1 ] )
2018-08-26 19:22:23 -04:00
# Read a chunk of data from a file on the flash storage
def fsreadpart ( filename , size = 256 , start = 0 ) :
2018-08-29 07:14:03 -04:00
global holdoffirq
2018-08-26 07:11:11 -04:00
mode = int ( start > 0 )
2018-08-26 15:57:26 -04:00
command ( )
2018-08-28 17:49:14 -04:00
# Don't let the interupt process the buffer mid command
holdoffirq = True
2018-08-26 15:57:26 -04:00
request = " AT+FSREAD= " + str ( filename ) + " , " + str ( mode ) + " , " + str ( size ) + " , " + str ( start ) + " \n "
uart . write ( request )
data = uart . read ( )
while True :
time . sleep ( uart_timeout / 1000 )
if uart . any ( ) == 0 :
break
data + = uart . read ( )
2018-08-28 17:49:14 -04:00
holdoffirq = False
2018-08-26 15:57:26 -04:00
if not data . endswith ( " ERROR \r \n " ) :
return data [ len ( request ) + 2 : - 6 ]
else :
return None
2018-08-26 07:11:11 -04:00
2018-08-26 19:22:23 -04:00
# Read data from a file on the flash storage
def fsread ( filename ) :
result = bytearray ( 0 )
while True :
chunk = fsreadpart ( filename , 256 , len ( result ) )
if chunk is not None :
result + = chunk
else :
return result
2018-08-26 15:57:26 -04:00
# Append a small chunk data to a file on the flash storage, you should use sfwrite
def fswritepart ( filename , data ) :
response = command ( " AT+FSWRITE= " + str ( filename ) + " ,1, " + str ( len ( data ) ) + " ,8 " , 2000 , None , " > " )
2018-08-26 07:20:33 -04:00
if response [ - 1 ] . startswith ( " > " ) :
2018-08-26 07:11:11 -04:00
return ispositive ( command ( data ) [ - 1 ] )
else :
return False
2018-08-26 15:57:26 -04:00
# Write data to a file on the flash storage
def fswrite ( filename , data , truncate = False ) :
length = len ( data )
pointer = 0
chunksize = 256
# Create a file if needed
if truncate or ( fssize ( filename ) < 0 ) :
fscreate ( filename )
# Loop through the data in small chunks
while pointer < length :
result = fswritepart ( filename , data [ pointer : min ( pointer + chunksize , length ) ] )
if not result :
return False
pointer + = chunksize
return True
2018-08-26 07:11:11 -04:00
# Delete a file from flash storage
def fsrm ( filename ) :
return ispositive ( command ( " AT+FSDEL= " + str ( filename ) ) [ - 1 ] )
# Rename a file on the flash storage
def fsmv ( filenamefrom , filenameto ) :
return ispositive ( command ( " AT+FSRENAME= " + str ( filenamefrom ) + " , " + str ( filenameto ) ) [ - 1 ] )
2018-08-28 15:08:41 -04:00
# Callback for call buton being pressed
def callbuttonpressed_internal ( nullparam = None ) :
answer ( )
# Callback for end buton being pressed
def endbuttonpressed_internal ( nullparam = None ) :
hangup ( )
2018-08-29 07:14:03 -04:00
# Startup...
2018-08-28 15:51:02 -04:00
2018-08-28 07:56:18 -04:00
# Start turning on the SIM800 asynchronously
2018-08-26 12:37:19 -04:00
onatstart = poweron ( True )
2018-08-28 14:23:02 -04:00
2018-08-28 15:51:02 -04:00
# Reset SIM800 configuration if hardware is still on from before
if onatstart :
senddefaultconfig ( )
2018-08-28 14:23:02 -04:00
# Turn on the audio amp
amp_pin . on ( )
2018-08-28 15:08:41 -04:00
# Hook in the Call / End buttons
tilda . Buttons . enable_interrupt ( tilda . Buttons . BTN_Call , callbuttonpressed_internal )
tilda . Buttons . enable_interrupt ( tilda . Buttons . BTN_End , endbuttonpressed_internal )
2018-08-28 15:51:02 -04:00
# Enable the interupts on network light to poll uart
netlight_pin . irq ( netlightirq_internal )