#define TINY_GSM_MODEM_SIM900

#include <TinyGsmClient.h>
#include <PubSubClient.h>

#include <OneWire.h>                 
#include <DallasTemperature.h>
OneWire ourWire(9);                
DallasTemperature sensors(&ourWire); 
const int sensorPin= A0; 

// Set serial for debug console (to the Serial Monitor, default speed 115200)
#define SerialMon Serial

// Use Hardware Serial on Mega, Leonardo, Micro
#define SerialAT Serial1

// or Software Serial on Uno, Nano
#include <SoftwareSerial.h>
//SoftwareSerial SerialAT(7, 8); // RX, TX

//#define DUMP_AT_COMMANDS
#define TINY_GSM_DEBUG SerialMon

#ifdef DUMP_AT_COMMANDS
  #include <StreamDebugger.h>
  StreamDebugger debugger(SerialAT, SerialMon);
  TinyGsm modem(debugger);
#else
  TinyGsm modem(SerialAT);
#endif

// Your GPRS credentials
// Leave empty, if missing user or pass
const char apn[]  = "i.euskaltel.mobi";
const char user[] = "";
const char pass[] = "";

TinyGsmClient client(modem);
PubSubClient mqttClient(client);

float temp_in=0;
float temp_out=0;

volatile long count_timer2_tics = 0;
volatile bool flag_timer2_sec = false;

long last_time = 0;

void setup() {
  delay(1000);
  sensors.begin();                  

  // Set console baud rate
  SerialMon.begin(115200);
  delay(10);

  // Set GSM module baud rate
  TinyGsmAutoBaud(SerialAT);
  delay(1000); 

  // Restart takes quite some time
  // To skip it, call init() instead of restart()
  SerialMon.println("Initializing modem...");
  modem.init();

  String modemInfo = modem.getModemInfo();
  SerialMon.print("Modem: ");
  SerialMon.println(modemInfo);

  // Unlock your SIM card with a PIN
  modem.simUnlock("3948");

  SerialMon.print("Waiting for network...");
  if (!modem.waitForNetwork()) {
    SerialMon.println(" fail");
    while (true);
  }
  SerialMon.println(" OK");

  SerialMon.print("Connecting to ");
  SerialMon.print(apn);
  if (!modem.gprsConnect(apn, user, pass)) {
    SerialMon.println(" fail");
    while (true);
  }
  SerialMon.println(" OK");

  const char* server = "mqtt.thingspeak.com"; 
  mqttClient.setServer(server, 1883); // Set the MQTT broker details.
  mqttClient.setClient(client);
  mqttConnect();

  set_timer2_interrupt();
}

void loop() {
  if(millis() - last_time > 1000) {
    mqttClient.loop();
    if(!mqttClient.connected()) {
      mqttConnect();
    }
    last_time = millis();
  }

  if(flag_timer2_sec) {
    readSensors();
    mqttPublish();
    flag_timer2_sec = false;
  }
}

void mqttConnect() {
  char mqttUserName[] = "maideraran"; 
  char mqttPass[] = "Q3DB3R7FTTOK5ML2";  // MQTT API Key from Account > MyProfile
  // For random generation of client ID.
  const char alphanum[] PROGMEM = "0123456789"
                                  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                                  "abcdefghijklmnopqrstuvwxyz";  
                                
  do {
    Serial.print(F("Attempting MQTT connection..."));
    
    // Generate ClientID
    char clientID[9];
    for(int i = 0; i < 8; i++) {
      clientID[i] = alphanum[random(51)];
    }
    clientID[8]='\0';
  
    // Connect to the MQTT broker
    if(mqttClient.connect(clientID, mqttUserName, mqttPass)) {
      Serial.println(F("connected"));
    } else {
      // See http://pubsubclient.knolleary.net/api.html#state for the failure code explanation.
      Serial.print(F("failed, rc="));
      Serial.print(mqttClient.state());
      delay(1000);
    }
    Serial.println();
  } while(!mqttClient.connected()); 
}

boolean mqttPublish() {
  // Create data string to send to ThingSpeak
  String data = "field1=" + String(temp_in, 1) + "&field2=" + String(temp_out, 1); 
  int length = data.length();
  char msgBuffer[length+1];
  data.toCharArray(msgBuffer, length+1);
  msgBuffer[length] = '\0';
  Serial.println(msgBuffer);

  long channelID = 579800;
  char writeAPIKey[] = "DXXRCZWDU6N6F73F";  // channel's Write API Key.
  // Create a topic string and publish data to ThingSpeak channel feed. 
  String topicString = "channels/" + String(channelID) + "/publish/" + String(writeAPIKey);
  length = topicString.length();
  char topicBuffer[length+1];
  topicString.toCharArray(topicBuffer, length+1);
 
  if(mqttClient.publish(topicBuffer, msgBuffer)) {
    Serial.println(F("Publish succeeded"));
  } else {
    Serial.println(F("Publish failed"));
  }
  Serial.println();
}

void readSensors() {
  sensors.requestTemperatures();       
  temp_in = sensors.getTempCByIndex(0); 
  
  delay(1000);                          
  
  int value = analogRead(sensorPin);
  float millivolts = (value / 1023.0) * 5000;
  float celsius = millivolts / 10; 
  temp_out = celsius;
}

void set_timer2_interrupt() {
  cli();

  // Timer2 clk/1024 --> t_BIT_TCNT2: 1 / (16*10^6 / 1024) = 64us
  // OCR2A? --> OCR2A * 64us = 1s --> OCR2A = 15625
  // OCR2A 8bit-eko erregistroa da... --> 15625/256 ~ 61.03 --> 1s
  //                                                 915.45 --> 15s  
  // Table 20-8. Waveform Generation Mode Bit Description
  // Mode WGM22 WGM21 WGM20 Timer/Counter_Mode_of_Operation  TOP
  // 0    0     0     0     Normal                           0xFF

  // 20.10.1 TCCR2A – Timer/Counter2 Control Register A
  // COM2A1 COM2A0 COM2B1 COM2B0 X X WGM21 WGM20
  // Table 20-2. Compare Output Mode, non-PWM
  // COM2A1 COM2A0 Description
  // 0      0      Normal port operation, OC2A disconnected
  TCCR2A &= (~(_BV(COM2A1)) & ~(_BV(COM2A0)));
  TCCR2A &= (~(_BV(WGM21)) & ~(_BV(WGM20)));

  // 20.10.2 TCCR2B – Timer/Counter1 Control Register B
  // X X - - WGM22 CS22 CS21 CS20
  TCCR2B &= ~(_BV(WGM22));

  // Table 20-9. Clock Select Bit Description
  // CS22 CS21 CS20 Description
  // 1    1    1    clk/1024
  TCCR2B |= (_BV(CS22) | _BV(CS21) | _BV(CS20));

  TCNT2 = 0;
  TIMSK2 |= 1 << OCIE2A;

  sei();
}

ISR(TIMER2_COMPA_vect) {
  if (count_timer2_tics == 917) {
    //Serial.print("TCNT2: "); Serial.print((millis() - last_time) / 1000.0); Serial.print("s\t");
    //last_time = millis();
    count_timer2_tics = 0;
    flag_timer2_sec = true;
  } else {
    count_timer2_tics++;
  }
}

