/*
 * REVISION HISTORY
 * 
 * 3.02
 * Enabled SMS to be sent to MASTER USER when power is restored to unit.
 * 
 * 3.01
 * Minor bug fixes
 * 
 * 300f
 * Disabled Boot Time SMS
 * 
 * SIM800 modules have diferent default settings. Forced the following:
 *   Cancel Call Forwarding
 *   Cancel Call Waiting
 *   Enable List of Current Calls
 *   Disable Caller Line ID
 * 
 * 300e
 * Multiple input alarm did not send SMS correct
 * Corrected composing of SMS message
 * 
 * 300d
 * 
 * Removing WaitOK() when sending SMSs caused problems sending multiple SMS
 * Inhibit WDT during sending of multiple SMSs
 * 
 * 300c
 * Increased available SRAM from 936 to 1436 bytes :) :)
 * This is a saving of 500 bytes
 * 
 * 300b
 * Implemented WDT function
 * 
 * 300a
 * Code clean-up
 * Removed WaitOk() during sending of SMSs
 * 
 * 300
 * Correct calculations of input & output timers
 * 
 */


//========================================================================================
//========================================================================================
//
// ! ! ! NOTE ! ! !
//       Buffer in software serial library to be changed to at least 200
//
//========================================================================================
//========================================================================================
//
//
// EEPROM STRING/TEXT MEMORY LOCATIONS
// -----------------------------------
//  1 - Master number
//  2 - Master name
//  3 - USSD number
//  4 - USSD text
//  5 - Spare
//  6 - Spare
//  7 - Input  1 Name
//  8 - Input  1 ON text
//  9 - Input  1 OFF text
// 10 - Input  2 Name
// 11 - Input  2 ON text
// 12 - Input  2 OFF text
// 13 - Input  3 Name
// 14 - Input  3 ON text
// 15 - Input  3 OFF text
// 16 - Input  4 Name
// 17 - Input  4 ON text
// 18 - Input  4 OFF text
// 19 - Output 1 Name
// 20 - Output 1 ON text
// 21 - Output 1 OFF text
// 22 - Output 2 Name
// 23 - Output 2 ON text
// 24 - Output 2 OFF text
// 25 - Output 3 Name
// 26 - Output 3 ON text
// 27 - Output 3 OFF text
// 28 - Output 4 Name
// 29 - Output 4 ON text
// 30 - Output 4 OFF text
// 31 - Input triggers ON
// 32 - Input triggers OFF
// 33 - Status update tel nr for User 1
// 34 - Status update input channels for User 1
// 35 - Status update tel nr for User 2
// 36 - Status update input channels for User 2
// 37 - Status update tel nr for User 3
// 38 - Status update input channels for User 3
// 39 - Status update tel nr for User 4
// 40 - Status update input channels for User 4
// 41 - Status update tel nr for User 5
// 42 - Status update input channels for User 5
// 43 - Pulsed   output channels
// 44 - Timed    output channels
// 45 - Latching output channels 
// 46 - Spare
// END OF STRING/TEXT MEMORY - NO MORE EEPROM AVAILABLE
//
// EEPROM INTEGER MEMORY LOCATIONS
//--------------------------------
//  1 - Input timer 1
//  2 - Input timer 2
//  3 - Input timer 3
//  4 - Input timer 4
//  5 - Output timer 1
//  6 - Output timer 2
//  7 - Output timer 3
//  8 - Output timer 4
//  9 - Pulse Timer
// 10 - Spare
// 11 - Spare
// 12 - Spare
// 13 - Spare
// 14 - Spare
// 15 - Spare
// END OF INTEGER MEMORY - NO MORE EEPROM AVAILABLE
//
//========================================================================================

#include <EEPROM.h>
#include <SoftwareSerial.h>
#include <avr/wdt.h>

// Define I/O pins
//-----------------------------------------------
#define SIM800_Rx      3  //SIM800 Serial port Rx
#define SIM800_Tx      4  //SIM800 serial port Tx
#define SIM800_Reset   2  //SIM800 Reset pin

#define Output1       A4  // digital output 
#define Output2       A5  // digital output
#define Output3       A3  // digital output
#define Output4       A2  // digital output
#define Input1         5  // digital input
#define Input2         6  // digital input
#define Input3         7  // digital input
#define Input4         8  // digital input
#define LedG           9  // Green LED
#define LedR          10  // Red LED

// Define system commands
//-----------------------------------------------
#define Req_Master    F("MASTER")             // add MASTER NAME
#define Req_ClearAll  F("CLEARALL")           // Perform factory reset
#define Req_ClearSIM  F("CLEARSIM")           // Remove all numbers from SIM card
#define Req_Add       F("Add,")               // add name/nr to SIM card
#define Req_Del       F("Del,")               // delete name/nr from SIM card
#define Req_Balance   F("$$$$")               // balance enquiry request
#define Req_USSD      F("SETUSSD,")           // Save USSD balance request nr
#define Req_PulseCh   F("PULSE,")             // Save channels that must pulse
#define Req_PulseTime F("PULSETIME,")         // pulse duration in ms (500 - 30000)
#define Req_InText    F("INTEXT,")            // Input text
#define Req_OutText   F("OUTTEXT,")           // Output text
#define Req_In_On     F("INPUTON,")           // Alarm triggers rising edge
#define Req_In_Off    F("INPUTOFF,")          // Alarm triggers falling edge
#define Req_In_Time   F("INTIME,")            // Input timer value, integer
#define Req_Update    F("????")               // update request 
#define Req_Reset     F("RESET")              // Save channels that must pulse
#define Req_OutMode   F("OUTMODE,")           // Set output channel modes

// Create an instance of software serial port
//-----------------------------------------------
SoftwareSerial SSerial(SIM800_Tx, SIM800_Rx); // RX, TX

// Define program variables
//-----------------------------------------------
#define BootTime       600000                  // Boot timer in ms

boolean       Connected = false;
boolean       Boot      = true;
String        RxString  = "";
char          RxChar    = ' ';
int           Counter   = 0;
String        GSM_Nr    = "100000000000000";
String        GSM_Name  = "200000000000000";
String        GSM_Msg   = "300000000000000";
String        Master_Nr = "400000000000000";
String        USSD_Nr   = "500000000000000";
String        USSD_Name = "600000000000000";

String        Text_Name = "1222222222";
String        Text_On   = "5222222222";
String        Text_Off  = "9222222222";

boolean       On1       = false;
boolean       On2       = false;
boolean       On3       = false;
boolean       On4       = false;
int           Pulse_ms  = 1000;

boolean       Alm1_On   = false;
boolean       Alm1_Off  = false;
boolean       Alm2_On   = false;
boolean       Alm2_Off  = false;
boolean       Alm3_On   = false;
boolean       Alm3_Off  = false;
boolean       Alm4_On   = false;
boolean       Alm4_Off  = false;

boolean       In1_Old   = false;
boolean       In2_Old   = false;
boolean       In3_Old   = false;
boolean       In4_Old   = false;

unsigned long InT1_ms   = 0;
unsigned long InT2_ms   = 0;
unsigned long InT3_ms   = 0;
unsigned long InT4_ms   = 0;
boolean       InT1_Run  = false;
boolean       InT2_Run  = false;
boolean       InT3_Run  = false;
boolean       InT4_Run  = false;
int           InT1      = 1;
int           InT2      = 1;
int           InT3      = 1;
int           InT4      = 1;

boolean       Pulse1    = false;
boolean       Pulse2    = false;
boolean       Pulse3    = false;
boolean       Pulse4    = false;
boolean       Time1     = false;
boolean       Time2     = false;
boolean       Time3     = false;
boolean       Time4     = false;
boolean       Latch1    = false;
boolean       Latch2    = false;
boolean       Latch3    = false;
boolean       Latch4    = false;
boolean       OutT1_Run = false;
boolean       OutT2_Run = false;
boolean       OutT3_Run = false;
boolean       OutT4_Run = false;
unsigned long OutT1_ms  = 0;
unsigned long OutT2_ms  = 0;
unsigned long OutT3_ms  = 0;
unsigned long OutT4_ms  = 0;
unsigned int  OutT1     = 1;
unsigned int  OutT2     = 1;
unsigned int  OutT3     = 1;
unsigned int  OutT4     = 1;

unsigned long CalcTime  = 0;

boolean       Bal_Check = false;
unsigned long Bal_ms    = 0;
unsigned int  Bal_Limit = 30;   // low balance value

