//#include <HTU21D.h>
#include "DHT.h"
#include <DS18B20.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <ctype.h>

#define DHTPIN 18     // Pin donde está conectado el sensor




//#define DHTTYPE DHT11   // Descomentar si se usa el DHT 11
#define DHTTYPE DHT22   // Sensor DHT22

DHT dht(DHTPIN, DHTTYPE);
/*
void setup() {
  Serial.begin(9600);
  Serial.println("Iniciando...");
}
void loop() {
  delay(2000);
  float h = dht.readHumidity(); //Leemos la Humedad
  float t = dht.readTemperature(); //Leemos la temperatura en grados Celsius
  float f = dht.readTemperature(true); //Leemos la temperatura en grados Fahrenheit
  //--------Enviamos las lecturas por el puerto serial-------------
  Serial.print("Humedad ");
  Serial.print(h);
  Serial.print(" %t");
  Serial.print("Temperatura: ");
  Serial.print(t);
  Serial.print(" *C ");
  Serial.print(f);
  Serial.println(" *F");
}
*/


/*
Relacionado con las pantallas
*/
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library. 
// On an arduino UNO:       A4(SDA), A5(SCL)
// On an arduino MEGA 2560: 20(SDA), 21(SCL)
// On an arduino LEONARDO:   2(SDA),  3(SCL), ...
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x78 ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

//////////////

/*
Relacionado con Virtuino.
*/
#include <WiFi.h>
//---VirtuinoCM  Library settings --------------
#include "VirtuinoCM.h"
VirtuinoCM virtuino;               
#define V_memory_count 32          // the size of V memory. You can change it to a number <=255)
float V[V_memory_count];           // This array is synchronized with Virtuino V memory. You can change the type to int, long etc.
bool debug=true;

//--- SETTINGS conexion Wifi------------------------------------------------
const char* ssid = "ONETEK";         // enter the name (SSID) of your WIFI network
const char* password = "2708511921NGR";         // enter your WIFI network PASSWORD
WiFiServer server(8000);                   // Default Virtuino Server port 
IPAddress ip(192, 168, 100, 109);            // where 150 is the desired IP Address. The first three numbers must be the same as the router IP
IPAddress gateway(192, 168, 100, 1);         // set gateway to match your network. Replace with your router IP

//---

//HTU21D htu;
//---
/*
Relacionado con los sesores DS18B20
*/
DS18B20 ds(32); // pin GIO donde esta conectado los datos de los sensores.
struct obj_DS18B20{
  uint8_t Index;   // indice del sensor, relacionado con que función
  String address;  // Codigo o direccion del sensor 
  String Descrip;  // Descipcion del sensor
  uint8_t Resol;   // resolución 
  uint8_t Power;   // tipo de alimentacion
  float Offset=0;   // correncion de la medida   ºC
  float Cel=0.0;   // temperatura ºC de la ultima medida
  float Far=0.0;   // Temperatura ºF de la ultima medida  
  uint8_t CntRep;  // Numero de no reportes
  bool Identificado=false; // ya fue identificado
  bool Reporto=false; // ya reporto
  bool Inhibidor=false;// Si falla inhibe el funcionamiento. para el equipo.
};
obj_DS18B20 Sen_Temp[20]; // numero de sensores posibles
// definicion de la medida hecha por cada sensor DS18B20.
const String ArrIden []={"Cooler Salida","Cooler Retorno","Comp.Salida","Comp.Succion","Comp. bloque","Sal.Conde"};

int LED = 32;            /*LED pin defined*/
int Sensor_input = 18;    /*Digital pin for sensor input*/
int TotalDS=0;          // numero de sensores DS18B20 conseguidos.
/// fin de relacionados con los sensores DS18B20

//====== conexion como Cliente WiFi ========================================================
void connectToWiFiNetwork(){
  Serial.println("Connecting to "+String(ssid));
   // If you don't want to config IP manually disable the next two lines
   IPAddress subnet(255, 255, 255, 0);        // set subnet mask to match your network
  WiFi.config(ip, gateway, subnet);          // If you don't want to config IP manually disable this line
  WiFi.mode(WIFI_STA);                       // Config module as station only.
  WiFi.begin(ssid, password);
   while (WiFi.status() != WL_CONNECTED) {
     delay(1000);
     Serial.print(".");
    }
   Serial.println("");
   Serial.println("WiFi connected");
   Serial.println(WiFi.localIP());
}


