#include <SensirionI2CScd4x.h>
#include <Wire.h>
#include "esp8266_mqtt_setup.h"

const char* topicTemperature = "aquaponics/air/temperature";
const char* topicHumidity = "aquaponics/air/humidity";
const char* topicCO2 = "aquaponics/air/co2";

const char* clientName = "AirSensorClient";

SensirionI2CScd4x co2Sensor;

const int MEASUREMENT_PERIOD = 5000;
unsigned long lastMeasurement = 0;

void setup() {
  Serial.begin(115200);

  Wire.begin();
  co2Sensor.begin(Wire);
  ResetCO2Measurement();

  setup_wifi();
  
  setup_mqtt();
}

void loop() {
  if (!mqttClient.connected()) {
    reconnect(clientName);
  }
  mqttClient.loop();

  unsigned long currentMillis = millis();
  if (currentMillis - lastMeasurement >= MEASUREMENT_PERIOD) {
    lastMeasurement = currentMillis;
    TakeCO2Measurement();
  }

  delay(500);
}

void TakeCO2Measurement() {
  uint16_t co2;
  float temperature;
  float humidity;
  char errorMessage[256];

  int error = co2Sensor.readMeasurement(co2, temperature, humidity);
  if (error) {
    Serial.print("Error trying to execute readMeasurement(): ");
    errorToString(error, errorMessage, 256);
    Serial.println(errorMessage);
  } else {
    Serial.print("CO2: ");
    Serial.println(co2);
    Serial.print("Temperature:");
    Serial.println(temperature);
    Serial.print("Humidity:");
    Serial.println(humidity);

    mqttClient.publish(topicCO2, String(co2).c_str());
    mqttClient.publish(topicTemperature, String(temperature).c_str());
    mqttClient.publish(topicHumidity, String(humidity).c_str());
  }
}

void ResetCO2Measurement() {
  char errorMessage[256];

  // Stop potentially previously started measurement
  uint16_t error = co2Sensor.stopPeriodicMeasurement();
  if (error) {
      Serial.print("Error trying to execute stopPeriodicMeasurement(): ");
      errorToString(error, errorMessage, 256);
      Serial.println(errorMessage);
  }

  uint16_t serial0;
  uint16_t serial1;
  uint16_t serial2;
  error = co2Sensor.getSerialNumber(serial0, serial1, serial2);
  if (error) {
      Serial.print("Error trying to execute getSerialNumber(): ");
      errorToString(error, errorMessage, 256);
      Serial.println(errorMessage);
  } else {
      printSerialNumber(serial0, serial1, serial2);
  }

  // Start Measurement
  error = co2Sensor.startPeriodicMeasurement();
  if (error) {
      Serial.print("Error trying to execute startPeriodicMeasurement(): ");
      errorToString(error, errorMessage, 256);
      Serial.println(errorMessage);
  }

  Serial.println("Waiting for first measurement... (5 sec)");
  delay(5000);
}

void printUint16Hex(uint16_t value) {
  Serial.print(value < 4096 ? "0" : "");
  Serial.print(value < 256 ? "0" : "");
  Serial.print(value < 16 ? "0" : "");
  Serial.print(value, HEX);
}

void printSerialNumber(uint16_t serial0, uint16_t serial1, uint16_t serial2) {
  Serial.print("Serial: 0x");
  printUint16Hex(serial0);
  printUint16Hex(serial1);
  printUint16Hex(serial2);
  Serial.println();
}