//###############################################################################################
// SETUP
// 
//###############################################################################################
void setup() {


  // Disable WDT during start-up
  //--------------------------
  cli();                           // disable interrupts for changing the registers
  MCUSR = 0;                       // reset status register flags
                                   // Put timer in interrupt-only mode:                                       
  WDTCSR |= 0b00011000;            // Set WDCE (5th from left) and WDE (4th from left) to enter config mode,
                                   // using bitwise OR assignment (leaves other bits unchanged).
  WDTCSR =  0b01000000 | 0b100001; // set WDIE: interrupt enabled
                                   // clr WDE: reset disabled
                                   // and set delay interval (right side of bar) to 8 seconds
  sei();                           // enable interrupts again
  wdt_disable();
  
  // Setup I/Os
  //-----------
  pinMode(Input1,INPUT_PULLUP);
  pinMode(Input2,INPUT_PULLUP);
  pinMode(Input3,INPUT_PULLUP);
  pinMode(Input4,INPUT_PULLUP);
  pinMode(Output1,OUTPUT);
  pinMode(Output2,OUTPUT);
  pinMode(Output3,OUTPUT);
  pinMode(Output4,OUTPUT);
  pinMode(LedR,OUTPUT);
  pinMode(LedG,OUTPUT);
  
  digitalWrite(Output1,LOW);
  digitalWrite(Output2,LOW);
  digitalWrite(Output3,LOW);
  digitalWrite(Output4,LOW);
  digitalWrite(LedR,LOW);
  digitalWrite(LedG,LOW);

  // Start serial ports
  //-------------------
  SSerial.begin(9600);

  // Read settings from EEPROM
  //--------------------------
  ReadSettings();

  // Initialise SIM800
  //------------------
  InitGSM();

  // Set up WDT for 8 seconds
  //--------------------------
  wdt_reset();
  wdt_enable(WDTO_8S);

  // Read inputs for reference
  //--------------------------
  In1_Old = !digitalRead(Input1);
  In2_Old = !digitalRead(Input2);
  In3_Old = !digitalRead(Input3);
  In4_Old = !digitalRead(Input4);

}


//###############################################################################################
// MAIN LOOP
// 
//###############################################################################################
void loop() {

  // Reset WDT every pass
  //--------------------------
  wdt_reset();

  //See if unit was restarted, allow 10 minute for special functions
  //-----------------------------------------------
  if (Boot) {
    if (millis() > BootTime) {
       Boot = false;
      // Flash LED to indicate boot up time passed
      //-----------------------------------------------
      SSerial.write("AT+SLEDS=2,64,3000\r\n");
      WaitOK();
    }
  }
  
  // scan for data from software serial port
  //-----------------------------------------------
  RxString = "";
  Counter = 0;
  while(SSerial.available()){
    delay(1);  // short delay to give time for new data to be placed in buffer
    // get new character
    RxChar = char(SSerial.read());
    //add first 200 character to string
    if (Counter < 200) {
      RxString.concat(RxChar);
      Counter = Counter + 1;
    }
  }
  
  // Is there a new incoming call? 
  //-----------------------------------------------
  if (Received(F("RING")) ) GetCall();
  
  // Is there a new USSD Message?
  //-----------------------------------------------
  if (Received(F("CUSD:")) ) GetUSSD();

  // Is there a new SMS?
  //-----------------------------------------------
  if (Received(F("CMT:")) ) GetSMS();
  
  // Scan inputs
  //-----------------------------------------------
  ScanInputs();

  // Scan output timer
  //-----------------------------------------------
  ScanOutputTimers();
  
  // Daily balance check
  //-----------------------------------------------
  BalanceCheck();  
}

//###############################################################################################
// Daily balance check
// 
//###############################################################################################
void BalanceCheck() {

  if ( (millis() - Bal_ms) > 86400000 ) {
    Bal_ms = millis();
    
    //ShowMasterOk();
    SSerial.print(F("ATD"));
    SSerial.print(USSD_Nr);
    SSerial.print(F("\n\r"));
    Bal_Check = true;
  }
}


//###############################################################################################
// Scan Inputs
// 
//###############################################################################################
void ScanInputs() {
  boolean LH1      = false;
  boolean LH2      = false;
  boolean LH3      = false;
  boolean LH4      = false;
  boolean HL1      = false;
  boolean HL2      = false;
  boolean HL3      = false;
  boolean HL4      = false;
  boolean IOChange = false;
  
  // Read inputs
  //-------------
  boolean In1 = !digitalRead(Input1);
  boolean In2 = !digitalRead(Input2);
  boolean In3 = !digitalRead(Input3);
  boolean In4 = !digitalRead(Input4);

    // Scan and start timer 1
    //-----------------------
    if (In1 != In1_Old) {
      if (!InT1_Run) {
        InT1_ms = millis();
        InT1_Run = true;
      }
    }
    else {
      if (InT1_Run) {
        InT1_Run = false;
      }
    }
    // Scan and start timer 2
    //-----------------------
    if (In2 != In2_Old) {
      if (!InT2_Run) {
        InT2_ms = millis();
        InT2_Run = true;
      }
    }
    else {
      if (InT2_Run) {
        InT2_Run = false;
      }
    }
    // Scan and start timer 3
    //-----------------------
    if (In3 != In3_Old) {
      if (!InT3_Run) {
        InT3_ms = millis();
        InT3_Run = true;
      }
    }
    else {
      if (InT3_Run) {
        InT3_Run = false;
      }
    }
    // Scan and start timer 4
    //-----------------------
    if (In4 != In4_Old) {
      if (!InT4_Run) {
        InT4_ms = millis();
        InT4_Run = true;
      }
    }
    else {
      if (InT4_Run) {
        InT4_Run = false;
      }
    }
 
    // See if timer 1 reached end
    //----------------------------
    CalcTime = InT1;
    CalcTime = CalcTime * 1000;
    if ( (millis() - InT1_ms) > CalcTime ) {
      if (InT1_Run) {
        InT1_Run = false;
        // Change on input 1 
        //------------------
        if ( (In1 == 1) and (In1_Old == 0) ) {
          // Rising edge detected
          if (Alm1_On == true) {
            // must react to LH
            LH1 = true;
            IOChange = true;        
          }
        }
        if ( (In1 == 0) and (In1_Old == 1) ) {
          // Falling edge detected
          if (Alm1_Off == true) {
            // must react to HL
            HL1 = true;
            IOChange = true;        
          }
        }
        In1_Old = In1;
      }
    }
   
    // See if timer 2 reached end
    //----------------------------
    CalcTime = InT2;
    CalcTime = CalcTime * 1000;
    if ( (millis() - InT2_ms) > CalcTime ) {
      if (InT2_Run) {
        InT2_Run = false;
        // Change on input 2
        //------------------
        if ( (In2 == 1) and (In2_Old == 0) ) {
          // Rising edge detected
          if (Alm2_On == true) {
            // must react to LH
            LH2 = true;
            IOChange = true;        
          }
        }
        if ( (In2 == 0) and (In2_Old == 1) ) {
          // Falling edge detected
          if (Alm2_Off == true) {
            // must react to HL
            HL2 = true;
            IOChange = true;        
          }
        }
        In2_Old = In2;
      }
    }
    // See if timer 3 reached end
    //----------------------------
    CalcTime = InT3;
    CalcTime = CalcTime * 1000;
    if ( (millis() - InT3_ms) > CalcTime ) {
      if (InT3_Run) {
        InT3_Run = false;
        // Change on input 3
        //------------------
        if ( (In3 == 1) and (In3_Old == 0) ) {
          // Rising edge detected
          if (Alm3_On == true) {
            // must react to LH
            LH3 = true;
            IOChange = true;        
          }
        }
        if ( (In3 == 0) and (In3_Old == 1) ) {
          // Falling edge detected
          if (Alm3_Off == true) {
            // must react to HL
            HL3 = true;
            IOChange = true;        
          }
        }
        In3_Old = In3;
      }
    }
    // See if timer 4 reached end
    //----------------------------
    CalcTime = InT4;
    CalcTime = CalcTime * 1000;
    if ( (millis() - InT4_ms) > CalcTime ) {
      if (InT4_Run) {
        InT4_Run = false;
        // Change on input 4
        //------------------
        if ( (In4 == 1) and (In4_Old == 0) ) {
          // Rising edge detected
          if (Alm4_On == true) {
            // must react to LH
            LH4 = true;
            IOChange = true;        
          }
        }
        if ( (In4 == 0) and (In4_Old == 1) ) {
          // Falling edge detected
          if (Alm4_Off == true) {
            // must react to HL
            HL4 = true;
            IOChange = true;        
          }
        }
        In4_Old = In4;
      }
    }

  // Return if no change on IO found
  //-----------------------------------
    if (!IOChange) return;
    
  // Compose SMS for phone entry 1 - 5
  //-----------------------------------
  for (byte i = 1; i < 6;i++) {
    
    if (i == 1) RxString = ReadText(34);
    if (i == 2) RxString = ReadText(36);
    if (i == 3) RxString = ReadText(38);
    if (i == 4) RxString = ReadText(40);
    if (i == 5) RxString = ReadText(42);
    
    boolean Do1      = false;
    boolean Do2      = false;
    boolean Do3      = false;
    boolean Do4      = false;

    if ( (Received("1")) and ( (LH1 == true) or (HL1 == true) ) ) Do1 = true;
    if ( (Received("2")) and ( (LH2 == true) or (HL2 == true) ) ) Do2 = true;
    if ( (Received("3")) and ( (LH3 == true) or (HL3 == true) ) ) Do3 = true;
    if ( (Received("4")) and ( (LH4 == true) or (HL4 == true) ) ) Do4 = true;

    // See if SMS must be sent
    //-------------------------
    if ( (Do1 == true) or (Do2 == true) or (Do3 == true) or (Do4 == true) ) {

      // Get SMS number SMS
      if (i == 1) GSM_Nr = ReadString(33);
      if (i == 2) GSM_Nr = ReadString(35);
      if (i == 3) GSM_Nr = ReadString(37);
      if (i == 4) GSM_Nr = ReadString(39);
      if (i == 5) GSM_Nr = ReadString(41);

      RxString = "";
      if (Do1 == true) {
        RxString = ReadText(7);
        RxString = RxString + " ";
        if (LH1 == true) RxString = RxString + ReadText(8);
        if (HL1 == true) RxString = RxString + ReadText(9);
        RxString = RxString + "\n";
      }
      if (Do2 == true) {
        RxString = RxString + ReadText(10);
        RxString = RxString + " ";
        if (LH2 == true) RxString = RxString + ReadText(11);
        if (HL2 == true) RxString = RxString + ReadText(12);
        RxString = RxString + "\n";
      }
      if (Do3 == true) {
        RxString = RxString + ReadText(13);
        RxString = RxString + " ";
        if (LH3 == true) RxString = RxString + ReadText(14);
        if (HL3 == true) RxString = RxString + ReadText(15);
        RxString = RxString + "\n";
      }
      if (Do4 == true) {
        RxString = RxString + ReadText(16);
        RxString = RxString + " ";
        if (LH4 == true) RxString = RxString + ReadText(17);
        if (HL4 == true) RxString = RxString + ReadText(18);
      }
      SendSMS(GSM_Nr,RxString);
    }
  }
}


