#define DHTPIN 2
#define DHTTYPE DHT22

#define rojo 13
#define verde 12
#define azul 14

#define ENCODER_CLK 27





#include<Wire.h>
#include <DHT.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <ArduinoOTA.h>
const char* ssid         = "Wokwi-GUEST";
const char* password     = "";
const char* mqttServer   = "mqtt.thingsboard.cloud";
const int   mqttPort     = 1883;
const char* mqttUser     = "amerejil";       //User name
const char* mqttPassword = "amerejil";        //Password
const char* mqttClient   = "amerejil_test"; //ID

WiFiClient espClient;
PubSubClient client(espClient);
const int capacity = JSON_OBJECT_SIZE(21);
StaticJsonDocument<capacity> doc;


//---------- MPU6050 Measurement & Filtering Range ----------------------------------------------
#define AFS_SEL 2  // Accelerometer Configuration Settings   AFS_SEL=2, Full Scale Range = +/- 8 [g]
#define DLPF_SEL  0  // DLPF Configuration Settings  Accel BW 260Hz, Delay 0ms / Gyro BW 256Hz, Delay 0.98ms, Fs 8KHz 

//---------- Variables for gravity --------------------------------------------------------------
const int MPU_ADDR=0x68;  // I2C address of the MPU-6050
int16_t AcX,AcY,AcZ;  // Accelerometer values
long Cal_AcX, Cal_AcY, Cal_AcZ; // Calibration values
float GAcX, GAcY, GAcZ; // Convert accelerometer to gravity value
float Min_GAcX=0, Max_GAcX=0, PtoP_GAcX, Min_GAcY=0, Max_GAcY=0, PtoP_GAcY, Min_GAcZ=0, Max_GAcZ=0, PtoP_GAcZ; // Finding Min, Max & Peak to Peak of gravity value
float Min = 0, Max = 0; // Initial value of Min, Max
int cnt; // Count of calibration process
float Grvt_unit; // Gravity value unit
long period, prev_time; // Period of calculation


//
float temperature=0;
float temperaturaMin=50;
float temperaturaMax=65;

float rpmMin=20;
float rpmMax=50;

float humidity=0;
float humedadMin=40;
float humedadMax=60;

float vibracionMin=0.1;
float vibracionMax=0.2;

DHT dht(DHTPIN, DHTTYPE);

int contador = 1;
char mensagem[30];
volatile unsigned tiempoInterrupcionActual=0;
volatile unsigned tiempoInterrupcionAnterior=0;
volatile unsigned deltaTiempoInterrupciones=0;
double frecuencia =0;
int N=20; //Numero de ranuras
double rpm=0;
void IRAM_ATTR leerEncoder(){

  frecuencia=(1000)/(double)deltaTiempoInterrupciones;
  tiempoInterrupcionAnterior=tiempoInterrupcionActual;
  
}

unsigned long TiempoAhora = 0;

void setup() {
  pinMode(ENCODER_CLK, INPUT);
  attachInterrupt(digitalPinToInterrupt(ENCODER_CLK), leerEncoder, RISING);
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  dht.begin();
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.println("Iniciando conexión a red wifi..");
  }
  Serial.println("Conectado a la red WiFi!");
  Wire.begin();
  init_MPU6050();
  Gravity_Range_Option();
  Calib_MPU6050(); // Calculating calibration value

  pinMode(rojo, OUTPUT);
  pinMode(verde, OUTPUT);
  pinMode(azul, OUTPUT);

  
  
}

