SMS (Text) Gateway with MicroMite

Modified on 2018/03/18 07:56 by CaptainBoing — Categorized as: Communications, Control, Electronics, Network, Network (GSM), Wifi

Version 1.2. Lots of improvements and an easier-to-extend API parsing section. Added the console command section - not all commands work yet. Security in the code of your Wifi password, it is Base64 encoded so it is not plainly visible in the code. If you need an encoder, there's one here.

This SMS gateway attaches to your LAN via WiFi (Want to provide wired LAN through ENC28J60 one day). It takes a SIM card for your favourite GSM operator and connects to that mobile network. It then bridges the two and permits messages to be passed between them. Security is pretty low at the moment, but firewall external access for now.

Program-wise, there is nothing exotic about it - no CFunctions or anything, it is a good demonstration of using the building blocks in MMBasic to provide an intelligent (and for me) really useful tool that sees daily use. It represents about two months of evolving code with tears, tantrums, a few late nights and a lot of head scratching... READ THE DATASHEETS - over and over until they make sense.

It exposes a web API allowing text messages to be sent via a web browser or any HTTP client (i.e. your own programs). These are then forwarded in real time to the GSM network. The API is multi-session and up to 5 simultaneous connections to the API are supported. Sending of SMS messages is single session but because sending is handled on the main thread it naturally pauses the parsing while a text is sent so it providing an implicit queuing system. Messages seem to take about 5 seconds-ish to actually go from the GSM module, so the real time response (i.e. confirmation from the API) is entirely do-able. My own webpage code waits for the response and parses the reply from the API.

Inbound SMS messages are largely ignored (and deleted from the SIM) as of Version 1.1 except for the specially crafted messages from an "admin" mobile number. One activates a relay for 10 seconds, sending a message to the effect either side of the activation. and the other sends back the eight most important flags and the enclosure temperature.

For my application, I connected a mains extension through the Normally Closed contacts of a relay module. This feeds the mains power to the PSU for my fibre router. This router suffers from mystery lockups every few months. By sending my "crafted" message, I can remotely bounce the router through a controlled 10 second power cycle from anywhere on the planet. As a frequent traveler, I have tired of trying to talk "remote hands" through the procedure, even though to me it is trivial. Used this feature recently from Madeira when my network went dark. The gateway pings out to the internet at regular intervals (I ping one of my ISPs DNS serves - be careful who you ping - they might not like it) and alerts after a set number of fails, so I might not even know I have lost connectivity but this gateway will tell me. It then confirms when the internet connectivity comes back.

It is a bit rough round the edges, but it does it's job well.

Future improvements:

Software is stable but still evolving but for now it demonstrates a reasonably robust working solution. It has run continuously for over a year with no problems - in fact the only problems I did have were with a weak Vodafone signal that meant the M590 was constantly trying to register on the network. Recently went to a Virgin SIM on a £6 a month unlimited texts deal and it has never failed since.

Software and circuit diagram below - there are numerous connectors for modules cheaply available on eBay - the whole thing should cost you less than £20 in bits.

The LCD is currently unused (although a header is there) and rich logging is provided through the CH340G USB interface which connects to the console of the micromite. The idea being eventually that it becomes a self-contained module and an LCD display will provide status etc. There's no need to leave it plugged in to putty all the time (but I do coz I love logs).



Circuit Diagram The M590 module has a dodgy voltage dropper in the form of a single diode, and the power rail is marked as +5V. It relies on the forward voltage of the 1N4001 to bring the power in line with the M590's working spec - a practice expressly forbidden in the documentation, but hey what do the Chinese care so long as they can sell stuff. This caused me lots of problems early on with the modem section resetting out-of-the-blue. I replaced the diode with a wire link (in the photo, between the SIM and M590) and drove it from the 3.3V - it is perfectly happy now it's working in accordance with the spec :o/ beware cheap modules I guess. I also modified the module by bringing the "EMERGPDOWN" pin of the M590 (effectively a reset) out to the header so I can reset the thing. Normally this isn't available on these modules and you have a power toggle signal that mimics the power button on a mobile... problem is because it is a toggle, you can never be certain of the state of the module. As I found out when it was running from 5v, you can get weird outages, toggle the power to try and resolve it and find you have actively turned the thing off (because it was actually on but not responding)... horrible arrangement.

Image



Completed working unit (yet to be cased nicely). Power is from a 5V3A "wallwart", 5v for the relay module and LCD, everything else is 3.3v from a 3A DC-DC converter module which gives a juicy power rail - specifically for the GSM module which can draw up to 1A with a weak mobile signal and the ESP8266 can draw 280mA, so bags of power for everything.