//###############################################################################################
// Control Output Timers
// 
//###############################################################################################
void ScanOutputTimers() {

  // Output Timer 1
  //---------------
  if (OutT1_Run) {
    CalcTime = OutT1;
    CalcTime = CalcTime * 60000;
    if ( (millis() - OutT1_ms) > CalcTime ) {
      OutT1_Run = false;
      digitalWrite(Output1,LOW);
    }
  }
  // Output Timer 2
  //---------------
  if (OutT2_Run) {
    CalcTime = OutT2;
    CalcTime = CalcTime * 60000;
    if ( (millis() - OutT2_ms) > CalcTime ) {
      OutT2_Run = false;
      digitalWrite(Output2,LOW);
    }
  }
  // Output Timer 3
  //---------------
  if (OutT3_Run) {
    CalcTime = OutT3;
    CalcTime = CalcTime * 60000;
    if ( (millis() - OutT3_ms) > CalcTime ) {
      OutT3_Run = false;
      digitalWrite(Output3,LOW);
    }
  }
  // Output Timer 4
  //---------------
  if (OutT4_Run) {
    CalcTime = OutT4;
    CalcTime = CalcTime * 60000;
    if ( (millis() - OutT4_ms) > CalcTime ) {
      OutT4_Run = false;
      digitalWrite(Output4,LOW);
    }
  }
}


//###############################################################################################
// Control Outputs
// 
//###############################################################################################
void SetOutputs() {

  boolean Pulse = false;
  // Switch ON Outputs
  //-----------------------------------------------
  //OUTPUT 1
  //--------
  if (On1 == true) {
    if (digitalRead(Output1) == 0) {
     if (Pulse1 == true) {
        Pulse = true;
        digitalWrite(Output1,HIGH);
      }
      if (Time1 == true) {
        OutT1_ms = millis();
        OutT1_Run = true;
        digitalWrite(Output1,HIGH);
        On1 = false;
      }
      if (Latch1 == true) {
        digitalWrite(Output1,HIGH);
        On1 = false;
      }
    }
    else {
      On1 = false;
      if (Time1 == true) {
        OutT1_ms = millis();
        OutT1_Run = true;
      }
      if (Latch1 == true) {
        digitalWrite(Output1,LOW);
      }
    }
  }

  //OUTPUT 2
  //--------
  if (On2 == true) {
    if (digitalRead(Output2) == 0) {
     if (Pulse2 == true) {
        Pulse = true;
        digitalWrite(Output2,HIGH);
      }
      if (Time2 == true) {
        OutT2_ms = millis();
        OutT2_Run = true;
        digitalWrite(Output2,HIGH);
        On2 = false;
      }
      if (Latch2 == true) {
        digitalWrite(Output2,HIGH);
        On2 = false;
      }
    }
    else {
      On2 = false;
      if (Time2 == true) {
        OutT2_ms = millis();
        OutT2_Run = true;
      }
      if (Latch2 == true) {
        digitalWrite(Output2,LOW);
      }
    }
  }

  //OUTPUT 3
  //--------
  if (On3 == true) {
    if (digitalRead(Output3) == 0) {
     if (Pulse3 == true) {
        Pulse = true;
        digitalWrite(Output3,HIGH);
      }
      if (Time3 == true) {
        OutT3_ms = millis();
        OutT3_Run = true;
        digitalWrite(Output3,HIGH);
        On3 = false;
      }
      if (Latch3 == true) {
        digitalWrite(Output3,HIGH);
        On3 = false;
      }
    }
    else {
      On3 = false;
      if (Time3 == true) {
        OutT3_ms = millis();
        OutT3_Run = true;
      }
      if (Latch3 == true) {
        digitalWrite(Output3,LOW);
      }
    }
  }

  //OUTPUT 4
  //--------
  if (On4 == true) {
    if (digitalRead(Output4) == 0) {
     if (Pulse4 == true) {
        Pulse = true;
        digitalWrite(Output4,HIGH);
      }
      if (Time4 == true) {
        OutT4_ms = millis();
        OutT4_Run = true;
        digitalWrite(Output4,HIGH);
        On4 = false;
      }
      if (Latch4 == true) {
        digitalWrite(Output4,HIGH);
        On4 = false;
      }
    }
    else {
      On4 = false;
      if (Time4 == true) {
        OutT4_ms = millis();
        OutT4_Run = true;
      }
      if (Latch4 == true) {
        digitalWrite(Output4,LOW);
      }
    }
  }
  
  // Pulse delay
  //-----------------------------------------------
  if (Pulse == true) {
    if (Pulse_ms > 5000) {
      delay(5000);
     // reset WDT
     wdt_reset();
     delay(Pulse_ms - 5000);
    }
    else {
      delay(Pulse_ms);
    }
     // reset WDT
     wdt_reset();
  }
  
  // Switch OFF Outputs
  //-----------------------------------------------
  //OUTPUT 1
  //--------
  if (On1 == true) {
    if (Pulse1 == true) {
      digitalWrite(Output1,LOW);
      On1 = false;
    }
  }
  //OUTPUT 2
  //--------
  if (On2 == true) {
    if (Pulse2 == true) {
      digitalWrite(Output2,LOW);
      On2 = false;
    }
  }
  //OUTPUT 3
  //--------
  if (On3 == true) {
    if (Pulse3 == true) {
      digitalWrite(Output3,LOW);
      On3 = false;
    }
  }
  //OUTPUT 4
  //--------
  if (On4 == true) {
    if (Pulse4 == true) {
      digitalWrite(Output4,LOW);
      On4 = false;
    }
  }
}


