Welcome Guest, you are in: Login

Fruit Of The Shed

Navigation (MMBasic)






Search the wiki

»


Page History: SMS (Text) Gateway with MicroMite

Compare Page Revisions



« Older Revision - Back to Page History - Current Revision


Page Revision: 2016/12/28 07:49


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 a specially crafted message from an "admin" mobile number. Upon receipt it activates a relay for 10 seconds, sending a message to the effect either side of the activation. 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 weeks 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 yet!

Full functionality list and software will follow, Circuit diagram is below - there are numerous connectors for modules cheaply available on eBay - the whole thing should cost you less than £20.

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 Secs=1000
    CONST Mins=Secs*60
    CONST Hrs=Mins*60

    CONST GSM=1,GSMRST=16
    CONST LAN=2,LANRST=2
    CONST RL1=15,RL1Time=10000
    CONST DailySMS="05:59" ' Set to "-1" to prevent the daily test SMS

    CONST PA=CHR$(34)+"8.8.8.8"+CHR$(34) ' the internet connectivity ping address, e.g. something close in your ISP
    CONST PingCTRRecharge=3
    CONST DownReminder=5

    CONST WDTime=60000

    CONST AdminPH$="+447xxxxyyyyyy" ' the "admin" mobile phone e.g. yours.
    CONST ContMsg="101561019110156BOUNCE" ' the control message to activate the slave relay... suitably cryptic for you.

    CONST PingTMRRecharge=180 'secs
    CONST GSMTMRRecharge=60 'Secs

    CONST IP=CHR$(34)+"192.168.0.14"+CHR$(34) ' the static address to assign to the LAN interface ...
    CONST GW=CHR$(34)+"192.168.0.1"+CHR$(34)  ' ... and it's gateway...
    CONST MS=CHR$(34)+"255.255.255.0"+CHR$(34)' ... and its netmask
    CONST AP=CHR$(34)+"XYZ"+CHR$(34)          ' your WiFi SSID (name of your wifi lan)...
    CONST PW=CHR$(34)+"######"+CHR$(34)       ' ... and its password - sorry this is plain text, later versions will hide this

'H/W Init
    WATCHDOG OFF
    PIN(LANRST)=1
    SETPIN LANRST,DOUT
    PIN(GSMRST)=0 'hold GSM in reset to prevent booting
    SETPIN GSMRST,DOUT,OC
    PIN(RL1)=1
    SETPIN RL1,DOUT,OC

'S/W Init
    DIM STRING A$,B$,D$
    DIM STRING LNBuf$
    DIM STRING LANRxLN$,LANTxLN$,TChr$ LENGTH 1,LCDLN1$ LENGTH 16,LCDLN2$ LENGTH 16
    DIM INTEGER h,m,C,Z,TOFg,Chan,Conn(4),PingTMR,PingCtr,RL1TMR,DailySMSFg
    DIM INTEGER LANf,GSMf,DistSMSCount,OBSMSCount,IBSMSCount
    DIM FLOAT x

    CLog "SMS Gateway V1.1",0

    SetClock

    IF MM.WATCHDOG then
        CLog "*** RESTART FORCED BY WATCHDOG ***",0
    END IF

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

    CLog "System coming up",0

'timer inits
    PingTMR=PingTMRRecharge
    GSMPollTMR=GSMTMRRecharge

    PingCtr=1
    RL1TMR=-1

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

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

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

    CLog "Boot took "+STR$(TIMER)+"mS",0
    CLog "System Running",0

    z=GSMSend(AdminPH$,"SMS Gateway has started")

