Table of Contents
SMS (Text) Gateway with MicroMite
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...
...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