//###############################################################################################
// Handle CONTROLS received via SMS
// 
//###############################################################################################
void RespondToSMS() {

  // Get output channels that user can operate via SMS
  // This is the stored in GSM_Name
  //---------------------------------------------------
  int t = GSM_Name.indexOf(",");
  GSM_Name.remove(0,t+1);
  t = GSM_Name.indexOf(",");
  GSM_Name.remove(t);

  // Set output channel flags that can be controlled by the user
  //------------------------------------------------------------
  boolean oc1 = false;
  boolean oc2 = false;
  boolean oc3 = false;
  boolean oc4 = false;
  
  t = GSM_Name.indexOf("1");
  if (t >= 0) oc1 = true;
  t = GSM_Name.indexOf("2");
  if (t >= 0) oc2 = true;
  t = GSM_Name.indexOf("3");
  if (t >= 0) oc3 = true;
  t = GSM_Name.indexOf("4");
  if (t >= 0) oc4 = true;
  
  // Clear ShowCmd flag
  boolean ShowCmd = false;
  On1 = false;
  On2 = false;
  On3 = false;
  On4 = false;
    
  // Check outputs controlled by text labels
  //----------------------------------------

  // Output 1
  //---------
  if (oc1) {
    if ( ReceivedL(ReadText(19)) ) {
      if (Pulse1 == true) {
        On1 = true;
        ShowCmd = true;
      }
      if (Time1 == true) {
        On1 = true;
        ShowCmd = true;
      }
      if (Latch1 == true) {
        if (digitalRead(Output1) == false) {
          if (ReceivedL(ReadText(20)) ) {
            On1 = true;
            ShowCmd = true;
          }
        }
        if (digitalRead(Output1) == true) {
          if (ReceivedL(ReadText(21) ) ) {
            On1 = true;
            ShowCmd = true;
          }
        }
      }
    }
  }

  // Output 2
  //---------
  if (oc2) {
    if ( ReceivedL(ReadText(22)) ) {
      if (Pulse2 == true) {
        On2 = true;
        ShowCmd = true;
      }
      if (Time2 == true) {
        On2 = true;
        ShowCmd = true;
      }
      if (Latch2 == true) {
        if (digitalRead(Output2) == false) {
          if (ReceivedL(ReadText(23)) ) {
            On2 = true;
            ShowCmd = true;
          }
        }
        if (digitalRead(Output2) == true) {
          if (ReceivedL(ReadText(24) ) ) {
            On2 = true;
            ShowCmd = true;
          }
        }
      }
    }
  }

  // Output 3
  //---------
  if (oc3) {
    if ( ReceivedL(ReadText(25)) ) {
      if (Pulse3 == true) {
        On3 = true;
        ShowCmd = true;
      }
      if (Time3 == true) {
        On3 = true;
        ShowCmd = true;
      }
      if (Latch3 == true) {
        if (digitalRead(Output3) == false) {
          if (ReceivedL(ReadText(26)) ) {
            On3 = true;
            ShowCmd = true;
          }
        }
        if (digitalRead(Output3) == true) {
          if (ReceivedL(ReadText(27) ) ) {
            On3 = true;
            ShowCmd = true;
          }
        }
      }
    }
  }
  // Output 4
  //---------
  if (oc4) {
    if ( ReceivedL(ReadText(28)) ) {
      if (Pulse4 == true) {
        On4 = true;
        ShowCmd = true;
      }
      if (Time4 == true) {
        On4 = true;
        ShowCmd = true;
      }
      if (Latch4 == true) {
        if (digitalRead(Output4) == false) {
          if (ReceivedL(ReadText(29)) ) {
            On4 = true;
            ShowCmd = true;
          }
        }
        if (digitalRead(Output4) == true) {
          if (ReceivedL(ReadText(30) ) ) {
            On4 = true;
            ShowCmd = true;
          }
        }
      }
    }
  }

  // If Command was received, show
  if (ShowCmd) {
    ShowCmdOk();
    SetOutputs();
    return;
  }

  // Check status request or "????"
  //-----------------------------------------------
  if ( Received(Req_Update) ){
    digitalWrite(LedG,HIGH);
    // Disable WDT
    wdt_disable();

    // Configure to send SMS
    //-----------------------------------------------
    SSerial.print(F("AT+CMGS=\""));
    SSerial.print(GSM_Nr); 
    SSerial.print(F("\"\n\r"));
    delay(50);

    // Send Input SMS message
    //-----------------------------------------------
    RxString = F("INPUTS\n");
    RxString = RxString + ReadText(7);
    RxString = RxString + " ";
    if (digitalRead(Input1) == 0) RxString = RxString + ReadText(8); else RxString = RxString + ReadText(9);
    RxString = RxString + "\n";
    SSerial.print(RxString);
    RxString = ReadText(10);
    RxString = RxString + " ";
    if (digitalRead(Input2) == 0) RxString = RxString + ReadText(11); else RxString = RxString + ReadText(12);
    RxString = RxString + "\n";
    SSerial.print(RxString);
    RxString = ReadText(13);
    RxString = RxString + " ";
    if (digitalRead(Input3) == 0) RxString = RxString + ReadText(14); else RxString = RxString + ReadText(15);
    RxString = RxString + "\n";
    SSerial.print(RxString);
    RxString = ReadText(16);
    RxString = RxString + " ";
    if (digitalRead(Input4) == 0) RxString = RxString + ReadText(17); else RxString = RxString + ReadText(18);

    SSerial.print(RxString);
    delay(50);

    // Send output SMS message
    //-----------------------------------------------
    RxString = F("\n\nOUTPUTS\n");
    RxString = RxString + ReadText(19);
    RxString = RxString + " ";
    if (digitalRead(Output1) == 1) RxString = RxString + ReadText(20); else RxString = RxString + ReadText(21);
    RxString = RxString + "\n";
    SSerial.print(RxString);
    RxString = ReadText(22);
    RxString = RxString + " ";
    if (digitalRead(Output2) == 1) RxString = RxString + ReadText(23); else RxString = RxString + ReadText(24);
    RxString = RxString + "\n";
    SSerial.print(RxString);
    RxString = ReadText(25);
    RxString = RxString + " ";
    if (digitalRead(Output3) == 1) RxString = RxString + ReadText(26); else RxString = RxString + ReadText(27);
    RxString = RxString + "\n";
    SSerial.print(RxString);
    RxString = ReadText(28);
    RxString = RxString + " ";
    if (digitalRead(Output4) == 1) RxString = RxString + ReadText(29); else RxString = RxString + ReadText(30);

    SSerial.print(RxString);
    delay(50);
    SSerial.write((char)26);
    WaitOK();

    // enable WDT
    wdt_reset();
    wdt_enable(WDTO_8S);  
    
    digitalWrite(LedG,LOW);

    return;
  }
  // No valid command received
  PulseRed(2);
}