Image



Sending a text message from a web page...

Image



... received on the SMS Gateway API... Output from the console log

Image



...and the same text received on my mobile phone.

Image



Here is a procession of recieved SMS messages. this shows a typical life cycle together with some power cycling.

Image


Here is the source code... remember this is very specific to my application, you'll have to pick through it if you want to use it and add/remove any bits you like. Please use the discuss option to ask questions, that way everyone sees the thread and you will likely answer the same questions they have ;o)


'Preamble
	OPTION BASE 0
        CONST Mime$="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
	CONST Secs=1000,Mins=Secs*60,Hrs=Mins*60
	CONST GSM=1,GSMRST=16,LAN=2,LANRST=2,RL1=15
	CONST GSMTRchg=30,PRchg=180,PCRchg=2,DnRmdr=5
	CONST WDTime=60000
	CONST q=chr$(34)

	CONST defHB="05:59" ' time to send the daily heartbeat message , set to -1 to disable
	CONST defPA="8.8.8.8" ' an address to ping on the internet
	CONST defAMbl$="+4470123456789"  ' the "admin" mobile number
	CONST defIP="192.168.0.14" ' the unit's static IP address
	CONST defGW="192.168.0.1" ' its network gateway
	CONST defMS="255.255.255.0"' network mask
	CONST defAP="xyz" '  your wifi access point
	CONST defPW="bXkgcGFzc3dvcmQ==" ' and its password in B64 encoded form
	CONST defTmpAlm=40 ' the enclosure temperature alarm point

	' 0-63 Flag CONSTs
	CONST SlaveSKT	=0
	CONST LANUp	=1
	CONST InetUp	=2
	CONST GSMUp	=3
	CONST GSMReg	=4

	CONST WDRst	=7

	CONST Conn	=8 'HTTP channels 0...
	'		=9
	'		=10
	'		=11
	'		=12 '... to 4

	CONST HBFg	=28
	CONST ConRFG	=29
	CONST ConPFG	=30
	CONST TOFg	=31

'H/W Init
	WATCHDOG OFF
	PIN(LANRST)=1:SETPIN LANRST,DOUT
	PIN(GSMRST)=0:SETPIN GSMRST,DOUT,OC
	PIN(RL1)=1:SETPIN RL1,DOUT,OC

'S/W Init
	DIM STRING A$,B$,D$,HBTm LENGTH 5,MAC LENGTH 20
	DIM STRING PA$ LENGTH 15,IP$ LENGTH 15,GW$ LENGTH 15,MS$ LENGTH 15,AP$ LENGTH 32,PW$ LENGTH 32
	DIM STRING LNBuf$,CBuf$,Ver LENGTH 64,LANRxLN$,LANTxLN$,TC$ LENGTH 1,LL1$ LENGTH 16,LL2$ LENGTH 16
	DIM INTEGER FLAGS,h,m,C,Z,Chan,PTmr,PCt,RL1TMR
	DIM INTEGER LANf,GSMf,DSMSCt,OBSMSCt,IBSMSCt,Temp,TmpAlm
	DIM FLOAT x

	HBTm=defHB
	AMbl$=defAMbl$
	TmpAlm=defTmpAlm

	PA$=defPA
	IP$=defIP
	GW$=defGW
	MS$=defMS
	AP$=defAP
	PW$=B64Dec$(defPW)

	Ver="SMS Gateway V1.2 (C)2016,2017"

	CL Ver,0

	SetClock

	IF MM.WATCHDOG THEN FlagSet WDRst:CL "*** RESTART FORCED BY WATCHDOG ***",0 ELSE FlagRes WDRst

	FLAGS=0:FlagSet SlaveSKT

	TC$=" ":a$=Replace$(Replace$(DS$(),":",""),"-","")
	LL2$= MID$(a$,3,6) + MID$(a$,10,4) + STRING$(6," ")
	BuildLCD

	CL "System coming up",0

'timer inits
	PTmr=PRchg
	GSMPollTMR=GSMTRchg

	PCt=1:RL1TMR=-1

	CL "Starting LAN Interface",0
	TIMER=0
	Open "COM2:9600,1024" As #LAN
	ESP8266Reset
	FlagSet LANUp:FlagSet InetUp
	CL "... LAN up in "+STR$(TIMER)+"mS",0

	CL "Starting GSM Interface",0
	Chan=TIMER
	Open "COM1:9600,8192" As #GSM
	M590Reset
	FlagSet GSMUp:FlagSet GSMReg
	CL "... GSM up in "+STR$(TIMER-Chan)+"mS",0

	CL "Starting Core timer thread",0
	SETTICK 1*Secs,CoreTMR,4

	CL "Boot took "+STR$(TIMER)+"mS",0

	GL GSMSend(AMbl$,"SMS Gateway has started")

	CL "System Running",0

	GetTemp

