#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <FS.h>        //
#include <LittleFS.h>  // LittleFS_esp32
#include <DallasTemperature.h>   // url: https://github.com/milesburton/Arduino-Temperature-Control-Library
#include <OneWire.h>             // url: https://github.com/PaulStoffregen/OneWire


#define PRJ_NAME "ESP32 DS18B20"  // Project Name
#define VERSION "0.0.1"           // Project version
#define PRINT_DASHED_LINES printf("\r-------------------------------------------------------\n\r")


#define SCREEN_WIDTH 128 // OLED width,  in pixels
#define SCREEN_HEIGHT 64 // OLED height, in pixels");
uint8_t DebugLevel = 3 ;

// create an OLED display object connected to I2C
Adafruit_SSD1306 oled(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
#ifdef LED_BUILTIN
#define MY_LED LED_BUILTIN  // LED_BUILTIN -> On Board Led GPIO pin 2 (D2)
#else
#define MY_LED 2  // On Board Led
#endif
#define ONE_WIRE_BUS 14  // DS18B20 pin - Temperature Sensor

float TemperatureC;

OneWire oneWire(ONE_WIRE_BUS);        // Setup a oneWire instance to communicate with any OneWire device
DallasTemperature sensors (&oneWire);  // Pass oneWire reference to DallasTemperature library
DeviceAddress insideDS18x20;          // DS18x20  arrays to hold device address
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=


// ======================================================================
// ESP Flash Chip Mode
// ======================================================================
const char *const PROGMEM FlashChipMode[] = { "QIO", "QOUT", "DIO", "DOUT", "FM FAST READ", "FM SLOW READ", "UNKNOWN" };

char BaffESPinfo[32];


void setup() {
//  Serial.begin(9600);
  Serial.begin(115200);
  // initialize OLED display with I2C address 0x3C
  if (!oled.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("failed to start SSD1306 OLED"));
    while (1);
  }

  delay(2000);         // wait two seconds for initializing
  oled.clearDisplay(); // clear display

  oled.setTextSize(1);         // set text size
  oled.setTextColor(WHITE);    // set text color
  oled.setCursor(1, 2);       // set position to display (x,y)
  oled.println("GCL1 Clorinator"); // set text
  //oled.setTextColor(YELOW);
  oled.setCursor(20, 20);       // set position to display (x,y)
  oled.println("Giga Technology"); // set text
  oled.setCursor(30, 30);
  oled.println("Temp 19.5C"); // set text
  oled.display();              // display on OLED
  //================================
// ====================================================================
//  Serial.begin(115200);
//  delay(10);

  // if DEBUGLEVEL > 0  //Serial Debugging
//  Serial.println(F("SEBUG"));
  if (DebugLevel > 0) {
    printf("\r\n--------------- Booting ESP ---------------------------\r\n");
    printf("Arduino IDE      : %d.%d.%d\r\n", ARDUINO / 10000, ARDUINO % 10000 / 100, ARDUINO % 100 / 10 ? ARDUINO % 100 : ARDUINO % 10);
    printf("Compiler version : %s \r\n", __VERSION__);
    printf("On Board         : %s \r\n", ARDUINO_BOARD);
    printf("Firmware name    : %s v%s \r\n", PRJ_NAME, VERSION);
    printf("Firmware Date    : %s \r\n", (__DATE__ ", " __TIME__));
    printf("Author           : Kernel Panic  \r\n");
    PRINT_DASHED_LINES;
  }
  // #endif  // DEBUGLEVEL > 0

  pinMode(MY_LED, OUTPUT);
  sensors_setup();
  printESPinfo();
  printf("Time waiting for Boot : %2.2f sec. \r\n", (millis() / 1000.0));
  PRINT_DASHED_LINES;
  printf("\r\n");
  //================================
}

// #define DEBUGLEVEL 2  // Debug Level from 0 to 5 => 0 = no print, 5 = print all


// ======================================================================
// Arduino Core Version  getCoreVersion
// ======================================================================
char *GetCoreVersion() {
  sprintf(BaffESPinfo, "%u.%u.%u", ESP_ARDUINO_VERSION_MAJOR, ESP_ARDUINO_VERSION_MINOR, ESP_ARDUINO_VERSION_PATCH);
  return BaffESPinfo;
}

// ======================================================================
// ESP id
// ======================================================================
char *GetESPid() {
  sprintf(BaffESPinfo, "%012llX", ESP.getEfuseMac());
  return BaffESPinfo;
}

// ======================================================================
// ESP Chip Model
// ======================================================================
char *GetChipModel() {
  sprintf(BaffESPinfo, "%s Rev.%d", ESP.getChipModel(), ESP.getChipRevision());
  return BaffESPinfo;
}


// ======================================================================
// format bytes
// ======================================================================
char *formatBytes(size_t bytes) {
  if (bytes < 1024) {
    sprintf(BaffESPinfo, "%.2f Byte", bytes);
    return BaffESPinfo;
  } else if (bytes < (1024 * 1024)) {
    sprintf(BaffESPinfo, "%.2f KB", (bytes / 1024.0));
    return BaffESPinfo;
  } else if (bytes < (1024 * 1024 * 1024)) {
    sprintf(BaffESPinfo, "%.2f MB", (bytes / 1024.0 / 1024.0));
    return BaffESPinfo;
  } else {
    sprintf(BaffESPinfo, "%.2f GB", (bytes / 1024.0 / 1024.0 / 1024.0));
    return BaffESPinfo;
  }
} //== Close formatBytes ===



