//#define usb_disable // Disable all USB
//#define simplcv1
#ifdef simplcv1
#define lcd_disable // Disable LCD (no LCD available on SimPLCv1)
#endif

//#define gsm_echo_ext_rx // Echo data input received via USB to the GSM module

/*
*** Brief ***
- Handle various events from the GSM Library in the gsmEvent routine
  (such as missed-call, message received, etc)
- Send messages using gsmMsgSend routine
- HTTP GET/POST using gsmGprsHttpGet/gsmGprsHttpPost
This example code for the SimPLC is currently set up to perform various actions
when a message is received, missed call is detected, or RB7 is pulled low - 
please see the below overview for details. 
!!! Please see the warning below regarding msgs stored in SIM card memory !!!
*/

/*
*** Overview ***
Software written by Dane du Plooy
http://www.dizzy.co.za
September 2014

!!! Warning !!!
The code uses the SIM card memory for drafting and reading messages.
This is done in order to maximise reliability.
If the SIM card message memory is not currently empty, then
any messages stored on the SIM card will be processed and deleted, 
and any drafts saved on the SIM card will be sent.
Please check that this will not cause any problems
before running this code with your SIM card. 
(Perhaps copy any relevant messages from SIM card memory to phone memory.)

Currently, the software will:
(please note that all received SMSs are converted to lower-case by the software, 
and are thus not case sensitive)
- Toggle PortD.0 when a missed-call is detected
- Reply with a status report if a message beginning with "stat" is received
- Toggle PortD.1 and reply with a status report if a "Please Call Me"
  (South African SMS phenomenon) is received.
- Set PortD all on if a message beginning with "on" is received
- Set PortD all off if a message beginning with "off" is received
- Reply with the SimPLC's internal date/time if a message beginning with
  "c" (for clock) is received
- Send a message to the number stored in strDestID if RB7 is pulled low
  (if strDestID is not explicitly set then it will be taken from the first
  missed call or SMS received)
  - or -
  Perform an HTTP GET or POST if RB7 is pulled low
  - or -
  Power the GSM module off whilst RB7 is pulled low
  (comment/uncomment code as required)
  
Debug information is output via USB
(receivable by the mikroElektronika USB (HID) Terminal).
*/

//#include "Str.h"
//#include "GSM.h"
#include "RTCC.h"

//<RTCC>
bit bitRTCC_Set; // Indicates if the SimPLCs internal real-time calendar/clock has been set
//</RTCC>

//<GSM>
#ifndef simplcv1 //SimPLCv2 or later
sbit GSM_Pwr_Key at LATE2_bit;
sbit GSM_Pwr_Key_Dir at TRISE2_bit;
sbit GSM_Stat at PORTE.B0; // Indicates if the module is powered on
sbit GSM_Stat_Dir at TRISE0_bit;
#else //SimPLCv1
sbit GSM_Pwr_Key at LATE0_bit;
sbit GSM_Pwr_Key_Dir at TRISE0_bit;
sbit GSM_Stat at PORTE.B2; // Indicates if the module is powered on
sbit GSM_Stat_Dir at TRISE2_bit;
#endif
#ifdef gsm_debug_state
char *gsmDebugStateStrPtr;
#endif
//</GSM>

#ifndef lcd_disable
//<LCD>
sbit LCD_RS at LATA6_bit;
sbit LCD_EN at LATA7_bit;
sbit LCD_D4 at LATA0_bit;
sbit LCD_D5 at LATA1_bit;
sbit LCD_D6 at LATA2_bit;
sbit LCD_D7 at LATA3_bit;
sbit LCD_BL at LATA5_bit;

sbit LCD_RS_Direction at TRISA6_bit;
sbit LCD_EN_Direction at TRISA7_bit;
sbit LCD_D4_Direction at TRISA0_bit;
sbit LCD_D5_Direction at TRISA1_bit;
sbit LCD_D6_Direction at TRISA2_bit;
sbit LCD_D7_Direction at TRISA3_bit;
sbit LCD_BL_Direction at TRISA5_bit;

unsigned int wrdLcdTmr; // Timer to refresh the LCD
char strLcd[17]; // Text to be displayed on the LCD
//</LCD>
#endif

#ifndef usb_disable
//<USB>
char USBin[64] absolute 0x0500;
char USBout[64] absolute 0x0540;
//</USB>
#endif

// -- Variables --
//GSM SMS
char strMsgSendTxt[161]; //Message text to send
char strDestID[15] = ""; // Destination ID (will be taken from first
                         // missed-call / SMS if left blank)