//==========  conexion como Accespoint WiFi =======================================================
void initAccessPoint(){
  Serial.print("Setting soft-AP ... ");                   // Default IP: 192.168.4.1
   WiFi.mode(WIFI_AP);                                     // Config module as Access point only.  Set WiFi.mode(WIFI_AP_STA); to config module as Acces point and station
   boolean result = WiFi.softAP("ESP32", "12345678");      // SSID: Virtuino Network   Password:12345678
   if(result == true)  {
    Serial.println("Server Ready");
    Serial.println(WiFi.softAPIP());
   }
   else Serial.println("Failed!");
}
//==============================================================
///// V I R T U I N O
//==============================================================
//======= onCommandReceived
/* This function is called every time Virtuino app sends a request to server to change a Pin value
 * The 'variableType' can be a character like V, T, O  V=Virtual pin  T=Text Pin    O=PWM Pin 
 * The 'variableIndex' is the pin number index of Virtuino app
 * The 'valueAsText' is the value that has sent from the app   */
 void onReceived(char variableType, uint8_t variableIndex, String valueAsText){     
   if (debug){
    Serial.print("onReceived");
    Serial.print(" Tipo:");
    Serial.print(variableType);
    Serial.print("Variable: ");
    Serial.println(valueAsText);
   }
    if (variableType=='V'){
        float value = valueAsText.toFloat();        // convert the value to float. The valueAsText have to be numerical
        if (variableIndex<V_memory_count) V[variableIndex]=value;              // copy the received value to arduino V memory array
    }
}

//==============================================================
/* This function is called every time Virtuino app requests to read a pin value*/
String onRequested(char variableType, uint8_t variableIndex){     
   if (debug){
    Serial.print("onRequested");
    Serial.print(" Tipo:");
    Serial.print(variableType);
    Serial.print("Indice Var: ");
    Serial.println(variableIndex);
   }
    if (variableType=='V') {
    if (variableIndex<V_memory_count) return  String(V[variableIndex]);   // return the value of the arduino V memory array
    }
    if (variableType=='M') {
      String txt="";
      txt=Sen_Temp[variableIndex].address+" "+Sen_Temp[variableIndex].Descrip;      
      
      return txt;     
    }
  return "";
}

//==== eSTA FUNCION ES LLAMADA CONTINUAMENTE PARA GESTIONAR LA COMUNICACION virtuino =======
  void virtuinoRun(){
   WiFiClient client = server.available();
   if (!client) return;
   if (debug) Serial.println("Connected");
   unsigned long timeout = millis() + 3000;
   while (!client.available() && millis() < timeout) delay(1);
   if (millis() > timeout) {
    Serial.println("timeout");
    client.flush();
    client.stop();
    return;
    }
    virtuino.readBuffer="";    // clear Virtuino input buffer. The inputBuffer stores the incoming characters
      while (client.available()>0) {        
        char c = client.read();         // read the incoming data
        virtuino.readBuffer+=c;         // add the incoming character to Virtuino input buffer
        if (debug) Serial.write(c);
      }
     client.flush();
     if (debug) Serial.println("\nReceived data: "+virtuino.readBuffer);
     String* response= virtuino.getResponse();    // get the text that has to be sent to Virtuino as reply. The library will check the inptuBuffer and it will create the response text
     if (debug) Serial.println("Response : "+*response);
     client.print(*response);
     client.flush();
     delay(10);
     client.stop(); 
    if (debug) Serial.println("Disconnected");
}

 //= Este dela es llamado para evita el bloqueo y hacer llamadas al gestor Virtuino === vDelay
  void vDelay(int delayInMillis){long t=millis()+delayInMillis;while (millis()<t) virtuinoRun();}

//////////////////////////////////////////////////////////////////////////////////////
//// FIN DEL PROCESO VIRTUINO  ///////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////

/// CONVIERTE UN ENTERO EN HEX
const char LHex[]="0123456789ABCDEF";
String Int_Hex(int v)
{
String TxHex="";
  int H=0,L=0,i=0;
  H=v/16;
  L=v%16;
  TxHex=String(LHex[H])+String(LHex[L]);
/*  Serial.print("\n\rV:");Serial.print(v);Serial.print(" ");Serial.print(v,HEX);
  Serial.print(" LHex[H]:");Serial.print(LHex[H]);Serial.print(" LHex[L]:");Serial.print(LHex[L]);
  Serial.print(" HEX:");Serial.println(TxHex);
  */
  return TxHex;
}