Main:
    WATCHDOG=WDTime

    IF LEFT$(TIME$,5)=DailySMS THEN
        IF DailySMSFg=0 THEN
            z=GSMSend(AdminPH$,"SMS Gateway daily test. " + DATE$ + " " + TIME$)
            DailySMSFg=1
        END IF
    ELSE
        DailySMSFg=0
    END IF

    IF RL1TMR=0 then
        RL1TMR=-1
        A$="Slave socket power ON"
        PIN(RL1)=1
        CLog A$,0
        z=GSMSend(AdminPH$,A$)
        CLog "{response="+str$(z)+"}",0
    END IF


    IF PingTMR<=0 THEN
        PingTMR=PingTMRRecharge

        SetClock 'convenient place

        CLog "Internet connectivity...",0
        A$=ESP8266Exec$("AT+PING="+PA,4000,0)

        IF A$="OK" THEN
            CLog "... good",0
            LANf=0:DistSMSCount=0
            PingCtr=PingCTRRecharge
            IF PingCtr<=0 THEN
                'send "UP" sms
                CLog "*** back up message",0
                z=GSMSend(AdminPH$,"Internet UP")
            END IF
        ELSE
            LANf=LANf+1:IF LANf>9 THEN LANf=9
            CLog "Internet Down",0
            PingCtr=PingCtr-1
            IF PingCtr=0 THEN
                'send "DOWN" sms
                CLog "*** Internet down message",0
                z=GSMSend(AdminPH$,"Internet DOWN, please check")
            ELSE
                CLog "... for "+STR$(PingCtr),0
                x=ABS(PingCtr)/DownReminder
                IF x=INT(x) AND x>0 THEN 'whole ten minute periods
                    'send "STILL DOWN" sms
                    DistSMSCount=DistSMSCount+1:IF DistSMSCount>99 THEN DistSMSCount=99
                    CLog "*** Still down message",0
                    z=GSMSend(AdminPH$,"Internet remains DOWN, please check")
                END IF
            END IF
        END IF

    END IF


    IF GSMPollTMR<=0 THEN
        GSMPollTMR=GSMTMRRecharge
        CLog "GSM connectivity...",0
        A$="AT+CREG?"
        A$=M590Exec$(A$,2,0,"+CREG: 0,1|+CREG: 0,4")
        IF A$="T/O" OR A$="ERROR" THEN
            CLog A$,0
            GSMf=GSMf+1:IF GSMf>9 THEN GSMf=9
            CLog "GSM registration Down",0
        ELSE
            CLog A$,0
            CLog "... good",0
            GSMf=0
        END IF

        FlushIO(GSM)

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

        'messages look like this:
        '+CMGL: 1,"REC UNREAD","+447000123456","","16/10/07,22:11:35+04"
        'message body

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

            'IF A$="ERROR" THEN ' get extended info
            '   PRINT #GSM,"AT+CEER"
            '   PAUSE 500
            '   DO
            '       A$=GetIO$(GSM,2)
            '       CLog A$,0
            '   LOOP UNTIL A$="OK" or A$="T/O"
            '   EXIT DO
            'END IF

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

                IF SP$(2)="REC UNREAD" THEN
                    IF RTRim$(SP$(3))=AdminPH$ THEN
                        IF UCASE$(B$)=ContMsg THEN
                            A$="Slave socket power OFF"
                            CLog A$,0
                            PIN(RL1)=0
                            RL1TMR=10
                            z=GSMSend(AdminPH$,A$)
                            CLog "{response="+str$(z)+"}",0
                        END IF
                    END IF
                END IF

            END IF
        LOOP

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


    'Console INPUT - Parse any commands here


    'Web API parsing

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

    CLog LANRxLN$,0
    IF MID$(LANRxLN$,2,8)=",CONNECT" THEN  'new inbound web connection
        Chan=VAL(LANRxLN$)
        IF Chan>=0 AND Chan<=4 THEN
            Conn(Chan)=1
            CLog "Inbound connect channel "+STR$(Chan),0
            GOTO IPEXIT
        END IF
    END IF

    IF MID$(LANRxLN$,2,7)=",CLOSED" THEN ' web connection closed
        Chan=VAL(LANRxLN$)
        IF Chan>=0 AND Chan<=4 THEN
            IF Conn(Chan)=0 THEN A$="Abnormally " ELSE A$=""
            CLog "Closed channel "+STR$(Chan),0
            Conn(Chan)=0
            GOTO IPEXIT
        END IF
    END IF

    IF LEFT$(LANRxLN$,5)="+IPD," THEN ' web connection payload
        Chan=VAL(MID$(LANRxLN$,6,1))
        IF Chan>4 THEN GOTO IPDerrEXIT
        IF Conn(Chan)<>1 THEN GOTO IPDerrEXIT 'channel not open

        z=INSTR(LANRxLN$,":GET /smssend?") '+IPD,0,nnn:GET /smssend?tel=?msg= HTTP/1.1

        do:a$=GetIO$(LAN,2):loop until a$="Connection: Keep-Alive" ' empty the rest of the envelope

        IF z=>8 AND Z<=11 THEN
            b$=MID$(LANRxLN$,z+13)
            z=INSTR(b$," HTTP/")
            IF z<21 THEN GOTO IPDerrEXIT
            b$=LEFT$(b$,z-1) 'remove the HTTP bit
            b$=Replace$(b$,"+"," ")  ' +   to spaces
            args=Split(b$,"?")
            IF args <3 THEN GOTO IPDerrEXIT
            Tel$="":Msg$=""
            FOR n=2 TO args 'args=1 is a dummy
                SELECT CASE LEFT$(ucase$(SP$(n)),4)
                    CASE "TEL="
                        Tel$=LTrim$(RTrim$(MID$(sp$(n),5)))
                    CASE "MSG="
                        Msg$=LTrim$(RTrim$(MID$(sp$(n),5)))
                END SELECT
            NEXT
            IF Tel$="" OR Msg$="" THEN GOTO IPDerrEXIT
            ERASE SP$
            Msg$=URLDecode$(Msg$)

            CLog "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

			' other web API commands parsd here
            'z=INSTR(LANRxLN$,":GET /stat ") '+IPD,0,nnn:GET /stat HTTP/1.1
            '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="+LCDLN1$+LCDLN2$
            '   GOTO IPOKEXIT

        END IF

        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)
    Conn(Chan)=0