Main:
	WATCHDOG=WDTime

	IF LEFT$(TIME$,5)=HBTm THEN
		IF FlagTest(HBFg)=0 THEN
			GL GSMSend(AMbl$,"SMS Gateway heartbeat. " + DATE$ + " " + TIME$)
			FlagSet HBFg
		END IF
	ELSE
		FlagRes HBFg
	END IF

	IF RL1TMR=0 then
		RL1TMR=-1
		FlagSet SlaveSKT
		PIN(RL1)=1
		A$="Slave socket power ON"
		CL A$,0
		GL GSMSend(AMbl$,A$)
	END IF

	IF PTmr<=0 THEN
		PTmr=PRchg

		A$=ESP8266Exec$("AT+PING="+q+PA+q,4000,0)

		IF A$="OK" THEN
			IF FlagTest(InetUp)=0 THEN
				GL GSMSend(AMbl$,"I/Net UP")
			END IF
			FlagSet InetUp
			CL "I/Net connected OK",0
			LANf=0:DSMSCt=0
			PCt=PCRchg
		ELSE
			FlagRes InetUp
			LANf=LANf+1:IF LANf>9 THEN LANf=9
			CL "I/Net Down",0
			PCt=PCt-1
			IF PCt=0 THEN
				GL GSMSend(AMbl$,"I/Net DOWN, please check")
			ELSE
				CL "... for "+STR$(ABS(PCt)*PRchg),0
				x=ABS(PCt)/DnRmdr
				IF x=INT(x) AND x>0 THEN
					DSMSCt=DSMSCt+1:IF DSMSCt>99 THEN DSMSCt=99
					GL GSMSend(AMbl$,"I/Net remains DOWN, please check")
				END IF
			END IF
		END IF

'convenient place
		GetTemp
		SetClock
		CL "Enclosure Temp " + str$(Temp)+"C",0

	END IF

	IF GSMPollTMR<=0 THEN
		GSMPollTMR=GSMTRchg
		A$="AT+CREG?"
		A$=M590Exec$(A$,2,0,"+CREG: 0,1")
		IF A$="T/O" OR A$="ERROR" THEN
			FlagRes GSMReg
			GSMf=GSMf+1:IF GSMf>9 THEN GSMf=9
			CL A$,0
			CL "GSM not registered",0
		ELSE
			FlagSet GSMReg
			GSMf=0
			CL A$,0
			CL "GSM registration OK",0
		END IF

		FlushIO(GSM)

'check recieved messages
		A$="AT+CMGL=4"
		CL A$,0
		PRINT#GSM,A$
		PAUSE 500

		DO
			A$=GetIO$(GSM,2)
			IF A$<>"" then CL A$,0
			IF A$="OK" THEN EXIT DO

			IF INSTR(A$,"+CMGL:") THEN 'envelope
				B$=GetIO$(GSM,2) 'message
				CL B$,0
				A$=Replace$(A$,CHR$(34),"")
				z=Split(A$,",")
				'    1       2          3       4    5       6
				'+CMGL: 1,REC UNREAD,+447777777,,16/10/07,22:11:35+04

				IF SP$(2)="REC UNREAD" THEN
					IF RIGHT$(RTRim$(SP$(3)),13)=AMbl$ THEN
						SELECT CASE UCASE$(B$)
							CASE "AFGHANISTANBANANASTAND" ' magic message from admin mobile to activate the relay
								FlagRes SlaveSKT
								PIN(RL1)=0:RL1TMR=10
								A$="Slave socket power OFF"
								CL A$,0
								GL GSMSend(AMbl$,A$)
							CASE "PING"
								GL GSMSend(AMbl$,"OK FLAGS="+RIGHT$(BIN$(FLAGS,8),8)+" "+STR$(Temp)+"C")
						END SELECT
					END IF
				END IF

			END IF
		LOOP

		A$="AT+CMGD=1,1"
		A$=M590Exec$(A$,2,0,"OK")
		FlushIO(GSM)
	END IF