void loop() {

  
  mostrarAlarma();
  tiempoInterrupcionActual=millis();
  deltaTiempoInterrupciones= tiempoInterrupcionActual-tiempoInterrupcionAnterior;
  
  if(millis() > TiempoAhora + 2000){
  TiempoAhora = millis();
  ReadDate_MPU6050();
  
 
  rpm=frecuencia*(60/N);
  Calc_Grvt();

  //Display_Grvt();
  Min_GAcX = 0;
    Max_GAcX = 0;
    Min_GAcY = 0;
    Max_GAcY = 0;
    Min_GAcZ = 0;
    Max_GAcZ = 0;
  reconectabroker();
  temperature = dht.readTemperature();
  humidity = dht.readHumidity();
  doc["temp"] = temperature;
  doc["hum"]=humidity;
  doc["vel"]=rpm;
  doc["vibrX"]=PtoP_GAcX;
  doc["vibrY"]=PtoP_GAcY;
  doc["vibrZ"]=PtoP_GAcZ;

  Serial.println(rpm);
  String string1 = "v1/devices/me/telemetry";
  char Buf1[256];  string1.toCharArray(Buf1, 256);
  Serial.println(Buf1);

  char docBuffer1[256];
  size_t n = serializeJson(doc, docBuffer1);
  Serial.println(docBuffer1);
  client.publish(Buf1, docBuffer1, n);
  sprintf(mensagem, "MQTT ESP32 - mensaje no. %d", contador);
  Serial.print("Mensagem enviada: ");
  Serial.println(mensagem);
  
  client.publish(Buf1, mensagem);
  Serial.println("Mensaje enviado satisfactoriamente...");
  contador++; 
  }
  
  
}

void reconectabroker() {
  client.publish("v1/devices/me/telemetry", mensagem);
  Serial.println(mensagem);
  String string1 = "v1/devices/me/telemetry";
  char Buf1[256];  
  string1.toCharArray(Buf1, 256);
  Serial.println(Buf1);
  client.setServer(mqttServer, mqttPort);
  
  while (!client.connected()) {
    Serial.println("Conectando a broker MQTT...");  
    if (client.connect(mqttClient, mqttUser, mqttPassword )) {
      Serial.println("Conectado a broker!");
    }else {
      Serial.print("Error al conectar al broker - Estado: ");
      Serial.println(client.state());
      
    }
  }
}


void init_MPU6050(){
  //MPU6050 Initializing & Reset
  Wire.beginTransmission(MPU_ADDR);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);

  //MPU6050 Clock Type
  Wire.beginTransmission(MPU_ADDR);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0x03);     // Selection Clock 'PLL with Z axis gyroscope reference'
  Wire.endTransmission(true);

  //MPU6050 Accelerometer Configuration Setting
  Wire.beginTransmission(MPU_ADDR);
  Wire.write(0x1C);  // Accelerometer Configuration register
  if(AFS_SEL == 0) Wire.write(0x00);     // AFS_SEL=0, Full Scale Range = +/- 2 [g]
  else if(AFS_SEL == 1) Wire.write(0x08);     // AFS_SEL=1, Full Scale Range = +/- 4 [g]
  else if(AFS_SEL == 2) Wire.write(0x10);     // AFS_SEL=2, Full Scale Range = +/- 8 [g]
  else  Wire.write(0x18);     // AFS_SEL=3, Full Scale Range = +/- 10 [g]
  Wire.endTransmission(true);

  //MPU6050 DLPF(Digital Low Pass Filter)
  Wire.beginTransmission(MPU_ADDR);
  Wire.write(0x1A);  // DLPF_CFG register
  if(DLPF_SEL == 0) Wire.write(0x00);     // Accel BW 260Hz, Delay 0ms / Gyro BW 256Hz, Delay 0.98ms, Fs 8KHz 
  else if(DLPF_SEL == 1)  Wire.write(0x01);     // Accel BW 184Hz, Delay 2ms / Gyro BW 188Hz, Delay 1.9ms, Fs 1KHz 
  else if(DLPF_SEL == 2)  Wire.write(0x02);     // Accel BW 94Hz, Delay 3ms / Gyro BW 98Hz, Delay 2.8ms, Fs 1KHz 
  else if(DLPF_SEL == 3)  Wire.write(0x03);     // Accel BW 44Hz, Delay 4.9ms / Gyro BW 42Hz, Delay 4.8ms, Fs 1KHz 
  else if(DLPF_SEL == 4)  Wire.write(0x04);     // Accel BW 21Hz, Delay 8.5ms / Gyro BW 20Hz, Delay 8.3ms, Fs 1KHz 
  else if(DLPF_SEL == 5)  Wire.write(0x05);     // Accel BW 10Hz, Delay 13.8ms / Gyro BW 10Hz, Delay 13.4ms, Fs 1KHz 
  else  Wire.write(0x06);     // Accel BW 5Hz, Delay 19ms / Gyro BW 5Hz, Delay 18.6ms, Fs 1KHz 
  Wire.endTransmission(true);
}