//GSM GPRS
bit bitGsmGprsFirstLine;
//PortB.7 input
unsigned int wrdRB7Tmr = 65535;  

void interrupt() { 
  #ifndef usb_disable
  // <USB Interrupt>
  if (USBIF_bit) {
    USB_Interrupt_Proc();
  }
  // </USB Interrupt>  
  #endif
  // <Timer0 Interrupt>
  if (T0IF_bit) { // 1ms Interrupt
    // Timer Calculator (www.libstock.com/projects/view/398/timer-calculator)
    // can help to calculate the correct values for TMR0H and TMR0L,
    // depending on MCU clock speed
    TMR0H = 0xD1;
    TMR0L = 0x20; 
    gsm1msPing();
    if (wrdRB7Tmr < 65535) {wrdRB7Tmr++;}
    #ifndef lcd_disable
    if (wrdLcdTmr) {wrdLcdTmr--;}
    #endif
    T0IF_bit = 0; // Clear interrupt flag
  }
  // </Timer0 Interrupt>
}

void setup() {
  OSCCON = 0b01110000; //8MHz
  PLLEN_bit = 1; //Enable PLL (48MHz now)
  TRISD = 0b00000000; //PortD all outputs
  LATD = 0b00000000; //PortD all off by default
  //TRISA = 0b00000000; //PortA all outputs
  #ifndef lcd_disable
  LCD_BL_Direction = 0; //Output
  #endif
  TRISB = 0b11111111; //PortB all inputs
  GSM_Pwr_Key_Dir = 0;
  GSM_Stat_Dir = 1;
  ANCON0 = 0b11111111; //All pins digital
  ANCON1 = 0b00011111; //All pins digital
  RBPU_bit = 0; //PortB Weak Pull-Ups Enabled
  IPEN_bit = 0; //Interrupt priority disable
  GIE_bit = 1; //Global interrupt enable
  PEIE_bit = 1; //Peripheral interrupt enable
  #ifndef usb_disable
  HID_Enable(&USBin,&USBout); // Enable HID communication
  //delay_ms(1000); // Give USB time to initialise
  #endif
  T0CON = 0b10001000; //Timer0 On, 16-bit, internal clock, pre-scaler not assgnd
  T0IE_bit = 1; //Enable Timer0 overflow interrupts
}   

void startup() {
  #ifdef gsm_debug_state
  #ifndef usb_disable
  gsmDebugStateStrPtr = &USBout;
  #endif
  #endif
  UART1_Init(9600);
  gsmInit();
  gsm_MS_Init();
  gsm_Msg_Init(); // Requires GSM_Msg module
  gsm_GPRS_Init(); // Requires GSM_GPRS module
  RTCC_Init();
  dtmRTCC.Year = 14; dtmRTCC.Month = 1; dtmRTCC.Day = 1; 
  dtmRTCC.Hour = 0; dtmRTCC.Minute = 0; dtmRTCC.Second = 0;
  RTCC_Write();
  bitRTCC_Set = 0;
  #ifndef lcd_disable
  LCD_BL = 1;
  LCD_Init();
  LCD_Cmd(_LCD_CURSOR_OFF);
  LCD_Cmd(_LCD_CLEAR);
  Lcd_Out(1,6,"SimPLC");
  Lcd_Out(2,1,"www.dizzy.co.za");
  wrdLcdTmr = 5000;
  #endif
}

#ifdef gsm_echo_int_rx
void gsmUartRxEcho(char *UartRxLine) {
  #ifndef usb_disable
  strncpyExNewLine(&USBout, UartRxLine, 64);
  HID_Write(&USBout, 64);
  #endif
}
#endif


#ifdef gsm_debug_state
void gsmDebugStateStrReady() {
  #ifndef usb_disable
  HID_Write(&USBout, 64);
  #endif
}
#endif

void rtccSet() {
  #ifdef gsm_debug_state
  #ifndef usb_disable
  strcpy(&USBout, "Setting RTCC: ");
  RTCC_GenerateString(&dtmRTCC, &USBout + 14);
  strcat(&USBout, "\r\n");
  gsmDebugStateStrReady();
  #endif
  #endif
  RTCC_Write();
  bitRTCC_Set = 1;
}

void ByteToBin(char input, char *output) {
  char mask;
  for (mask = 0b10000000; mask != 0; mask = mask >> 1) {
    if (input & mask) *output = '1'; else *output = '0';
    output++;
  }
  *output = 0; // Mark the end of the string
}