//###############################################################################################
// Get SMS Content
// 
//###############################################################################################
void GetSMS() {
  //Get SMS number
  //================================================
  GSM_Nr  = RxString;
  //get number
  int t1 = GSM_Nr.indexOf('"');
  int t2 = 0;
  GSM_Nr.remove(0,t1 + 1);
  t1 = GSM_Nr.indexOf('"');
  GSM_Nr.remove(t1);
   
  // Get SMS message
  //================================================
  GSM_Msg = RxString;
  t1 = GSM_Msg.indexOf('"');
  GSM_Msg.remove(0,t1 + 1);
  t1 = GSM_Msg.indexOf('"');
  GSM_Msg.remove(0,t1 + 1);
  t1 = GSM_Msg.indexOf('"');
  GSM_Msg.remove(0,t1 + 1);
  t1 = GSM_Msg.indexOf('"');
  GSM_Msg.remove(0,t1 + 1);
  t1 = GSM_Msg.indexOf('"');
  GSM_Msg.remove(0,t1 + 1);
  t1 = GSM_Msg.indexOf('"');
  GSM_Msg.remove(0,t1 + 1);
  GSM_Msg.remove(0,1);
  GSM_Msg.trim();

  // Get name from SMS stored on SIM
  //================================================
  GSM_Name = RxString;
  t1 = GSM_Name.indexOf('"');
  GSM_Name.remove(0,t1 + 1);
  t1 = GSM_Name.indexOf('"');
  GSM_Name.remove(0,t1 + 1);
  t1 = GSM_Name.indexOf('"');
  GSM_Name.remove(0,t1 + 1);
  t1 = GSM_Name.indexOf('"');
  GSM_Name.remove(t1);
  GSM_Name.trim();
  
  // If SMS contains format "MASTER,x" add number to SIM
  // ==================================================
  RxString = GSM_Msg;
  if (Received(Req_Master)) {
    if (!Boot) {
      PulseRed(5);
      return;
    }
    //Strip MASTER, from message
    GSM_Name = GSM_Msg;
    t1 = GSM_Name.indexOf(',');
    GSM_Name.remove(0,t1 + 1);
    GSM_Name.trim();
    
    // Save details to EEPROM
    SaveString(GSM_Nr,1);

    if (GSM_Name.length() == 0) {
      GSM_Name = "Smart-GSM";
    }
    SaveString(GSM_Name,2);
    Master_Nr = GSM_Nr;

    // Send confirmation SMS
    RxString = F("MASTER:\n");
    RxString = RxString + GSM_Name;
    SendSMS(Master_Nr,RxString);
    
    PulseGreen(2);
    return;
  }

  // The following commands can only be received from MASTER number
  // CLEAR ALL SETITNGs        CLEARALL
  // CLEAR SIM                 CLEARSIM
  // Reset unit                RESET
  // ADD USER                  Add,i,nr,x,y
  // DELETE USER               Del,x
  // GET BALANCE               $$$$
  // SET BALANCE USSD NUMBER   BALANCE,nr
  // SET PULSE DURATION        PULSETIME,x
  // SET INPUT NAMES           INTEXT,c,name,on,off
  // SET OUTPUT NAMES          OUTTEXT,c,name,on,off
  // SET TRIGGER ON CHANNELS   TRIGGERON,cccc
  // SET TRIGGER OFF CHANNEL   TRIGGEROFF,cccc
  // Set INPUT TIMERS          INTIME,c,t
  // SET OUTPUT MODES          OUTMODE,c,m,t
  //
  
  if (GSM_Nr == Master_Nr) {

    // If SMS contains format "RESET" reboot the unit
    //-----------------------------------------------
    if (Received(Req_Reset)) {
      ShowMasterOk();
      delay(1000);
      {
        asm volatile ("  jmp 0");  
      } 
      return;
    }  

    // If SMS contains format "CLEARSIM" delete all numbers from SIM
    //-----------------------------------------------
    if (Received(Req_ClearSIM)) {
      if (!Boot) {
        PulseRed(5);
        return;
      }
      digitalWrite(LedR,HIGH);
      for (byte i = 1;i <251;i++) {
        SSerial.print(F("AT+CPBW="));
        SSerial.print(i);      
        SSerial.print(F("\n\r"));
        WaitOK();
        // Reset WDT
        wdt_reset();
      }
      // Clear info on EEPROM for location 1 .. 5
      SaveString(" ",33);
      SaveText(F("0000"),34);
      SaveString(" ",35);
      SaveText(F("0000"),36);
      SaveString(" ",37);
      SaveText(F("0000"),38);
      SaveString(" ",39);
      SaveText(F("0000"),40);
      SaveString(" ",41);
      SaveText(F("0000"),42);
      digitalWrite(LedR,LOW);
      PulseGreen(3);
      return;
    }  

    // If SMS contains format "CLEARALL" clear all settings
    //-----------------------------------------------
    if (Received(Req_ClearAll)) {
      if (!Boot) {
        PulseRed(5);
        return;
      }
      // Reset EEPROM to factory default
      EEPROM.write(1,100);

      // Erase first 5 users from SIM
      for (byte i = 1;i <6;i++) {
        SSerial.print(F("AT+CPBW="));
        SSerial.print(i);      
        SSerial.print(F("\n\r"));
        WaitOK();
      }
 
      // Wait for unit to be rebooted via reset switch
      //----------------------------------------------
      while(1) {
        digitalWrite(LedR,HIGH);
        digitalWrite(LedG,LOW);
        delay(500);
        digitalWrite(LedR,LOW);
        digitalWrite(LedG,HIGH);
        delay(500);
      }
      return;
    }  

    // If SMS contains format "Add,<memory>,<number>,<name>, add number to SIM
    //-----------------------------------------------
    if (Received(Req_Add)) {
      ShowMasterOk();
      // Get memory location from message
      //-----------------------------------------------
      String Mem_Loc = GSM_Msg;
      t1 = Mem_Loc.indexOf(',');
      Mem_Loc.remove(0,t1 + 1);
      t1 = Mem_Loc.indexOf(',');
      Mem_Loc.remove(t1);  
      // Get phone number from message
      //-----------------------------------------------
      GSM_Nr = GSM_Msg;
      t1 = GSM_Nr.indexOf(',');
      GSM_Nr.remove(0,t1 + 1);
      t1 = GSM_Nr.indexOf(',');
      GSM_Nr.remove(0,t1 + 1);
      t1 = GSM_Nr.indexOf(',');
      GSM_Nr.remove(t1);  
    
      // Get name from message
      //-----------------------------------------------
      GSM_Name = GSM_Msg;
      t1 = GSM_Name.indexOf(',');
      GSM_Name.remove(0,t1 + 1);
      t1 = GSM_Name.indexOf(',');
      GSM_Name.remove(0,t1 + 1);
      t1 = GSM_Name.indexOf(',');
      GSM_Name.remove(0,t1 + 1);  
      GSM_Name.trim();
      
      // Save details to SIM
      //-----------------------------------------------
      SSerial.print(F("AT+CPBW="));
      SSerial.print(Mem_Loc);
      SSerial.print(F(",\""));
      delay(50);
      SSerial.print(GSM_Nr);
      delay(50);
      SSerial.print(F("\",,\"")); 
      delay(50);
      SSerial.print(GSM_Name);
      delay(50);
      SSerial.print(F("\"\n\r"));
      // Wait for save to complete
      WaitOK();
      // If location 1 .. 5 save to EEPROM
      t1 = GSM_Msg.indexOf(",");
      GSM_Msg.remove(0,t1 + 1);
      t1 = GSM_Msg.indexOf(",");
      GSM_Msg.remove(0,t1 + 1);
      t1 = GSM_Msg.indexOf(",");
      GSM_Msg.remove(0,t1 + 1);
      t1 = GSM_Msg.indexOf(",");
      GSM_Msg.remove(0,t1 + 1);
      t1 = GSM_Msg.indexOf(",");
      GSM_Msg.remove(0,t1 + 1);

      // Save info on EEPROM for location 1 .. 5
      if (Mem_Loc == "1") {
        SaveString(GSM_Nr,33);
        SaveText(GSM_Msg,34);
      }
      if (Mem_Loc == "2") {
        SaveString(GSM_Nr,35);
        SaveText(GSM_Msg,36);
      }
      if (Mem_Loc == "3") {
        SaveString(GSM_Nr,37);
        SaveText(GSM_Msg,38);
      }
      if (Mem_Loc == "4") {
        SaveString(GSM_Nr,39);
        SaveText(GSM_Msg,40);
      }
      if (Mem_Loc == "5") {
        SaveString(GSM_Nr,41);
        SaveText(GSM_Msg,42);
      }
      //Send confirmation SMS to new user

      RxString = F("Added to\n");
      RxString = RxString + ReadString(2);;
      RxString = RxString + F("\nPlease save!");
      SendSMS(GSM_Nr,RxString);
      
      return;
    }

    // If SMS contains format "Del,<memory> delete number from SIM
    //-----------------------------------------------
    if (Received(Req_Del)) {
      ShowMasterOk();
      // Get memory location from message
      //-----------------------------------------------
      String Mem_Loc = GSM_Msg;
      t1 = Mem_Loc.indexOf(',');
      Mem_Loc.remove(0,t1 + 1);
      t1 = Mem_Loc.indexOf(',');
      Mem_Loc.remove(t1);  
      //-----------------------------------------------
      SSerial.print(F("AT+CPBW="));
      SSerial.print(Mem_Loc);
      SSerial.print(F("\n\r"));
      // Wait for save to complete
      //-----------------------------------------------
      WaitOK();
      // Clear info on EEPROM for location 1 .. 5
      if (Mem_Loc == "1") {
        SaveString(" ",33);
        SaveText(F("0000"),34);
      }
      if (Mem_Loc == "2") {
        SaveString(" ",35);
        SaveText(F("0000"),36);
      }
      if (Mem_Loc == "3") {
        SaveString(" ",37);
        SaveText(F("0000"),38);
      }
      if (Mem_Loc == "4") {
        SaveString(" ",39);
        SaveText(F("0000"),40);
      }
      if (Mem_Loc == "5") {
        SaveString(" ",41);
        SaveText(F("0000"),42);
      }
      return;
    }
    
    // If SMS contains $$$$ or "Balance", request balance
    //-----------------------------------------------
    if ( Received(Req_Balance) ) {
      //ShowMasterOk();
      SSerial.print(F("ATD"));
      SSerial.print(USSD_Nr);
      SSerial.print(F("\n\r"));
      return;
    }
    // If SMS contains format "BALANCE,<number>,text add number to EEPROM
    //-----------------------------------------------
    if (Received(Req_USSD)) {
      ShowMasterOk();

      // Get phone number from message
      //-----------------------------------------------
      USSD_Nr = GSM_Msg;
      t1 = USSD_Nr.indexOf(',');
      USSD_Nr.remove(0,t1 + 1);
      t1 = USSD_Nr.indexOf(',');
      USSD_Nr.remove(t1); 

      // Get USSD keywords
      //-----------------------------------------------
      USSD_Name = GSM_Msg;
      t1 = USSD_Name.indexOf(',');
      USSD_Name.remove(0,t1 + 1);
      t1 = USSD_Name.indexOf(',');
      USSD_Name.remove(0,t1 + 1);

      // Save data to EEPROM
      //-----------------------------------------------
      SaveString(USSD_Nr,3);
      SaveString(USSD_Name,4);
      return;
    }
    
    String Temp  = GSM_Msg;
    String Temp1 = GSM_Msg;
    String Temp2 = GSM_Msg;
    String Temp3 = GSM_Msg;
    String Temp4 = GSM_Msg;
    
    // If SMS contains format "OUTMODE,c,m,t" set output mode
    //-----------------------------------------------
    if (Received(Req_OutMode)) {

      // Get channel
      Temp1 = GSM_Msg;
      t1 = Temp1.indexOf(",");
      Temp1.remove(0,t1 + 1);
      Temp2 = Temp1;
      t1 = Temp1.indexOf(",");
      Temp1.remove(t1);
      t1 = Temp1.toInt();

      if (t1 > 4) {
        // exit
        PulseRed(3);
        return;
      }

      // Get mode
      t2 = Temp2.indexOf(",");
      Temp2.remove(0,t2 + 1);
      Temp3 = Temp2;
      t2 = Temp2.indexOf(",");
      Temp2.remove(t2);
      t2 = 0;
      if ( (Temp2 == "P") or (Temp2 == "p") ) t2 = 1;
      if ( (Temp2 == "T") or (Temp2 == "t") ) t2 = 2;
      if ( (Temp2 == "L") or (Temp2 == "l") ) t2 = 3;
      if (t2 == 0) {
        // exit
        PulseRed(3);
        return;
      }

      // Get time
      int t3 = Temp3.indexOf(",");
      Temp3.remove(0,t3 + 1);
      Temp3.trim();
      t3 = Temp3.toInt();
      if (t3 < 1) t3 = 1;
      if (t3 > 720) t3 = 720;

      // Reset output timers
      OutT1_Run = false;
      OutT2_Run = false;
      OutT3_Run = false;
      OutT4_Run = false;
      digitalWrite(Output1,LOW);
      digitalWrite(Output2,LOW);
      digitalWrite(Output3,LOW);
      digitalWrite(Output4,LOW);

      // Compose channel details
      // Channel 1
      if (t1 == 1) {
        OutT1 = t3;
        SaveInt(OutT1,5);
        if (t2 == 1) {
          Pulse1 = true;
          Time1 = false;
          Latch1 = false;
        }
        if (t2 == 2) {
          Pulse1 = false;
          Time1 = true;
          Latch1 = false;
        }
        if (t2 == 3) {
          Pulse1 = false;
          Time1 = false;
          Latch1 = true;
        }
      }
      // Channel 2
      if (t1 == 2) {
        OutT2 = t3;
        SaveInt(OutT2,6);
        if (t2 == 1) {
          Pulse2 = true;
          Time2 = false;
          Latch2 = false;
        }
        if (t2 == 2) {
          Pulse2 = false;
          Time2 = true;
          Latch2 = false;
        }
        if (t2 == 3) {
          Pulse2 = false;
          Time2 = false;
          Latch2 = true;
        }
      }
      // Channel 3
      if (t1 == 3) {
        OutT3 = t3;
        SaveInt(OutT3,7);
        if (t2 == 1) {
          Pulse3 = true;
          Time3 = false;
          Latch3 = false;
        }
        if (t2 == 2) {
          Pulse3 = false;
          Time3 = true;
          Latch3 = false;
        }
        if (t2 == 3) {
          Pulse3 = false;
          Time3 = false;
          Latch3 = true;
        }
      }
      // Channel 4
      if (t1 == 4) {
        OutT4 = t3;
        SaveInt(OutT4,8);
        if (t2 == 1) {
          Pulse4 = true;
          Time4 = false;
          Latch4 = false;
        }
        if (t2 == 2) {
          Pulse4 = false;
          Time4 = true;
          Latch4 = false;
        }
        if (t2 == 3) {
          Pulse4 = false;
          Time4 = false;
          Latch4 = true;
        }
      }
     
      //Save Channel Modes
      RxString = "";
      if (Pulse1) RxString = RxString + "1"; else RxString = RxString + "0";
      if (Pulse2) RxString = RxString + "2"; else RxString = RxString + "0";
      if (Pulse3) RxString = RxString + "3"; else RxString = RxString + "0";
      if (Pulse4) RxString = RxString + "4"; else RxString = RxString + "0";
      SaveString(RxString,43);

      RxString = "";
      if (Time1) RxString = RxString + "1"; else RxString = RxString + "0";
      if (Time2) RxString = RxString + "2"; else RxString = RxString + "0";
      if (Time3) RxString = RxString + "3"; else RxString = RxString + "0";
      if (Time4) RxString = RxString + "4"; else RxString = RxString + "0";
      SaveString(RxString,44);

      RxString = "";
      if (Latch1) RxString = RxString + "1"; else RxString = RxString + "0";
      if (Latch2) RxString = RxString + "2"; else RxString = RxString + "0";
      if (Latch3) RxString = RxString + "3"; else RxString = RxString + "0";
      if (Latch4) RxString = RxString + "4"; else RxString = RxString + "0";
      SaveString(RxString,45);      
      ShowMasterOk();

      return;
    }
    
    // If SMS contains format "PULSETIME,x" set pulse duration
    //-----------------------------------------------
    if (Received(Req_PulseTime)) {
      ShowMasterOk();
      Temp = RxString;
      t1 = Temp.indexOf(",");
      Temp.remove(0,t1 + 1);
      Pulse_ms = Temp.toInt();
      if (Pulse_ms > 10) Pulse_ms = 10;
      SaveInt(Pulse_ms,9);
      if (Pulse_ms < 1) {
        Pulse_ms = 500;
      }
      else {
        Pulse_ms = Pulse_ms * 1000;    
      }
      return;
    }

    Temp  = GSM_Msg;
    Temp1 = GSM_Msg;
    Temp2 = GSM_Msg;
    Temp3 = GSM_Msg;
    Temp4 = GSM_Msg;
    
    // If SMS contains format "INTIME,c,t" set input timerpulse duration
    //-----------------------------------------------
    if (Received(Req_In_Time)) {
      ShowMasterOk();
      t1 = Temp.indexOf(",");
      Temp.remove(0,t1 + 1);
      Temp2 = Temp;
      // "INTEXT," removed
      Temp1 = Temp;
      t1 = Temp1.indexOf(",");
      Temp1.remove(t1);
      t1 = Temp2.indexOf(",");
      Temp2.remove(0,t1 + 1);
      Temp3 = Temp2;
      t1 = Temp2.indexOf(",");
      Temp2.remove(t1);
      // Save value in EEPROM
      t1 = Temp1.toInt();
      t2 = Temp2.toInt();
      if (t2 > 3600) t2 = 3600;          // Limit input timers to 3600s, or 60 minutes
      SaveInt(t2,t1);
      if (t1 ==1) InT1 = t2;
      if (t1 ==2) InT2 = t2;
      if (t1 ==3) InT3 = t2;
      if (t1 ==4) InT4 = t2;

      // Reset all input timers after changes.
      InT1_Run = false;
      InT2_Run = false;
      InT3_Run = false;
      InT4_Run = false;
      
      return;
    }
    
    Temp  = GSM_Msg;
    Temp1 = GSM_Msg;
    Temp2 = GSM_Msg;
    Temp3 = GSM_Msg;
    Temp4 = GSM_Msg;
    RxString = GSM_Msg;

    // If SMS contains format "INTEXT,a,b,c,d" then save Input Text
    // ------------------------------------------------------------
    if (Received(Req_InText)) {
      ShowMasterOk();
      t1 = Temp.indexOf(",");
      Temp.remove(0,t1 + 1);
      Temp2 = Temp;
      // "INTEXT," removed
      Temp1 = Temp;
      t1 = Temp1.indexOf(",");
      Temp1.remove(t1);

      t1 = Temp2.indexOf(",");
      Temp2.remove(0,t1 + 1);
      Temp3 = Temp2;
      t1 = Temp2.indexOf(",");
      Temp2.remove(t1);
      
      t1 = Temp3.indexOf(",");
      Temp3.remove(0,t1 + 1);
      Temp4 = Temp3;
      t1 = Temp3.indexOf(",");
      Temp3.remove(t1);

      t1 = Temp4.indexOf(",");
      Temp4.remove(0,t1 + 1);
      Temp4.trim();

      if(Temp1 == "1") {
        SaveText(Temp2,7);
        SaveText(Temp3,8);
        SaveText(Temp4,9);
      }
      if(Temp1 == "2") {
        SaveText(Temp2,10);
        SaveText(Temp3,11);
        SaveText(Temp4,12);
      }
      if(Temp1 == "3") {
        SaveText(Temp2,13);
        SaveText(Temp3,14);
        SaveText(Temp4,15);
      }
      if(Temp1 == "4") {
        SaveText(Temp2,16);
        SaveText(Temp3,17);
        SaveText(Temp4,18);
      }
      return;
    }

    Temp  = GSM_Msg;
    Temp1 = GSM_Msg;
    Temp2 = GSM_Msg;
    Temp3 = GSM_Msg;
    Temp4 = GSM_Msg;
    RxString = GSM_Msg;
        
     // If SMS contains format "OUTTEXT,a,b,c,d" then save Output Text
    // ------------------------------------------------------------
    if (Received(Req_OutText)) {
      ShowMasterOk();
      t1 = Temp.indexOf(",");
      Temp.remove(0,t1 + 1);
      Temp2 = Temp;
      // "OUTTEXT," removed
      Temp1 = Temp;
      t1 = Temp1.indexOf(",");
      Temp1.remove(t1);

      t1 = Temp2.indexOf(",");
      Temp2.remove(0,t1 + 1);
      Temp3 = Temp2;
      t1 = Temp2.indexOf(",");
      Temp2.remove(t1);
      
      t1 = Temp3.indexOf(",");
      Temp3.remove(0,t1 + 1);
      Temp4 = Temp3;
      t1 = Temp3.indexOf(",");
      Temp3.remove(t1);

      t1 = Temp4.indexOf(",");
      Temp4.remove(0,t1 + 1);
      Temp4.trim();
      
      if(Temp1 == "1") {
        SaveText(Temp2,19);
        SaveText(Temp3,20);
        SaveText(Temp4,21);
      }
      if(Temp1 == "2") {
        SaveText(Temp2,22);
        SaveText(Temp3,23);
        SaveText(Temp4,24);
      }
      if(Temp1 == "3") {
        SaveText(Temp2,25);
        SaveText(Temp3,26);
        SaveText(Temp4,27);
      }
      if(Temp1 == "4") {
        SaveText(Temp2,28);
        SaveText(Temp3,29);
        SaveText(Temp4,30);
      }
      return;
    }

    // If SMS contains format "INPUTON,xxxx" set ON alarms
    //----------------------------------------------------
    if (Received(Req_In_On)) {
      ShowMasterOk();
      if (Received("1") ) Alm1_On = true; else Alm1_On = false;
      if (Received("2") ) Alm2_On = true; else Alm2_On = false;
      if (Received("3") ) Alm3_On = true; else Alm3_On = false;
      if (Received("4") ) Alm4_On = true; else Alm4_On = false;
      t1 = RxString.indexOf(",");
      RxString.remove(0,t1 + 1);
      SaveString(RxString,31);
      return;
    }  

    // If SMS contains format "INPUTOFF,xxxx" set OFF alarms
    //----------------------------------------------------
    if (Received(Req_In_Off)) {
      ShowMasterOk();
      if (Received("1") ) Alm1_Off = true; else Alm1_Off = false;
      if (Received("2") ) Alm2_Off = true; else Alm2_Off = false;
      if (Received("3") ) Alm3_Off = true; else Alm3_Off = false;
      if (Received("4") ) Alm4_Off = true; else Alm4_Off = false;
      t1 = RxString.indexOf(",");
      RxString.remove(0,t1 + 1);
      SaveString(RxString,32);
      return;
    }  

   
  }

  //If number unknown return
  //---------------------------------------------------
  if ( GSM_Name.length() == 0) {
    //exit if no info stored for I/Os
    PulseRed(1);
    return;
  }
  //Respond to other SMS queries from SUPER USERs and NORMAL USERS
  RespondToSMS();
}