//// IDENTIFICA Y CUENTA LOS DS18B20 PRESENTES
void DS18B20Cnt()
  {
    uint8_t i,j;
    String AddressTxt="";
    while (ds.selectNext()) 
      {// identifica el proximo encontrado.
      ds.getFamilyCode(); // lee el codigo de familia
      AddressTxt="";
      uint8_t address[9];
      ds.getAddress(address); // lee la direccion.  
      address[8]=0;   //  poner terminador 0
      for (i=0;i<8;++i)
        AddressTxt+= Int_Hex(address[i]); // conversion de una direccion a String
      AddressTxt+='\0'; 
      ////
      //Serial.println(AddressTxt);
      ////
      // Loop1: ver si ya lo tenemos en la lista.      
      for (i=0; i<TotalDS; ++i) 
        {
        for (j = 0; j < 8; j++)// LOOP2: compara las dos direcciones
          {
            if( Sen_Temp[i].address != AddressTxt) break;// no son iguales, ir al proximo
          } // fin loop2
          if(j==8) 
          { // actualiza los datos.
          Sen_Temp[i].Cel=ds.getTempC(); // temperatura en ºC
          Sen_Temp[i].Far=ds.getTempF(); // temperatura en ºF
          Sen_Temp[i].Resol=ds.getResolution(); // ressolución
          Sen_Temp[i].Power=ds.getPowerMode(); // como se alimenta.
          if(debug) {Serial.print(i); Serial.println("\t Actualizado");}
            break;// son iguales, salir de loop 1
          }
        } // fin loop1

        if (i==TotalDS) // recorrimos todos y no hubo igual
        {// agregar el nuevo.
          Sen_Temp[TotalDS].address=String(AddressTxt);
          Sen_Temp[TotalDS].Descrip =String(ArrIden[TotalDS]);
          Sen_Temp[TotalDS].Cel=ds.getTempC();
          Sen_Temp[TotalDS].Far=ds.getTempF();
          Sen_Temp[TotalDS].Resol=ds.getResolution();
          Sen_Temp[TotalDS].Power=ds.getPowerMode();
          ++TotalDS;
          if(debug) {Serial.print("\n\rNuevo: ");
          Serial.print(i); Serial.println("\t Actualizado\r\n");}
        }
      }
//      Serial.print("\n\rConseguidos:");Serial.println(TotalDS);
    }

uint8_t incomingByte;


static float Ptemperature=0, Phumidity=0;
static uint8_t CntHTU=0;  
const String Series="HR=% T=ºC Gas=x100ppm";

void PlotDatos()
{
  Serial.println(Series);
  Serial.print(Phumidity,2);    
  Serial.print("\t");
  Serial.print(Ptemperature,2);
  Serial.print("\t");
  if(incomingByte=='9')Serial.println(float(ReadMQ2(false))/100.0,3);
  else Serial.println(float(ReadMQ2(true))/100.0,3);
}
void DspTabla()
{
  uint8_t i,j,k;
  if(debug) return;
  display.clearDisplay();

  display.setTextSize(1);             // Normal 1:1 pixel scale
  display.setTextColor(SSD1306_WHITE);        // Draw white text
  display.setCursor(0,0);             // Start at top-left corner
  Serial.println("\n\rDireccion\t Descripcion\t ºC \t ºF \t Res \ pwr");  
  for(i=0;i<TotalDS;++i)
  {
    Serial.print(Sen_Temp[i].address);   
    Serial.print(" ");
    Serial.print(Sen_Temp[i].Descrip);   
    Serial.print("\t");
    Serial.print(Sen_Temp[i].Cel,2);   
    Serial.print("\t");
    Serial.print(Sen_Temp[i].Far,2);   
    Serial.print("\t");
    Serial.print(Sen_Temp[i].Resol);   
    Serial.print("\t");
    Serial.println(Sen_Temp[i].Power);

    display.print(Sen_Temp[i].address);
    display.print(" ");
    display.print(Sen_Temp[i].Descrip);
    display.print("\t");
    display.println(Sen_Temp[i].Cel,2);
    display.display();
   }
  Serial.print("Gas ppm: ");
  Serial.print(analogRead(Sensor_input));
  Serial.print("\t Temp ºC: ");
  Serial.print(dht.readTemperature(),2);
  Serial.print("\t HR %: ");
  Serial.print(dht.readHumidity(),2);
}
 