void Gravity_Range_Option(){
  switch(AFS_SEL) { // Selecting Gravity unit value
    case 0:
      Grvt_unit = 16384;
      break;
    case 1:
      Grvt_unit = 8192;
      break;
    case 2:
      Grvt_unit = 4096;
      break;
    case 3:
      Grvt_unit = 3276.8;
      break;
  }
}

void Calib_MPU6050() {  
  for(int i = 0 ; i < 2000 ; i++) { // Summing Iteration for finding calibration value
    if(i % 200 == 0) {  // Display progress every 200 cycles
      cnt++;
      if(cnt == 1)  { // Characters to display first
        Serial.print("Calculating .");
        
      }
      else  { // Display progress by point
        Serial.print(".");
        
      }      
    }    
    
    ReadDate_MPU6050(); // Read Accelerometer data
    
    delay(10);

    // Sum data
    Cal_AcX += AcX;
    Cal_AcY += AcY;
    Cal_AcZ += AcZ;
  }

  // Average Data
  Cal_AcX /= 2000;
  Cal_AcY /= 2000;
  Cal_AcZ /= 2000;

  // Serial Print
  Serial.println("");
  Serial.println("End of Calculation");
  Serial.print("Cal_AcX = "); Serial.print(Cal_AcX);
  Serial.print(" | Cal_AcY = "); Serial.print(Cal_AcY);
  Serial.print(" | Cal_AcZ = "); Serial.println(Cal_AcZ);


}


void ReadDate_MPU6050() {
  Wire.beginTransmission(MPU_ADDR);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  //Wire.requestFrom(MPU_ADDR,14,true);  // request a total of 14 registers
  Wire.requestFrom(MPU_ADDR,6,true);  // request a total of 14 registers
  AcX=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)     
  AcY=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ=Wire.read()<<8|Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  
}


void Calc_Grvt() {
  AcX = (AcX - Cal_AcX);  // Calibrated Accelerometer value
  AcY = (AcY - Cal_AcY);  // Calibrated Accelerometer value
  AcZ = (AcZ - Cal_AcZ);  // Calibrated Accelerometer value
  
  GAcX = AcX / Grvt_unit; // Converting the Calibrated value to Gravity value
  GAcY = AcY / Grvt_unit; // Converting the Calibrated value to Gravity value
  GAcZ = AcZ / Grvt_unit; // Converting the Calibrated value to Gravity value

  //---------- Calculating Min, Max & Peak to Peak of Gravity --------------------------------------

  Min_GAcX = min(Min_GAcX, GAcX);
  Max_GAcX = max(Max_GAcX, GAcX);
  PtoP_GAcX = Max_GAcX - Min_GAcX;

  Min_GAcY = min(Min_GAcY, GAcY);
  Max_GAcY = max(Max_GAcY, GAcY);
  PtoP_GAcY = Max_GAcY - Min_GAcY;

  Min_GAcZ = min(Min_GAcZ, GAcZ);
  Max_GAcZ = max(Max_GAcZ, GAcZ);
  PtoP_GAcZ = Max_GAcZ - Min_GAcZ;
}

void Display_Grvt() {
  //---------- Serial print ----------------------------------------------------------------------
  Serial.print("PtoP_GAcX= " + String(PtoP_GAcX));
  Serial.print(" |PtoP_GAcY= " + String(PtoP_GAcY));
  Serial.println(" |PtoP_GAcZ= " + String(PtoP_GAcZ));

  
  period = millis() - prev_time;
  
  if(period > 1000) {
    
    prev_time = millis();
    

    Min_GAcX = 0;
    Max_GAcX = 0;
    Min_GAcY = 0;
    Max_GAcY = 0;
    Min_GAcZ = 0;
    Max_GAcZ = 0;
  }
}

void mostrarAlarma(){
  if (temperature<temperaturaMax && temperature>temperaturaMin 
      && PtoP_GAcX<0.2 && humidity>40 && humidity<60 && rpm<rpmMax && rpm>rpmMin)
  {
    digitalWrite(rojo, LOW);
    digitalWrite(azul, LOW);
    digitalWrite(verde, HIGH);
  }
  else
  {
    digitalWrite(azul, LOW);
    digitalWrite(verde, LOW);
    digitalWrite(rojo, HIGH);
    
  }

}