//###############################################################################################
// Get USSD message
// 
//###############################################################################################
void GetUSSD() {
  // Get USSD message
  //-----------------------------------------------
  GSM_Msg = RxString;
  byte t1 = GSM_Msg.indexOf('"');
  GSM_Msg.remove(0,t1 + 1);
  t1 = GSM_Msg.indexOf('"');
  GSM_Msg.remove(t1);
  GSM_Msg.trim();
  RxString = GSM_Msg;
  
  // If USSD message contains Airtime, forward to phone
  //-----------------------------------------------
  if (Received(USSD_Name)) {

    // Normal balance request
    //-----------------------------------------------
    if (!Bal_Check) SendSMS(Master_Nr,RxString);

    // Daily balance check
    //-----------------------------------------------
    if (Bal_Check) {
      Bal_Check = false;

      t1 = GSM_Msg.indexOf('R');
      GSM_Msg.remove(0,t1 + 1);
      t1 = GSM_Msg.indexOf(';');
      GSM_Msg.remove(t1);
      GSM_Msg.trim();
      t1 = GSM_Msg.toInt();
      RxString = "Balance low\r\nR" + GSM_Msg;
      if (t1 < Bal_Limit) SendSMS(Master_Nr,RxString);
    }
  }
}


//###############################################################################################
// Send SMS 
// 
//###############################################################################################
void SendSMS(String Nr, String Msg) {
  digitalWrite(LedG,HIGH);

  // Disable WDT
  wdt_disable();

    
    // Configure to send SMS
  //-----------------------------------------------
  SSerial.print(F("AT+CMGS=\""));
  // Send SMS number
  //-----------------------------------------------
  SSerial.print(Nr); 
  SSerial.print(F("\"\r\n"));
  delay(50);
  // Send SMS message
  //-----------------------------------------------
  SSerial.print(Msg);
  delay(50);
  // Send Ctrl+Z / ESC to denote SMS message is complete
  //-----------------------------------------------
  SSerial.write((char)26);
  WaitOK();
  
  // enable WDT
  wdt_reset();
  wdt_enable(WDTO_8S);    
  
  digitalWrite(LedG,LOW);
}