/*
Lectura del sensor MQ
*/
int MQ2=0;
int ReadMQ2(bool Ultima){
  static int Pvalue=0, value=0,Cnt=0;
  
  int sensor_Aout = analogRead(Sensor_input);  /*Analog value read function*/
  value+=sensor_Aout;
  ++Cnt;
  if (Cnt==10)
    {
      Pvalue=value/10;
      Cnt=0;
      value=0;
    }
  if (Ultima) // Rereso la ultima?
    return sensor_Aout; // Si, retornar el ultimo valor leido
  else if (Pvalue>0)
        return Pvalue;// retornal el promedio
      else 
        return sensor_Aout; // si no se ha hecho un promedio retorna el ultimo
 }

/*
Lectura de los sensores DS18B20
*/
void DS18B20ReadShrt()
{
 String Tipo="";
    while (ds.selectNext()) {
      switch (ds.getFamilyCode()) {
        case MODEL_DS18S20:
          Tipo="DS18S20";
          break;
        case MODEL_DS1822:
          Tipo="DS1822 ";
          break;
        case MODEL_DS18B20:
          Tipo="DS18B20";
          break;
        default:
          Tipo="DESCONO";
          break;
      }
      Serial.print(Tipo);
      Serial.print("\t");
      uint8_t address[8];
      ds.getAddress(address);
       for (uint8_t i = 0; i < 8; i++) {
//        Serial.print(" ");
        if (address[i]<16) Serial.print("0");
        Serial.print(address[i],HEX);
      }
      Serial.print("\t");
      Serial.print(ds.getResolution());
      Serial.print("\t");
  
      if (ds.getPowerMode()) {
        Serial.print("Ext");
      } else {
        Serial.print("Par");
      }
        Serial.print("\t");

      Serial.print(ds.getTempC());
      Serial.print(" C / ");
      Serial.print(ds.getTempF());
      Serial.println(" F");
    }
  }

/*
Lectura del sensor HTU21

  float h = dht.readHumidity(); //Leemos la Humedad
  float t = dht.readTemperature(); //Leemos la temperatura en grados Celsius
  float f = dht.readTemperature(true); //Leemos la temperatura en grados Fahrenheit
*/
void DHT22DRead(){
static float temperature=0, humidity=0;
static int Cnt=0;
//  if(htu.measure()) {
    temperature += dht.readTemperature();
    humidity+= dht.readHumidity();
    ++Cnt;
    if (Cnt==10)
    {
      Ptemperature=temperature/10.0;
      Phumidity=humidity/10.0;
      Cnt=0;
      temperature=0;dht.readHumidity();
      humidity=0;
    }
//  }
}


void ScaniiC() {
  byte error, address;
  int nDevices;
  Serial.println("Scanning...");
  nDevices = 0;
  for(address = 1; address < 127; address++ ) {
    Wire.beginTransmission(address);
    error = Wire.endTransmission();
    if (error == 0) {
      Serial.print("I2C device found at address 0x");
      if (address<16) {
        Serial.print("0");
      }
      Serial.println(address,HEX);
      nDevices++;
    }
    else if (error==4) {
      Serial.print("Unknow error at address 0x");
      if (address<16) {
        Serial.print("0");
      }
      Serial.println(address,HEX);
    }    
  }
  if (nDevices == 0) {
    Serial.println("No I2C devices found\n");
  }
  else {
    Serial.println("done\n");
  }
  delay(5000);          
}