void generateStatusReport(char *dest) {
  strcpy(dest, "PORTA:");
  dest += 6;
  ByteToBin(PORTA, dest);
  dest += 8;
  *dest++ = ',';
  strcpy(dest, "PORTB:");
  dest += 6;
  ByteToBin(PORTB, dest);
  dest += 8;
  *dest++ = ',';
  strcpy(dest, "LATD:");
  dest += 5;
  ByteToBin(LATD, dest);
}

void msgSend() {   
  #ifndef usb_disable
  strcpy(&USBout, "Sending message to ");
  strcat(&USBout, &strDestID);
  strcat(&USBout, ":\r\n");
  HID_Write(&USBout, 64);
  strncpyExNewLine(&USBout, &strMsgSendTxt, 64);
  HID_Write(&USBout, 64);
  #endif
  if (strDestID[0] != 0) {
    if (gsmMsgSendPending()) {gsmMsgSendCancel();} // Cancel any message waiting to be sent
    gsmMsgSend(&strMsgSendTxt, &strDestID);
  }  
}

void gsmMissedCall(char *OriginatorID) {
  #ifndef lcd_disable
  LCD_Cmd(_LCD_Clear);
  LCD_Out(1,1,OriginatorID);
  LCD_Out(2,1,"(Missed call)");
  wrdLcdTmr = 2500;
  #endif
  LATD0_bit = !LATD0_bit; // Toggle PortD.0
  if (strDestID[0] == 0) {
    strcpy(&strDestID, OriginatorID);
  }
}

void gsmMsgRcvd(char *MessageText, char *OriginatorID) {
  #ifndef lcd_disable
  LCD_Cmd(_LCD_Clear);
  LCD_Out(1,1,OriginatorID);
  LCD_Out(2,1,MessageText);
  wrdLcdTmr = 2500;
  #endif
  if (strDestID[0] == 0) { // If the destination ID has not yet been set then
    strcpy(&strDestID, OriginatorID); // Copy it from the originator ID
  }                                   // (reply to sender)
  if (gsmMsgJustArrived() == 1) {
    // If the message has just arrived, then the time/date extracted from it
    // are probably good
    memcpy(&dtmRTCC, &dtmGsmEvent, 6);
    rtccSet();
    gsmDateTimeWrite();
  } 
  lcase(MessageText); // Convert the entire message to lower case
  // Interpret the message data
  if (*(MessageText + 1) == 'n') { // Looking for "on"
    LATD = 0xFF;
  } else if (*(MessageText + 1) == 'f') { // Looking for "off"
    LATD = 0x00;
  } else if (*MessageText == 'c') { // Query clock
    strcpy(&strMsgSendTxt, "SimPLC Date/Time: ");
    RTCC_GenerateString(&dtmRTCC, &strMsgSendTxt + 18);
    msgSend();
  } else if (memcmp(MessageText, "stat", 4) == 0) { //Status report 
    generateStatusReport(&strMsgSendTxt);
    msgSend();    
  } else if (memcmp(MessageText, "please call ", 12) == 0) { // "Please Call Me" 
    LATD1_bit = !LATD1_bit;
    generateStatusReport(&strMsgSendTxt);  
    msgSend();
  }
}

