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:
- Full 2-way messaging convergence is the long-term target with this device. Pass inbound SMS to an API (web page on [remote] server) - will use the ESP8266 in client mode and is fairly straightforward, but will need to craft the HTTP chatter to/from the server. Those "to evict Tom from the big brother house, text TOM to ..." systems would be very easy to implement once this is done. The web page on the server would be responsible for the data processing once the SMS hase been off-loaded from the gateway.
- Some kind of validation (password/encryption).
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.
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.
Sending a text message from a web page...... received on the SMS Gateway API... Output from the console log
...and the same text received on my mobile phone.Here is a procession of recieved SMS messages. this shows a typical life cycle together with some power cycling.
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