'Console input
	A$=INKEY$
	DO WHILE A$<>""
		SELECT CASE ASC(A$)
			CASE 13
				IF CBuf$<>"" THEN CBuf$=UCASE$(CBuf$):FlagSet ConRFG
			CASE 0 TO 31
			CASE 127 TO 255
			CASE ELSE
				IF LEN(CBuf$)<255 THEN 
					CBuf$=CBuf$+A$
				ELSE
					CL "Console buffer over-run",0:CBuf$="":FlagRes ConRFG
				END IF
		END SELECT
		A$=INKEY$
	LOOP

	IF FlagTest(ConRFG)=1 THEN 'parse the commands
		FlagRes ConPFG
		args=Split(Trim$(CBuf$)," ")
		SELECT CASE SP$(1) ' individual verbs

			CASE "HELP"
				CL SP$(1),0
				CL "Console commands:",0
				CL "  HELP   =This list.",0
				CL "  DEBUG  =Show internal flags etc.",0
				CL "  HEARTBEAT hh:mm|-1  =Set/Disable daily Heartbeat SMS time (24H)",0
				CL "  ADMINPH +44<num>  =Set the Admin mobile No.",0
				CL "  NETWORK DHCP|STATIC [<ip> <mask> <gateway>] =Set the LAN IP Address",0
				CL "  PING <ip>  =Set the Internet test IP Address",0
				CL "",0
				CL "Current Settings:",0
				CL "  Admin Phone No.  "+AMbl$
				CL "  Heartbeat SMS    "+HBTm
				CL "  IP,Gateway,Mask  "+IP$+" "+GW$+" "+MS$,0
				CL "  MAC address      "+MAC,0
				CL "  Inet Ping IP     "+PA$,0
				CL "",0
				CL "  Enclosure Temp   " + str$(Temp)+"C",0
				CL "  Temp alarm at    " + str$(TmpAlm)+"C",0
				CL "",0
				CL "Additional material:",0
				CL "    Geoff Graham (MMBasic) and MicroMite.org (MicroMite)",0
				CL "",0

			CASE "DEBUG"
				CL SP$(1),0
				CL "FLAGS="+Bin$(FLAGS,32),0

			CASE "HEARTBEAT"
				IF args<2 THEN
					FlagSet ConPFG
				ELSE
					IF SP$(2)="-1" then HBTm=SP$(2):CL "Daily heartbeat disabled",0:GOTO HBTExit
					IF LEN(SP$(2))<>5 then
						FlagSet ConPFG
					ELSE
						z=VAL(LEFT$(SP$(2),2))
						IF z<0 OR z>23 THEN 'hours
							FlagSet ConPFG
						ELSE
							z=VAL(RIGHT$(SP$(2),2)) 'Mins
							IF z<0 OR z>59 THEN
								FlagSet ConPFG
							ELSE
								IF MID$(SP$(2),3,1)<>":" THEN 'seperator
									FlagSet ConPFG
								ELSE
									HBTm=SP$(2)
									CL "Daily heartbeat set : "+SP$(2),0
								END IF
							END IF
						END IF
					END IF
				END IF

HBTExit:
			CASE ELSE
				FlagSet ConPFG

		END SELECT

		IF FlagTest(ConPFG)=1 THEN
			CL "Malformed command: "+CBuf$,0
		END IF

		CBuf$="":FlagRes ConRFG
	END IF