void gsmEvent(char GsmEventType) {
  switch (GsmEventType) {
    case gsmevntPIN_Request:
      //strcpy(pstrGsmEventData, "0000");
      break;
    case gsmevntPIN_Fail:
      #ifndef usb_disable
      strcpy(&USBout, "PIN entry failed\r\n");
      HID_Write(&USBout, 64);
      #endif
      break;
    case gsmevntMissedCall:
      gsmMissedCall(pstrGsmEventOriginatorID);
      break;
    case gsmevntMsgRcvd:
      gsmMsgRcvd(pstrGsmEventData, pstrGsmEventOriginatorID);
      break;
    case gsmevntMsgSent:
      #ifndef usb_disable
      strcpy(&USBout, "Message sent\r\n");
      HID_Write(&USBout, 64);
      #endif
      break;
    case gsmevntMsgDiscarded:
    case gsmevntMsgSendFailed:
      #ifndef usb_disable
      strcpy(&USBout, "Message send failed\r\n");
      HID_Write(&USBout, 64);
      #endif
      break;
    case gsmevntGprsHttpResponseLine:
      if (bitGsmGprsFirstLine) {
        #ifndef lcd_disable
        LCD_Cmd(_LCD_Clear);
        LCD_Out(1,1,"HTTP Result:");
        LCD_Out(2,1,pstrGsmEventData);
        wrdLcdTmr = 2500;
        #endif
        bitGsmGprsFirstLine = 0;
      }
      #ifndef usb_disable
      strcpy(&USBout, "HTTP response line: ");
      HID_Write(&USBout, 64);
      strcpyTillChar(pstrGsmEventData, &USBout, 0, 61);
      strcat(&USBout, "\r\n");
      HID_Write(&USBout, 64);
      #endif
      break;
    case gsmevntGprsHttpResultErr:
      #ifndef lcd_disable
      LCD_Cmd(_LCD_Clear);
      LCD_Out(1,1,"HTTP Error:");
      LCD_Out(2,1,pstrGsmEventData);
      wrdLcdTmr = 2500;
      #endif
      #ifndef usb_disable
      strcpy(&USBout, "HTTP response error, code ");
      strcat(&USBout, pstrGsmEventData);
      strcat(&USBout, "\r\n");
      HID_Write(&USBout, 64);
      #endif
      break;
    case gsmevntGprsFailed:
      #ifndef lcd_disable
      LCD_Cmd(_LCD_Clear);
      LCD_Out(1,1,"GPRS Failed");
      wrdLcdTmr = 2500;
      #endif
      #ifndef usb_disable
      strcpy(&USBout, "GPRS failed\r\n");
      HID_Write(&USBout, 64);
      #endif
      break;
    case gsmevntDateTimeRead:
      memcpy(&dtmRTCC, &dtmGsmEvent, 6);
      rtccSet();
      break;
    case gsmevntDateTimeWrite:
      RTCC_Read();
      memcpy(&dtmGsmEvent, &dtmRTCC, 6);
      break;
    case gsmevntIMEI_Read:
      #ifndef usb_disable
      strcpy(&USBout, "IMEI: ");
      strcat(&USBout, pstrGsmEventData);
      strcat(&USBout, "\r\n");
      HID_Write(&USBout, 64);
      #endif
      break;
  }
}

#ifndef lcd_disable
void lcdRTCC() { // Display the RTCC date/time on the LCD
  RTCC_Read();
  memset(&strLCD, ' ', 16);
  strLCD[16] = 0;
  RTCC_GenerateTimeString(&dtmRTCC, &strLcd + 4, 1);
  strLcd[12] = ' ';
  Lcd_Out(2,1,&strLcd);
  RTCC_GenerateDateStringDMY(&dtmRTCC, &strLcd + 3);
  strLcd[13] = ' ';
  Lcd_Out(1,1,&strLcd);
}
#endif

void main() {
  setup();
  startup();
  gsmDateTimeRead(); // Request a read of the GSM modules date/time
  while (1) {
    if (OERR1_bit) { // Check for UART rx overflow
      // UART rx overflow can occur if the time between calls to gsmPoll()
      // is too large
      //#ifdef gsm_debug_state
      #ifndef usb_disable
      strcpy(&USBout, "UART rx overflow occurred.\r\n");
      HID_Write(&USBout, 64);
      #endif
      //#endif
    }
    gsmPoll();

    #ifndef lcd_disable
    if (wrdLcdTmr == 0) {
      lcdRTCC();
      wrdLcdTmr = 1000;
    }
    #endif
    
    #ifndef usb_disable
    if (HID_Read() != 0) {
      #ifdef gsm_echo_ext_rx                                    
      // Pass anything received via USB on to the GSM/GPRS module
      UART1_Write_Text(&USBin);
      #endif
    }
    #endif

    if ((RB7_bit == 0) && (wrdRB7Tmr > 10000)) {
      // Send a message if RB7 is pulled low
      /*strcpy(&strMsgSendTxt, "SimPLC: RB7 pulled low.");
      msgSend();*/
      
      // Perform HTTP operation if RB7 is pulled low
      if (!gsmGprsPending()) {
        bitGsmGprsFirstLine = 1;
        //gsmGprsHttpGet("www.dizzy.co.za/gprs_test.asp");
        gsmGprsHttpPost("www.dizzy.co.za/gprs_test.asp", "unit=SimPLC");
      }
      
      wrdRB7Tmr = 0;
    }
    //gsmPowerSetOnOff(RB7_bit); // Power the GSM module off when RB7 is pulled low
  } // (while)
} // (void)