//###############################################################################################
// Respond to incomming calls
// 
//###############################################################################################
void GetCall() {
  // Get caller name from SIM
  //-----------------------------------------------
  GSM_Name = RxString;
  byte t1 = GSM_Name.indexOf('"');
  GSM_Name.remove(0,t1 + 1);
  t1 = GSM_Name.indexOf('"');
  GSM_Name.remove(0,t1 + 1);
  t1 = GSM_Name.indexOf('"');
  GSM_Name.remove(0,t1 + 1);
  byte t2 = GSM_Name.indexOf('"');
  GSM_Name.remove(t2);
  GSM_Name.trim();
  
  // See if caller is stored on SIM
  //-----------------------------------------------
  t1 = GSM_Name.length();
  if (t1 == 0) {
    // Caller not on SIM
    //-----------------------------------------------    
    // Drop incoming call
    //-----------------------------------------------
    SSerial.print(F("ATH\n\r"));
    PulseRed(1);
    return;
  }
  // Caller present on SIM. Respond to call
  //-----------------------------------------------    
  PulseGreen(1);
  delay(2000);
  SSerial.print(F("ATH\n\r"));
  // Determine which outputs to switch
  //------------------------------------------------
  t1 = GSM_Name.indexOf(",");
  GSM_Name.remove(t1);
  RxString = GSM_Name;
  if (Received("1") ) On1 = true;
  if (Received("2") ) On2 = true;
  if (Received("3") ) On3 = true;
  if (Received("4") ) On4 = true;

  // Set outputs
  // -----------
  SetOutputs();
}


//###############################################################################################
// Init GSM Module
// 
//###############################################################################################
void InitGSM() {
  
  Connected = false;
  digitalWrite(LedR,HIGH);
  
  // Setup I/Os
  //-----------------------------------------------
  pinMode(SIM800_Reset,OUTPUT);
  
  // Reboot SIM800
  //-----------------------------------------------
  digitalWrite(SIM800_Reset,LOW);
  delay(250);
  digitalWrite(SIM800_Reset,HIGH);
  //wait for GSM module to reboot (about 5 seconds)
  delay(5000);

  //Scan for GSM Module
  //-----------------------------------------------
  SSerial.print(F("AT\r\n"));
  WaitOK();

  //Set SIM800 LED flash rate
  //-----------------------------------------------
  SSerial.print(F("AT+SLEDS=2,64,800\r\n"));
  WaitOK();

  // Wait for network connectivity
  //-----------------------------------------------
  while(!Connected) {
    SSerial.print(F("AT+CREG?\r\n"));
    delay(1000);
    RxString = "";
    while(SSerial.available()){
      RxString = RxString + char(SSerial.read());
    }
    if (Received(F("+CREG: 0,1"))) {
      //network connected
      Connected = true;
    }
  }
  
  // Set SMS mode to ASCII
  //-----------------------------------------------
  SSerial.print(F("AT+CMGF=1\r\n"));
  WaitOK();
  
  // Set device to read SMS if available and print to serial
  //-----------------------------------------------
  SSerial.print(F("AT+CNMI=1,2,0,0,0\r\n"));
  WaitOK();
  
  // Delete old SMS
  //-----------------------------------------------
  SSerial.print(F("AT+CMGD=1,4\r\n"));
  WaitOK();  
  
  // Set phone book to SIM
  //-----------------------------------------------
  SSerial.print(F("AT+CPBS=\"SM\"\r\n"));
  WaitOK();

  // Set local time to network time
  //-----------------------------------------------
  SSerial.print(F("AT+COPS=2\r\n"));
  WaitOK();
  SSerial.print(F("AT+CLTS=1\r\n"));
  WaitOK();
  SSerial.print(F("AT+COPS=0\r\n"));
  WaitOK();  
  
  //wait for network operator to be selected
  //-----------------------------------------------
  while(!SSerial.available()) {
  }
  //wait for time to be set
  delay(5000);
  while(SSerial.available()) {
    char c1 = char(SSerial.read());
    c1 = c1;
  }
  
  // Setup USSD function
  //-----------------------------------------------
  SSerial.print(F("AT+CUSD=1\r\n"));
  WaitOK();

  // Cancel Call Forwarding
  //-----------------------------------------------
  SSerial.print(F("AT+CCFC=0,0\r\n"));
  WaitOK();
  
  // Cancel Call Waiting
  //-----------------------------------------------
  SSerial.print(F("AT+CCWA=0,0\r\n"));
  WaitOK();

  // Enable List of Current Calls
  //-----------------------------------------------
  SSerial.print(F("AT+CLCC=1\r\n"));
  WaitOK();
  
  // Disable Caller Line ID
  //-----------------------------------------------
  SSerial.print(F("AT+CLIP=0\r\n"));
  WaitOK();
  
  // Flash LED to indicate boot up enabled
  //-----------------------------------------------
  SSerial.print(F("AT+SLEDS=2,64,64\r\n"));
  WaitOK();

  // Read serial buffer to empty
  //-----------------------------------------------
  while(SSerial.available()){
    char c = char(SSerial.read());
    c = c;
  }
  digitalWrite(LedR,LOW);
  
  // Send REBOOT SMS to MASTER USER
  //-----------------------------------------------
  if (Master_Nr.length() > 0) {
    RxString = ReadString(2);
    RxString = RxString + F("\nwas restarted!");
    SendSMS(Master_Nr,RxString);
  }
}


//###############################################################################################
// Wait for the OK response from software serial port
// 
//###############################################################################################
void WaitOK() {
  char c1= ' ';
  char c2 = ' ';
  boolean OK = false;
  // Wait for data in rx buffer
  //-----------------------------------------------
  while(!SSerial.available()){
  }
  // Read data from buffer
  //-----------------------------------------------
  while (!OK) {
    while(SSerial.available()){
      c1 = c2;
      c2 = char(SSerial.read());
      if ( (c1 == 'O') and (c2 == 'K') ) {
        // OK received
        //-----------------------------------------------
        OK = true;
      }
    }
  }
}


//###############################################################################################
// READ string from EEPROM
// 
//###############################################################################################
String ReadString(int s1) {
  String s2 = "";
  s1 = s1 * 20;
  for (byte i = 0;i < 20;i++) {
    s2 = s2 + char(EEPROM.read(s1 + i));
  }
  s2.trim();
  return s2;
}


//###############################################################################################
// SAVE string to EEPROM
// 
//###############################################################################################
void SaveString(String s1, unsigned int s2) {
  // Adjust string length to 20 characters
  //-----------------------------------------------
  s1.trim();
  s2 = s2 * 20;
  byte l = s1.length();
  if (s1.length() > 20) {
    s1.remove(20,l+1);
  }
  while(s1.length() < 20) {
    s1 = s1 + " ";  
  }
  // Save string to memory
  //------------------------------------------------
  for (byte i = 0;i < 20;i++) {
    EEPROM.write(s2 + i, s1[i]);
  }
}