'Web API parsing

	LANRxLN$=GetIO$(LAN,1)
	IF LANRxLN$="" OR LANRxLN$="T/O" THEN GOTO Main

	CL LANRxLN$,0
	IF MID$(LANRxLN$,2,8)=",CONNECT" THEN
		Chan=VAL(LANRxLN$)
		IF Chan>=0 AND Chan<=4 THEN
			FlagSet Conn+Chan
			CL "Inbound connect channel "+STR$(Chan),0
			GOTO IPEXIT
		END IF
	END IF

	IF MID$(LANRxLN$,2,7)=",CLOSED" THEN
		Chan=VAL(LANRxLN$)
		IF Chan>=0 AND Chan<=4 THEN
			IF FlagTest(Conn+Chan)=0 THEN A$="Abnormally " ELSE A$=""
			CL "Closed channel "+STR$(Chan),0
			FlagRes Conn+Chan
			GOTO IPEXIT
		END IF
	END IF

	IF LEFT$(LANRxLN$,5)="+IPD," THEN
		
		DO:a$=GetIO$(LAN,2):LOOP UNTIL a$="Connection: Keep-Alive"
		
		Chan=VAL(MID$(LANRxLN$,6,1))
		IF Chan>4 THEN GOTO IPDerrEXIT
		IF FlagTest(Conn+Chan)=0 THEN GOTO IPDerrEXIT 'channel not open
		
		z=INSTR(LANRxLN$,":GET /") '+IPD,0,nnn:GET /smssend?tel=077?msg=XYZ HTTP/1.1
		IF z=0 THEN GOTO IPDerrEXIT
		LANRxLN$=MID$(LANRxLN$,z+6)
		
		z=INSTR(LANRxLN$," HTTP")
		IF z=0 THEN GOTO IPDerrEXIT
		LANRxLN$=LEFT$(LANRxLN$,z-1)

		args=Split(Replace$(LANRxLN$,"+"," "),"?")

		SELECT CASE SP$(1)
			CASE "smssend"
				IF args <3 THEN GOTO IPDerrEXIT
				Tel$="":Msg$=""
				
				FOR n=2 TO args
					a$=Trim$(URLDecode$(MID$(sp$(n),5)))
					SELECT CASE LEFT$(ucase$(SP$(n)),4)
						CASE "TEL="
							Tel$=a$
						CASE "MSG="
							Msg$=a$
					END SELECT
				NEXT
				IF Tel$="" OR Msg$="" THEN GOTO IPDerrEXIT

				CL "O/B SMS "+Tel$+":"+Msg$,0
				z=GSMSend(Tel$,Msg$)
				IF z=0 THEN
					B$="OK: Message Queued for sending":GOTO IPOKEXIT
				ELSE
					B$="ERROR: Gateway failed to send message {"+STR$(z)+"}"
					GOTO IPOKEXIT
				END IF

			CASE "stat"
				'IF z=>8 AND Z<=11 THEN
				'other API calls go here...
				'z=INSTR(LANRxLN$,":GET /whatever?")
				'IF z<>0 THEN
				'etc...

				'	B$="OK: STAT="+LL1$+LL2$
				'	GOTO IPOKEXIT

		END SELECT

		GOTO IPDerrEXIT 'default action IF we don't find an API call

	END IF

	GOTO IPEXIT

IPDerrEXIT:
	B$="ERROR: Malformed request {"+LANRxLN$+"}"

IPOKEXIT:
	PRINT #LAN,"AT+CIPSENDEX="+STR$(Chan)+",255"
	PAUSE 100

	PRINT#LAN,B$+"\0"
	PAUSE 100

	PRINT#LAN,"AT+CIPCLOSE="+STR$(Chan)
	FlagRes Conn+Chan

IPEXIT:
	GOTO Main

'------------------ ESP8266 WiFi Modem SUBsys -------------
	SUB ESP8266Reset
		LOCAL STRING A$
		LOCAL INTEGER N
		FlagRes LANUp
		PULSE LANRST,10
		PAUSE 400

		LANExec "ATE0",2,0
		LANExec "AT+CWAUTOCONN=0",2,0
		LANExec "AT+CWMODE=1",2,0
		LANExec "AT+CWQAP",4,0
		LANExec "AT+RFVDD",2,0
		LANExec "AT+CWJAP="+q+AP$+q+","+q+PW$+q,10,1
		LANExec "AT+CIPSTA="+q+IP$+q+","+q+GW$+q+","+q+MS$+q,2,0
		LANExec "AT+CIFSR",2,0
		LANExec "AT+CWAUTOCONN=1",2,0
		LANExec "AT+CIPMUX=1",2,0
		LANExec "AT+CIPSERVER=1,19090",2,0

		LANTxLN$="":LANRxLN$=""

		FlagSet LANUp
	END SUB

	SUB LANExec(CM$,T,Suppress)
		LOCAL STRING A$
		A$=ESP8266Exec$(CM$,T,Suppress)
		IF Suppress<2 THEN CL A$,0
	END SUB

	FUNCTION ESP8266Exec$(Com$,TimeOut,Obf)
		LOCAL STRING A$ LENGTH 1,LN$
		LOCAL INTEGER C

		CL Com$,obf
		PRINT #LAN,Com$

		DO
			LN$=GetIO$(LAN,TimeOut)
			' get special values

			IF INSTR(LN$,"+CIFSR:STAMAC,")<>0 THEN
				MAC=MID$(LN$,16)
				MAC=LEFT$(MAC,INSTR(MAC,CHR$(34))-1)
			END IF

			IF INSTR(LN$,"WIFI DISCONNECT") THEN FlagRes(LANUp)
			IF INSTR(LN$,"WIFI CONNECTED") THEN FlagSet(LANUp)

			IF LN$="T/O" THEN ESP8266Exec$="T/O":FlagRes LANUp:EXIT DO
			IF LN$<>"" THEN
				IF Obf<2 THEN CL "["+LN$+"]",0
				IF LN$="OK" OR LN$=">" OR LN$="SEND OK" THEN ESP8266Exec$=LN$:EXIT DO
				IF INSTR(LN$,"ERROR") THEN ESP8266Exec$="ERROR ["+LNBuf$+"]":EXIT DO
			END IF
		LOOP

	END FUNCTION

