//todo add check for if i2c device is present to change error code

#include <avr/power.h>
#include <SoftwareSerial.h>
#include <LowPower.h>

#define SIMCOM_7000 // SIM7000A/C/E/G
#define BAUDRATE 9600 // MUST be below 19200 (for stability) but 9600 is more stable

#define CHARBUFF 196 //SIM7000 serial response buffer //longer than 255 will cause issues

// For SIM7000 BoSL board
#define PWRKEY 4
#define DTR 5 // Connect with solder jumper
#define BOSL_RX 9 // Microcontroller RX
#define BOSL_TX 8 // Microcontroller TX

//Site specific config
#define SITEID "\"MQTT_BOSLBOAD\""
#define MQTT_DOMAIN "\"domain_name_here\""

//millis timer variable 
extern volatile unsigned long timer0_millis;

//gobal variables 
char response[CHARBUFF]; //sim7000 serial response buffer
String dataStr; //mqtt_publish URL


//SIM7000 serial object
SoftwareSerial simCom = SoftwareSerial(BOSL_RX, BOSL_TX);


void setup() {

	//ensure sim is in the off state
	simOff();

	//begin serial
	Serial.begin(BAUDRATE);
	simCom.begin(BAUDRATE);

	Serial.println("Initialising SIM 7000");
	//initialise sim (on arduino startup only)
	simOn();
	simInit();
		
	netReg();
	 ///
	netUnreg();

	simOff();
}
    
void loop() {
 
	simOn();

	netUnreg();

	mqtt_publish(); 

	simOff();
	
    
  
    Serial.println("Sleep");
	Serial.println();
	
	delay(6000);
	// Sleepy(60);
}


////SLEEPS FOR SET TIME////
void Sleepy(uint16_t tsleep){ //Sleep Time in seconds
    
    simCom.flush(); // must run before going to sleep
 	
    Serial.flush(); // ensures that all messages have sent through serial before arduino sleeps

    LowPower.powerDown(SLEEP_4S, ADC_OFF, BOD_OFF); //8 seconds dosen't work on the 8mhz
    //advance millis timer as it is paused in sleep
    noInterrupts();
    timer0_millis += 4000;
    interrupts(); 
    
    
    while(tsleep >= 8){
        LowPower.powerDown(SLEEP_4S, ADC_OFF, BOD_OFF); //8 seconds dosen't work on the 8mhz
        //advance millis timer as it is paused in sleep
        noInterrupts();
        timer0_millis += 4000;
        interrupts();
        
        tsleep -= 4;
    }
}

////mqtt_publishes////
void mqtt_publish(){
    
    dataStr = "";
    dataStr += "&T=";
    dataStr += 6;
    dataStr += "&D=";
    dataStr += 9;
    dataStr += "&V=";
    dataStr += 3;
    dataStr += "&EC=";
    dataStr += 1;
    
    netReg();
        
	//close open bearer
    sendATcmd(F("AT+SAPBR=2,1"), "OK",1000);
    if (strstr(response, "1,1") == NULL){
        if (strstr(response, "1,3") == NULL){
        sendATcmd(F("AT+SAPBR=0,1"), "OK",1000);
        }
        sendATcmd(F("AT+SAPBR=3,1,\"APN\",\"simbase\""), "OK",1000); //set bearer apn
        sendATcmd(F("AT+SAPBR=1,1"), "OK",1000);
    }
	sendATcmd(F("AT+CNACT=1,\"simbase\""), "OK",1000);
	
    
    sendATcmd(F("AT+SMCONF=\"URL\"," MQTT_DOMAIN), "OK",1000);
    sendATcmd(F("AT+SMCONF=\"CLIENTID\"," SITEID), "OK",1000);
    sendATcmd(F("AT+SMCONN"), "OK",10000);
	
	char pub_at[] = "AT+SMPUB=\"pluto\",%d   ,0,1";
	sprintf(pub_at, pub_at, dataStr.length());
    sendATcmd(pub_at, ">",1000);
	// delay(500);
  
    sendATcmd(dataStr, "OK",1000);
   
   // sendATcmd(F("AT+SMCONN=0"), "200",2000);
  //close the bearer connection
    sendATcmd(F("AT+SAPBR=0,1"), "OK",1000);
    
    netUnreg();

}