int NroDS18B20=0;
//============================================================== 
//=setup========================================================
//==============================================================
void setup() {
   //----- Virtuino settings
  Serial.begin(9600);
  Serial.println("*****************************************************");
  Serial.println("*****************************************************");
  delay(2000);
  ScaniiC();

  //virtuino.=true;                         // set this value TRUE to enable the serial monitor status
  //virtuino.password="1234";                    // Set a password or prefix to your web server for more protection 
                                               // avoid special characters like ! $ = @ # % & * on your password. Use only numbers or text characters
  if(display.begin(SSD1306_SWITCHCAPVCC, 0x3D)) {
    Serial.println("Display en 0x3D");
  } else  if(display.begin(SSD1306_SWITCHCAPVCC,0x3C)) { 
    Serial.println("Display en 0x3C");
  } else  if(display.begin(SSD1306_SWITCHCAPVCC,0x78)) { 
    Serial.println("Display en 0x78");
  } else  if(display.begin(SSD1306_SWITCHCAPVCC,0x7A)) { 
    Serial.println("Display en 0x7A");
  } else {
    Serial.println(F("SSD1306 allocation failed"));
  }


  // Show initial display buffer contents on the screen --
  // the library initializes this with an Adafruit splash screen.
  display.display();
  delay(2000); // Pause for 2 seconds

  // Clear the buffer
  display.clearDisplay();

  // Draw a single pixel in white
  display.drawPixel(10, 10, SSD1306_WHITE);

  // Show the display buffer on the screen. You MUST call display() after
  // drawing commands to make them visible on screen!
  display.display();
  delay(2000);
  // display.display() is NOT necessary after every single drawing command,
  // unless that's what you want...rather, you can batch up a bunch of
  // drawing operations and then update the screen all at once by calling
  // display.display(). These examples demonstrate both approaches...


  virtuino.begin(onReceived,onRequested,256);  //Start Virtuino. Set the buffer to 256. With this buffer Virtuino can control about 28 pins (1 command = 9bytes) The T(text) commands with 20 characters need 20+6 bytes
  virtuino.key="1234";                       //This is the Virtuino password. Only requests the start with this key are accepted from the library

  //connectToWiFiNetwork();
  //initAccessPoint();
  //server.begin();
 
  pinMode(2,OUTPUT);
  Serial.print("Devices: ");
  Serial.println(NroDS18B20=ds.getNumberOfDevices());
  Serial.println();
  DS18B20Cnt();
 dht.begin(); 
  //  Serial.println(F("DHT_22 error"));
 
}

/*
Proceso que se ejecuta
*/
void proceso() {
//proceso 
 //DS18B20ReadShrt();
  digitalWrite(2,HIGH);
  ReadMQ2(false);// 
  DS18B20Cnt();
  DHT22DRead();
  if (incomingByte=='0')
    DspTabla();
  else 
    PlotDatos();
  digitalWrite(2,LOW);
  }

void DispTabla()
{
  for(uint8_t i=0;i<NroDS18B20;i++)
  {
  Serial.print("\n\r Tabla");
  Serial.println("Nro\tireccion\tDescripcion             \tRes\tºC\tºF\tCnt\tIden\tRep");
  Serial.print(Sen_Temp[i].Index);   // indice del sensor, relacionado con que función
  Serial.print(Sen_Temp[i].address);  // Codigo o direccion del sensor 
  Serial.print(Sen_Temp[i].Descrip);  // Descipcion del sensor
  Serial.print(Sen_Temp[i].Resol);   // resolución 
  Serial.print(Sen_Temp[i].Power);   // tipo de alimentacion
  Serial.print(Sen_Temp[i].Cel);   // temperatura ºC de la ultima medida  
  Serial.print(Sen_Temp[i].CntRep);   // Numero de no reportes
  Serial.print(Sen_Temp[i].Identificado); // ya fue identificado
  Serial.print(Sen_Temp[i].Reporto); // ya reporto
  }
}
//============================================================== loop
//==============================================================
//==============================================================
void loop() {
//  virtuinoRun();        // Necessary function to communicate with Virtuino. Client handler
if (Serial.available())
  {
    incomingByte =  toupper(Serial.read());
    switch(incomingByte)
      {
      case 'R':
        {
          Serial.println("Run");
          DispTabla();
          break;
        }
      case 'T':
        {
          Serial.println("Tabla");
          DispTabla();
          break;
        }
      case 'D':
        {
          debug= !debug;
          if(debug) Serial.println("\n\rDebug On");
          else Serial.println("\n\rDebug Off");
          break;
        }
      }
  }
  
 // enter your loop code here.
 if(incomingByte=='R')
    proceso();
if(incomingByte=='T')
    DispTabla();
  //------ avoid to use delay() function in your code
  vDelay(1000);     // This is an example of the recommended delay function. Remove this if you don't need


}
D0D1D2D3D4D5D6D7GNDLOGIC