//###############################################################################################
// SAVE text to EEPROM
// 
//###############################################################################################
void SaveText(String s1, unsigned int s2) {
  // Adjust string length to 20 characters
  //-----------------------------------------------
  s1.trim();
  s2 = s2 * 20;
  byte l = s1.length();
  if (s1.length() > 10) {
    s1.remove(10,l);
  }
  while(s1.length() < 10) {
    s1 = s1 + " ";  
  }
  // Save string to memory
  //------------------------------------------------
  for (byte i = 0;i < 10;i++) {
    EEPROM.write(s2 + i, s1[i]);
  }
}

//###############################################################################################
// READ text from EEPROM
// 
//###############################################################################################
String ReadText(int s1) {
  String s2 = "";
  s1 = s1 * 20;
  for (byte i = 0;i < 10;i++) {
    s2 = s2 + char(EEPROM.read(s1 + i));
  }
  s2.trim();
  return s2;
}

//###############################################################################################
// READ integer
// 
//###############################################################################################
unsigned int  ReadInt(int s1) {
  unsigned int s2 = 0;
  s1 = s1 * 2;
  s1 = s1 + 970;
  s2 = EEPROM.read(s1-1);
  s2 = s2 * 256;
  s2 = s2 + EEPROM.read(s1);
  return s2;
}


//###############################################################################################
// WRITE integer
// 
//###############################################################################################
void  SaveInt(unsigned int s1,int s2) {
  s2 = s2 * 2;
  s2 = s2 + 970;
  EEPROM.write(s2-1,s1/256);
  EEPROM.write(s2,s1%256);
}


//###############################################################################################
// Search for specific characters inside RxString
// 
//###############################################################################################
boolean Received(String S) {
  if (RxString.indexOf(S) >= 0) return true; else return false;
}


//###############################################################################################
// Search for specific characters inside RxString
// 
//###############################################################################################
boolean ReceivedL(String S) {
  S.trim();
  S.toLowerCase();
  RxString.toLowerCase();
  if (RxString.indexOf(S) >= 0) return true; else return false;
}


//###############################################################################################
// Green on to indicate command accepted
// 
//###############################################################################################
void ShowCmdOk() {
  digitalWrite(LedG,HIGH);
  delay(500);
  digitalWrite(LedG,LOW);
}

//###############################################################################################
// Red on to indicate MASTER USER
// 
//###############################################################################################
void ShowMasterOk() {
  digitalWrite(LedG,HIGH);
  digitalWrite(LedR,HIGH);
  delay(500);
  digitalWrite(LedG,LOW);
  digitalWrite(LedR,LOW);
}


//###############################################################################################
// Pulse Red LED
// 
//###############################################################################################
void PulseRed(byte n) {
  if (n == 0) return;
  for (byte i = 1;i <= n;i++) {
    digitalWrite(LedR,HIGH);
    delay(500);
    digitalWrite(LedR,LOW);
    delay(500);
  }
}


//###############################################################################################
// Pulse Green LED
// 
//###############################################################################################
void PulseGreen(byte n) {
  if (n == 0) return;
  for (byte i = 1;i<= n;i++) {
    digitalWrite(LedG,HIGH);
    delay(500);
    digitalWrite(LedG,LOW);
    delay(500);
  }
}


//###############################################################################################
// Read settings from EEPROM
// 
//###############################################################################################
void ReadSettings() {
   
  // See if new EEPROM
  //------------------
  if (EEPROM.read(1) != 124) {
    PulseRed(1);
    
    // Do not erase MASTER USER after Factory Reset
    if (EEPROM.read(1) != 100) {
      SaveString("",1);
      SaveString(F("Smart-GSM"),2);
    }

    // USSD Info
    SaveString(F("*111*502#"),3);
    SaveString(F("Airtime"),4);
    
    // Input labels
    SaveText(F("Input1"),7);
    SaveText(F("On"),8);
    SaveText(F("Off"),9);
    SaveText(F("Input2"),10);
    SaveText(F("On"),11);
    SaveText(F("Off"),12);
    SaveText(F("Input3"),13);
    SaveText(F("On"),14);
    SaveText(F("Off"),15);
    SaveText(F("Input4"),16);
    SaveText(F("On"),17);
    SaveText(F("Off"),18);

    // Output labels
    SaveText(F("Output1"),19);
    SaveText(F("On"),20);
    SaveText(F("Off"),21);
    SaveText(F("Output2"),22);
    SaveText(F("On"),23);
    SaveText(F("Off"),24);
    SaveText(F("Output3"),25);
    SaveText(F("On"),26);
    SaveText(F("Off"),27);
    SaveText(F("Output4"),28);
    SaveText(F("On"),29);
    SaveText(F("Off"),30);

    // Input L-H channels
    SaveText(F("0000"),31);

    // Input H-L channels
    SaveText(F("0000"),32);

    // SUPER USERS numbers and input channels
    SaveString("",33);
    SaveText(F("0000"),34);
    SaveString("",35);
    SaveText(F("0000"),36);
    SaveString("",37);
    SaveText(F("0000"),38);
    SaveString("",39);
    SaveText(F("0000"),40);
    SaveString("",41);
    SaveText(F("0000"),42);

    // Output PULSE channels
    SaveText(F("1234"),43);

    // Output TIMER channels
    SaveText(F("0000"),44);

    // Output LATCHING channels
    SaveText(F("0000"),45);
    
    // Input timers
    SaveInt(1,1);
    SaveInt(1,2);
    SaveInt(1,3);
    SaveInt(1,4);

    // Output timers
    SaveInt(1,5);
    SaveInt(1,6);
    SaveInt(1,7);
    SaveInt(1,8);
    
    // Pulse output delay
    SaveInt(1,9);  
    
    EEPROM.write(1,124);
  }
  else {
    PulseGreen(1);
  }

  // Read setting from EEDATA
  //-------------------------
  Master_Nr  = ReadString(1);
  GSM_Name   = ReadString(2);

  // Read USSD Codes
  USSD_Nr    = ReadString(3);
  USSD_Name  = ReadString(4);  

  // Read L-H input triggers
  RxString = ReadString(31);
  if (Received("1") ) Alm1_On = true; else Alm1_On = false;
  if (Received("2") ) Alm2_On = true; else Alm2_On = false; 
  if (Received("3") ) Alm3_On = true; else Alm3_On = false; 
  if (Received("4") ) Alm4_On = true; else Alm4_On = false; 

  // Read L-H input triggers
  RxString = ReadString(32);
  if (Received("1") ) Alm1_Off = true; else Alm1_Off = false;
  if (Received("2") ) Alm2_Off = true; else Alm2_Off = false;
  if (Received("3") ) Alm3_Off = true; else Alm3_Off = false;
  if (Received("4") ) Alm4_Off = true; else Alm4_Off = false;

  // Output pulse channels
  RxString = ReadText(43);
  if (Received("1") ) Pulse1 = true; else Pulse1 = false;
  if (Received("2") ) Pulse2 = true; else Pulse2 = false;
  if (Received("3") ) Pulse3 = true; else Pulse3 = false;
  if (Received("4") ) Pulse4 = true; else Pulse4 = false;

  // Output timer channels
  RxString = ReadText(44);
  if (Received("1") ) Time1 = true; else Time1 = false;
  if (Received("2") ) Time2 = true; else Time2 = false;
  if (Received("3") ) Time3 = true; else Time3 = false;
  if (Received("4") ) Time4 = true; else Time4 = false;

  // Output latching channels
  RxString = ReadText(45);
  if (Received("1") ) Latch1 = true; else Latch1 = false;
  if (Received("2") ) Latch2 = true; else Latch2 = false;
  if (Received("3") ) Latch3 = true; else Latch3 = false;
  if (Received("4") ) Latch4 = true; else Latch4 = false;

  // read input timers
  InT1 = ReadInt(1);
  if (InT1 > 3600) InT1 = 3600;
  InT2 = ReadInt(2);
  if (InT2 > 3600) InT2 = 3600;
  InT3 = ReadInt(3);
  if (InT3 > 3600) InT3 = 3600;
  InT4 = ReadInt(4);
  if (InT4 > 3600) InT4 = 3600;

  // read output timers
  OutT1 = ReadInt(5);
  if (OutT1 > 720) OutT1 = 720;          // limit value between 1 and 720
  if (OutT1 < 1  ) OutT2 = 1;
  OutT2 = ReadInt(6);
  if (OutT2 > 720) OutT2 = 720;          // limit value between 1 and 720
  if (OutT2 < 1  ) OutT2 = 1;
  OutT3 = ReadInt(7);
  if (OutT3 > 720) OutT3 = 720;          // limit value between 1 and 720
  if (OutT3 < 1  ) OutT3 = 1;
  OutT4 = ReadInt(8);
  if (OutT4 > 720) OutT4 = 720;          // limit value between 1 and 720
  if (OutT4 < 1  ) OutT4 = 1;

  // Read output pulse time
  Pulse_ms = ReadInt(9);
  if (Pulse_ms > 10) Pulse_ms = 10;
  if (Pulse_ms < 1) {
    Pulse_ms = 500;
  }
  else {
    Pulse_ms = Pulse_ms * 1000;    
  }
  
  delay(1000);
}