////power down cellular functionality////
void netUnreg(){
    sendATcmd(F("AT+CFUN=0"), "OK", 1000);
}

////register to network////
void netReg(){
     sendATcmd(F("AT+CFUN=1"), "OK", 1000);//enable the modem
    
     xDelay(2000);
     sendATcmd(F("AT+CGDCONT=1,\"IP\",\"simbase\""), "OK", 2000);//set IPv4 and apn
     if(sendATcmd(F("AT+CREG?"), "+CREG: 0,5", 2000) == 0){//check that modem is registered as roaming
         sendATcmd(F("AT+COPS=1,2,50501"), "OK",5000); //register to telstra network
         sendATcmd(F("AT+CREG?"), "+CREG: 0,5", 2000); //check that modem is registered as roaming
     }
}

////sends at command, checks for reply////
bool sendATcmd(String ATcommand, char* expctAns, unsigned int timeout){
    uint32_t timeStart;
    bool answer;
    uint8_t a=0;
    
    do{a++;
    
    Serial.println(ATcommand);
    
    answer=0;
    
    timeStart = 0;


    delay(100);

    while( simCom.available() > 0) {
        simCom.read();    // Clean the input buffer
    }
    
    simCom.println(ATcommand);    // Send the AT command 


    uint8_t i = 0;
    timeStart = millis();
    memset(response, '\0', CHARBUFF);    // Initialize the string

    // this loop waits for the answer

    do{
        if(simCom.available() != 0){    
            response[i] = simCom.read();
            i++;
            // check if the desired answer is in the response of the module
            if (strstr(response, expctAns) != NULL)    
            {
                answer = 1;
            }
        }    
            
            
        
        // Waits for the asnwer with time out
    }
    while((answer == 0) && ((millis() - timeStart) < timeout)); 

    if (expctAns == "0"){
                answer = 1;
            }
    Serial.println(response);
    
    }while(answer == 0 && a < 5);
    
     a = 0;
     return answer;
}



////initialises sim on arduino startup////
void simInit(){
   
      sendATcmd(F("AT+IPR=9600"),"OK",1000);
      
      sendATcmd(F("ATE0"),"OK",1000);
      
      sendATcmd(F("AT&W0"),"OK",1000);
  
}


////powers on SIM7000////
void simOn() {
    //do check for if sim is on
	pinMode(PWRKEY, OUTPUT);
	pinMode(BOSL_TX, OUTPUT);
	digitalWrite(BOSL_TX, HIGH);
	pinMode(BOSL_RX, INPUT_PULLUP);


	digitalWrite(PWRKEY, LOW);
	// See spec sheets for your particular module
	xDelay(1000); // For SIM7000

	digitalWrite(PWRKEY, HIGH);
    xDelay(4000);
}

////powers off SIM7000////
void simOff() {
	//  TX / RX pins off to save power

	digitalWrite(BOSL_TX, LOW);
	digitalWrite(BOSL_RX, LOW);

	digitalWrite(PWRKEY, LOW);

	// See spec sheets for your particular module
	xDelay(1200); // For SIM7000

	digitalWrite(PWRKEY, HIGH);
    xDelay(2000);
}


//like delay but lower power and more dodgy//
void xDelay(uint32_t tmz){
	uint32_t tmzslc = tmz/64;
	//64 chosen as ballance between power savings and time spent in full clock mode
	clock_prescale_set(clock_div_64);
		delay(tmzslc);
	clock_prescale_set(clock_div_1);
	
	cli();
	timer0_millis += 63*tmzslc;	
	sei();
	
	delay(tmz-64*tmzslc);
}