IPEXIT:

    GOTO Main

    END


'------------------ ESP8266 WiFi Modem SUBsys -------------

    SUB ESP8266Reset
        LOCAL STRING A$
        LOCAL INTEGER N

        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="+AP+","+PW,10,1
        LANExec "AT+CIPSTA="+IP+","+GW+","+MS,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$=""

        FOR N=0 TO 4:Conn(N)=0:NEXT

    END SUB

    SUB LANExec(CM$,T,Suppress) 'temp - will go when we have better handling of the startup
        LOCAL STRING A$
        A$=ESP8266Exec$(CM$,T,Suppress)
        IF Suppress<2 THEN CLog A$,0
    END SUB

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

        CLog Com$,obf
        PRINT #LAN,Com$

        DO
            LN$=GetIO$(LAN,TimeOut)
            IF LN$="T/O" THEN ESP8266Exec$="T/O":EXIT DO
            IF LN$<>"" THEN
                IF Obf<2 THEN CLog "["+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 TOFg THEN GOTO GSMFail
            A$=GetIO$(GSM,5)
            CLog A$,0
        LOOP UNTIL A$="MODEM:STARTUP"

        'ClearTOTimer
        'PRINT#GSM,"AT+ENPWRSAVE=0" 'solves slow +PBREADY ?

        ClearTOTimer
        DO
            IF TOFg THEN GOTO GSMFail
            A$=GetIO$(GSM,120)
            CLog 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 TOFg THEN GOTO GSMFail

        DO ' forever
            'FlushIO(GSM)
            'GSMExec "AT+CREG?",2,0,"+CREG: 0,1" '(MODULE IS REGISTERED ON THE NETWORK)
            PRINT #GSM,"AT+CREG?"
            DO:A$=GetIO$(GSM,2):LOOP WHILE LEN(A$)=0
            CLog A$,0
            IF TOFg THEN GOTO CREGWait
            IF INSTR(A$,"+CREG:") THEN
                IF INSTR(A$,"+CREG: 0,1")=0 THEN
                    GOTO CREGWait
                ELSE
                    CLog "Registered on GSM network",0
                    GSMf=0
                    EXIT DO
                END IF
            END IF
CREGWait:
            CLog "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 TOFg THEN GOTO GSMFail
        FlushIO(GSM)

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

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

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

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

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

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

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

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

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

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

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

        EXIT SUB

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

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

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

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

        DO
            IF TOFg=1 THEN M590Exec$="T/O ["+LNBuf$+"]":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$
                            CLog "["+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$+"]":EXIT DO
                            LN$="":LNBuf$=""
                        END IF
                    CASE 0 TO 31
                    CASE ELSE
                        LN$=LN$+A$
                END SELECT
            END IF
        LOOP
        ClearTOTimer
    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$
        CLog A$,0
        PAUSE 1000
        a$=LEFT$(m$,140)
        PRINT#GSM,A$+CHR$(26);
        CLog a$,0
        PAUSE 100
        DO
            A$=GetIO$(GSM,15)
            CLog A$,0
            IF INSTR(A$,"+CMGS:") THEN GSMSend=0:EXIT FUNCTION
            IF A$="T/O" THEN EXIT DO
        LOOP
        GSMSend=10156
    END FUNCTION
'----------------------------------------------------

    SUB CLog(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 TOFg=1 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
        PingTMR=PingTMR-1
        GSMPollTMR=GSMPollTMR-1
        RL1TMR=RL1TMR-1
        IF TChr$="*" THEN TChr$=" " ELSE TChr$="*"
        BuildLCD
    END SUB

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

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

    SUB SetTOFg
        TOFg=1
    END SUB

    SUB SetClock
        RTC GETTIME
    END SUB


    SUB BuildLCD
        LCDLN1$="L" + STR$(LANf) + "G" + STR$(GSMf) + "S" + STR$(PIN(16)) + "O" + STR$(OBSMSCount,4,0,"0") + "I" + STR$(IBSMSCount,4,0,"0")
        LCDLN2$=LEFT$(LCDLN2$,10) + "    " + TChr$ + STR$(DistSMSCount,2,0,"0")
        'PRINT LCDLN1$:PRINT LCDLN2$: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 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