// ======================================================================
void ReadTemperature() {
  // ====================================================================
  sensors.requestTemperatures();  // Send the command to get temperatures
  //  TemperatureC = sensors.getTempC(insideDS18x20);
  // We use the function ByIndex, and as an example get the temperature from the first sensor only.
  TemperatureC = sensors.getTempCByIndex(0);

  if (TemperatureC == DEVICE_DISCONNECTED_C) {
    TemperatureC = random(-1100, 5990) / 100.0;
      oled.println("Temp 33.5C"); // set text
      oled.display();  

    //#if DEBUGLEVEL > 0  //Serial Debugging
    if (DebugLevel > 2) {
      printf("\r-[Error]: Could not find DS18B20. Send Random value for test - \n\r");
    }
    //#endif  // DEBUGLEVEL > 0
  }

  // #if DEBUGLEVEL > 0  //Serial Debugging
  if (DebugLevel > 2) {
    // printf("Temperature : %3.1f\xe2\x84\x83 \n\r", TemperatureC);
    printf("Temperature : %3.1f%cC \n\r", TemperatureC, 0xB0 );
//    Serial.println(F("Temperature : %3.1f%cC \n\r", TemperatureC, 0xB0));
  }

  // #endif  // DEBUGLEVEL > 0
}  //== Close ReadTemperature() ====


// ======================================================================
// function to print a device address
void printAddress(DeviceAddress deviceAddress) {
  // ======================================================================
  for (uint8_t i = 0; i < 8; i++) {
    // zero pad the address if necessary
    if (deviceAddress[i] < 16) Serial.print("0");
    Serial.print(deviceAddress[i], HEX);
  }
}  //== Close printAddress ==



// ======================================================================
void sensors_setup() {
  // ====================================================================
  sensors.begin();
  // Get ID of first sensor (at index 0)
  if (!sensors.getAddress(insideDS18x20, 0))
    Serial.println("Unable to find address for Device 0");

  // set the resolution to 10 bit
  sensors.setResolution(insideDS18x20, 10);

  Serial.print("Dallas Library Vers.: ");
  Serial.println(DALLASTEMPLIBVERSION);

  // show the addresses we found on the bus
  Serial.print("Device 0 Address    : ");
  printAddress(insideDS18x20);
  Serial.println();

  Serial.print("Device 0 Resolution : ");
  Serial.println(sensors.getResolution(insideDS18x20), DEC);

  PRINT_DASHED_LINES;
}  //== Close sensors_setup() ==



// ======================================================================
//void setup() {
  
//}


// ======================================================================
void loop() {
  // ====================================================================
  static uint32_t last_tick;

  if (millis() - last_tick >= 2000) {
    last_tick = millis();
    ReadTemperature();
  }

  yield();
  digitalWrite(MY_LED, millis() % 500 < 40);
}


// ======================================================================
void printESPinfo() {
  // ====================================================================
  //ESP info  url: https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/Esp.h
  // #if DEBUGLEVEL > 1  //Serial Debugging
  if (DebugLevel > 1) {
    printf("\r\n===========  i n f o r m a t i o n s  ====================\r\n");
    printf("\r\t--- ESP info --- \r\n");
    printf("My Board          : %s \r\n", ARDUINO_BOARD);
    printf("ESP32 Chip model  : %s \r\n", GetChipModel());
    printf("This chip has     : %d cores \r\n", ESP.getChipCores());
    printf("ESP Chip ID       : %s \r\n", GetESPid());
    printf("ESP-IDF version   : %s \r\n", ESP.getSdkVersion());
    printf("Arduino core ver. : %s \r\n", GetCoreVersion());
    // printf("Arduino core ver. : %s \r\n", ESP.getCoreVersion());  // -> new for core ver.3.0 => https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/Esp.h#L87
    printf("MCU Frequency     : %d MHz \r\n", ESP.getCpuFreqMHz());
    printf("Flash chip Speed  : %d MHz \r\n", ESP.getFlashChipSpeed() / 1000000UL);
    printf("ESPSPI Flash Mode : %s \r\n", FlashChipMode[ESP.getFlashChipMode()]);  // DIO
    printf("Flash chip Size   : %d MB \r\n", ESP.getFlashChipSize() / 1000000UL);
    printf("Total heap size   : %s \r\n", formatBytes(ESP.getHeapSize()));
    printf("Available  heap   : %s \r\n", formatBytes(ESP.getFreeHeap()));
    printf("Lowest free heap  : %s \r\n", formatBytes(ESP.getMinFreeHeap()));   //lowest level of free heap since boot
    printf("Largest block heap: %s \r\n", formatBytes(ESP.getMaxAllocHeap()));  //largest block of heap that can be allocated at once
    printf("Sketch Size       : %s \r\n", formatBytes(ESP.getSketchSize()));
    printf("Free Sketch Space : %s \r\n", formatBytes(ESP.getFreeSketchSpace()));
    // File System
    printf("\r\t--- File System informations ---\r\n");
    printf("LittleFS Total    : %s \r\n", formatBytes(LittleFS.totalBytes()));
    printf("LittleFS Used     : %s \r\n", formatBytes(LittleFS.usedBytes()));
    printf("LittleFS Free     : %s \r\n", formatBytes(LittleFS.totalBytes() - LittleFS.usedBytes()));
    printf("\r=============  END informations ===========================\r\n\n");
  }
  //#endif  // DEBUGLEVEL > 1
}  //== Close printInfo ===