'------------------ M590 GSM Modem SUBsys -----------------------
	SUB M590Reset
		LOCAL STRING A$
		LOCAL INTEGER N

		PIN(GSMRST)=0 'can't use pulse because it should already be lo
		PAUSE 500 'BOOT is active LO and at least 300mS
		PIN(GSMRST)=1

		ClearTOTimer
		DO
			IF FlagTest(TOFg) THEN GOTO GSMFail
			A$=GetIO$(GSM,5)
			CL A$,0
		LOOP UNTIL A$="MODEM:STARTUP"

		ClearTOTimer
		DO
			IF FlagTest(TOFg) THEN GOTO GSMFail
			A$=GetIO$(GSM,120)
			CL A$,0
		LOOP UNTIL A$="+PBREADY"
		FlushIO(GSM)

		GSMExec "ATE0",2,0,"OK" '(TURN OFF LOCAL ECHO)
		FlushIO(GSM)

		GSMExec "AT+CPAS",2,0,"+CPAS: 0"
		IF FlagTest(TOFg) THEN GOTO GSMFail

		DO
			PRINT #GSM,"AT+CREG?"
			DO:A$=GetIO$(GSM,2):LOOP WHILE LEN(A$)=0
			CL A$,0
			IF FlagTest(TOFg) THEN GOTO CREGWait
			IF INSTR(A$,"+CREG:") THEN
				IF INSTR(A$,"+CREG: 0,1")=0 THEN ' registered?
					GOTO CREGWait
				ELSE
					CL "Registered on GSM network",0
					GSMf=0
					EXIT DO
				END IF
			END IF
CREGWait:
			CL "Waiting for GSM network registration",0
			PAUSE 5000
			FlushIO(GSM)
			GSMf=1
		LOOP

		IF GSMf THEN GOTO GSMFail

		GSMf=0

		GSMExec "AT+ENPWRSAVE=0",2,0,"OK"
		IF FlagTest(TOFg) THEN GOTO GSMFail
		FlushIO(GSM)

		GSMExec "AT+COPS?",2,0,"OK"
		IF FlagTest(TOFg) THEN GOTO GSMFail
		FlushIO(GSM)

		GSMExec "AT+CSQ",2,0,"OK"
		IF FlagTest(TOFg) THEN GOTO GSMFail
		FlushIO(GSM)

		GSMExec "AT+XBANDSEL?",2,0,"OK"
		IF FlagTest(TOFg) THEN GOTO GSMFail
		FlushIO(GSM)

		GSMExec "AT+CCID?",2,0,"OK"
		IF FlagTest(TOFg) THEN GOTO GSMFail
		FlushIO(GSM)

		GSMExec "AT+CSCA?",2,0,"OK"
		IF FlagTest(TOFg) THEN GOTO GSMFail
		FlushIO(GSM)

		GSMExec "AT+CFUN?",2,0,"+CFUN: 1,0"
		IF FlagTest(TOFg) THEN GOTO GSMFail
		FlushIO(GSM)

		GSMExec "AT+CPIN?",2,0,"+CPIN: READY"
		IF FlagTest(TOFg) THEN GOTO GSMFail
		FlushIO(GSM)

		GSMExec "AT+CSMS?",2,0,"OK"
		IF FlagTest(TOFg) THEN GOTO GSMFail
		FlushIO(GSM)

		GSMExec "AT+CMGF=1",2,0,"OK"
		IF FlagTest(TOFg) THEN GOTO GSMFail
		FlushIO(GSM)

		GSMExec "AT+CSCS="+CHR$(34)+"GSM"+CHR$(34),2,0,"OK"
		IF FlagTest(TOFg) THEN GOTO GSMFail
		FlushIO(GSM)

		GSMExec "AT+CMGD=1,1",2,0,"OK"
		IF FlagTest(TOFg) THEN GOTO GSMFail
		FlushIO(GSM)

		EXIT SUB

GSMFail:
		FlagRes GSMUp
		GSMf=1
		CL "... Failed to start in "+STR$(TIMER-Chan)+"mS",0
	END SUB

	SUB GSMExec(CM$,T,obf,Resp$)
		LOCAL STRING A$
		A$=M590Exec$(CM$,T,obf,Resp$):CL A$,0
	END SUB

	FUNCTION M590Exec$(Com$,TimeOut,obf,Resp$)
		LOCAL STRING A$ LENGTH 1,LN$,LNBuf$
		LOCAL INTEGER C,n,z

		CL Com$,obf
		z=Split(Resp$,"|")
		PRINT #GSM,Com$
		StartTOTimer TimeOut

		DO
			IF FlagTest(TOFg) THEN M590Exec$="T/O ["+LNBuf$+"]":FlagRes GSMUp:EXIT DO
			IF LOC(#GSM)<>0 THEN
				A$=INPUT$(1,#GSM):C=ASC(A$)
				SELECT CASE C
					CASE 13
						IF LN$<>"" THEN
							LNBuf$=LNBuf$+LN$
							CL "["+LNBuf$+"]",0
							FOR n=1 TO z
								IF LN$=SP$(n) THEN M590Exec$=LN$:EXIT DO
							NEXT
							IF INSTR(LN$,"ERROR") THEN M590Exec$="ERROR ["+LNBuf$+"]":FlagRes GSMUp:EXIT DO
							LN$="":LNBuf$=""
						END IF
					CASE 0 TO 31
					CASE ELSE
						LN$=LN$+A$
				END SELECT
			END IF
		LOOP
		ClearTOTimer
		FlagSet GSMUp
	END FUNCTION

	FUNCTION GSMSend(t$,m$)
		LOCAL INTEGER n
		LOCAL STRING A$
		IF GSMf<>0 THEN GSMSend=10191:EXIT FUNCTION
		a$="AT+CMGS="+chr$(34)+t$+CHR$(34)
		PRINT#GSM,A$
		CL A$,0
		PAUSE 1000
		a$=LEFT$(m$,140)
		PRINT#GSM,A$+CHR$(26);
		CL a$,0
		PAUSE 100
		DO
			A$=GetIO$(GSM,15)
			CL A$,0
			IF INSTR(A$,"+CMGS:") THEN GSMSend=0:EXIT FUNCTION
			IF A$="T/O" THEN EXIT DO
		LOOP
		GSMSend=10156
	END FUNCTION

	SUB GL(z as integer)
		IF z=0 then CL "SMS Send OK",0 ELSE CL "SMS Send Fail ("+STR$(z)+")",0
	END SUB
'----------------------------------------------------

	SUB CL(Com$,obf)
		SELECT CASE Obf
			CASE 0
				PRINT DS$();Com$
			CASE 1
				PRINT DS$();STRING$(20,"#")
			CASE ELSE
		END SELECT
	END SUB

	FUNCTION GetIO$(io,TimeOut)
		LOCAL STRING a$,b$
		LOCAL INTEGER c
		StartTOTimer TimeOut
		DO
			IF FlagTest(TOFg) THEN GetIO$="T/O":EXIT DO
			IF LOC(#io)<>0 THEN
				A$=INPUT$(1,#io):C=ASC(A$)
				SELECT CASE C
					CASE 13
						GetIO$=b$:EXIT DO
					CASE 0 TO 31
					CASE ELSE
						IF LEN(b$)+LEN(a$)<255 THEN b$=b$+a$ ELSE b$=a$
				END SELECT
			END IF
		LOOP
		ClearTOTimer
	END FUNCTION

	SUB FlushIO(stream)
		LOCAL STRING A$
		DO WHILE LOC(#stream)<>0
			PAUSE 50:A$=INPUT$(LOC(#stream),#stream):PAUSE 50
		LOOP
	END SUB

	SUB CoreTMR
		PTmr=PTmr-1
		GSMPollTMR=GSMPollTMR-1
		RL1TMR=RL1TMR-1
		IF TC$="*" THEN TC$=" " ELSE TC$="*"
		BuildLCD
	END SUB

	SUB StartTOTimer(Timeout)
		FlagRes TOFg:SETTICK TimeOut*1000,SetTOFg,2
	END SUB

	SUB ClearTOTimer
		SETTICK 0,0,2:FlagRes TOFg
	END SUB

	SUB SetTOFg
		FlagSet TOFg
	END SUB

	SUB BuildLCD
		LL1$="L" + STR$(LANf) + "G" + STR$(GSMf) + "S" + STR$(PIN(16)) + "O" + STR$(OBSMSCt,4,0,"0") + "I" + STR$(IBSMSCt,4,0,"0")
		LL2$=LEFT$(LL2$,10) + "	 " + TC$ + STR$(DSMSCt,2,0,"0")
		'PRINT LL1$:PRINT LL2$:PRINT
	END SUB

	FUNCTION DS$()
		LOCAL a$,b$
		DO:a$=date$:b$=time$:LOOP WHILE a$<>date$ OR b$<>time$
		DS$=RIGHT$(a$,4)+MID$(a$,3,3)+"-"+LEFT$(a$,2)+" "+b$+" "
	END FUNCTION

	FUNCTION Replace$(a$,b$,c$)
		LOCAL INTEGER z
		LOCAL STRING x$,y$
		z=1
		DO
			z=INSTR(z,a$,b$)
			IF z=0 THEN EXIT DO
			x$=LEFT$(a$,z-1):y$=MID$(a$,z+LEN(b$)):a$=x$+c$+y$:z=LEN(x$)+LEN(c$)
		LOOP
		Replace$=a$
	END FUNCTION

	FUNCTION URLDecode$(a$)
		LOCAL INTEGER z
		LOCAL STRING b$,c$
		z=1
		DO
			z=INSTR(z,a$,"%")
			IF z=0 OR z=LEN(a$) THEN EXIT DO
			b$=MID$(a$,z,3):c$=CHR$(VAL("&h"+MID$(b$,2))):a$=Replace$(a$,b$,c$)
		LOOP
		URLDecode$=a$
	END FUNCTION

	FUNCTION Trim$(a$)
		Trim$=LTrim$(RTrim$(a$))
	END FUNCTION

	FUNCTION LTrim$(a$)
		LOCAL INTEGER m
		FOR m=1 TO LEN(a$)
			IF NOT(MID$(a$,m,1)=" " OR MID$(a$,m,1)=CHR$(9)) THEN LTrim$=MID$(a$,m):EXIT FUNCTION
		NEXT
		LTrim$=""
	END FUNCTION

	FUNCTION RTrim$(a$)
		LOCAL INTEGER n
		FOR n=LEN(a$) TO 1 STEP -1
			IF NOT(MID$(a$,n,1)=" " OR MID$(a$,n,1)=CHR$(9)) THEN RTrim$=LEFT$(a$,n):EXIT FUNCTION
		NEXT
		RTrim$=""
	END FUNCTION

	FUNCTION Split(a$,b$)
		LOCAL INTEGER z,n,m
		ON ERROR SKIP
		ERASE SP$
		z=1:n=0
		DO
			z=INSTR(z,a$,b$)
			IF z=0 THEN
				IF n=0 THEN
					DIM SP$(1):SP$(1)=a$:Split=1:EXIT FUNCTION
				ELSE
					EXIT DO
				END IF
			ELSE
				n=n+1:z=z+LEN(b$)
			END IF
		LOOP

		m=n+1:n=1
		DIM SP$(m)
		DO
			z=INSTR(1,a$,b$)
			IF z=0 THEN
				SP$(m)=a$:EXIT DO
			ELSE
				SP$(n)=LEFT$(a$,z-1):a$=MID$(a$,z+LEN(b$)):n=n+1
			END IF
		LOOP
		Split=m
	END FUNCTION

	SUB FlagSet(bit AS INTEGER)
		FLAGS=FLAGS OR (2^bit)
	END SUB

	SUB FlagRes(bit AS INTEGER)
		FLAGS=(FLAGS OR (2^bit)) XOR (2^bit)
	END SUB

	FUNCTION FlagTest(bit AS INTEGER) AS INTEGER
		FlagTest=ABS(SGN(FLAGS AND (2^bit)))
	END FUNCTION

	FUNCTION B64Dec$(x$)
		LOCAL INTEGER  w1,w2,w3,w4,n
		LOCAL STRING so$
		FOR n=1 TO LEN(x$) STEP 4
			w1=MD(MID$(x$,n,1)):w2=MD(MID$(x$,n+1,1)):w3=MD(MID$(x$,n+2,1)):w4=MD(MID$(x$,n+3,1))
			IF w2>=0 THEN so$=so$+CHR$(((w1*4+INT(w2/16)) AND 255))
			IF w3>=0 THEN so$=so$+CHR$(((w2*16+INT(w3/4)) AND 255))
			IF w4>=0 THEN so$=so$+CHR$(((w3*64+w4) AND 255))
		NEXT
		B64Dec$=so$
	END FUNCTION

	FUNCTION MD(x$)
		IF LEN(x$)=0 THEN MD=-1 ELSE MD=INSTR(Mime$,x$)-1
	END FUNCTION

	SUB SetClock
		RTC GETTIME
	END SUB

	SUB GetTemp
		RTC GetReg 17,Temp
		IF Temp>=TmpAlm THEN GL GSMSend(AMbl$,"Enclosure temperature alarm! Temp is " + str$(Temp)+"C Threshold is " + str$(TmpAlm)+"C")
	END SUB