#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;
#define BTN_GREEN GPIO_NUM_16
#define BTN_RED GPIO_NUM_4
#define DELAY_TIME 200
volatile bool button_pressed = false;
 


// 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;
String temp;
//int line;
static const unsigned char PROGMEM logo_bmp[] =
{ 0b00000000, 0b11000000,
  0b00000001, 0b11000000,
  0b00000001, 0b11000000,
  0b00000011, 0b11100000,
  0b11110011, 0b11100000,
  0b11111110, 0b11111000,
  0b01111110, 0b11111111,
  0b00110011, 0b10011111,
  0b00011111, 0b11111100,
  0b00001101, 0b01110000,
  0b00011011, 0b10100000,
  0b00111111, 0b11100000,
  0b00111111, 0b11110000,
  0b01111100, 0b11110000,
  0b01110000, 0b01110000,
  0b00000000, 0b00110000 };

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];

static void gpio_isr_handler(void* arg) 
{
    button_pressed = true;
}

void button_config()
{
    gpio_install_isr_service(0);
    printf("configuring button\n");
    gpio_reset_pin(BTN_RED);
    gpio_set_direction(BTN_RED, GPIO_MODE_INPUT);
    gpio_pullup_en(BTN_RED);
    gpio_set_intr_type(BTN_RED, GPIO_INTR_POSEDGE);
    gpio_isr_handler_add(BTN_RED, gpio_isr_handler, NULL);
    gpio_reset_pin(BTN_GREEN);
    gpio_set_direction(BTN_GREEN, GPIO_MODE_INPUT);
    gpio_pullup_en(BTN_GREEN);
    gpio_set_intr_type(BTN_GREEN, GPIO_INTR_POSEDGE);
    gpio_isr_handler_add(BTN_GREEN, gpio_isr_handler, NULL);
    printf("config complete\n");
}

void setup() {
  //DebugLevel = 0;
  button_config;
//  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
//If you have text size = 1 character height will be 7 pixel.  
  oled.setTextSize(1);         // set text size
  oled.setTextColor(WHITE);    // set text color
  oled.setCursor(0,0);       // set position to display (x,y)
  oled.println("123456789012345678901"); // set text
  oled.setCursor(0,10); 
  oled.println("123456789012345678901"); // set text
  oled.setTextSize(2);
  oled.setCursor(0,20);
  oled.println("1234567890"); // set text
  oled.setCursor(0,35);
  oled.println("1234567890");
  oled.setTextSize(3);
  oled.setCursor(0,50);
  oled.println("1234567890");
  //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
//  testdrawbitmap();
  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.setCursor(0,0);
      //oled.println("123456789012345678901234567890")           ");
      //oled.display();
//      oled.setCursor(40, 30);
//      cleareTextLine(30);
//      //temp = "Temp: " + TemperatureC;
//      oled.println("Temp: "); // set text
//      oled.setCursor(70, 30);
//      oled.println(TemperatureC);
//      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 cleareTextLine( int line){
//
int y;
int x;
int yl;
for (y=0; y<=6; y++) // y = up down  x = left write
      {
       for (x=0; x<127; x++)
       {
        yl = line + y;
        oled.drawPixel(x, yl, BLACK); 
       }
      }
}
// ======================================================================
//void setup() {
  
//}


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

  if (millis() - last_tick >= 1000) {
    last_tick = millis();
    ReadTemperature();
  }
  if (button_pressed) {
            printf("*\n");
            button_pressed = false;
            oled.setCursor(40, 40);
            oled.println("BUTTON PRESSED"); // set text
            oled.display();   
            //led_value = !led_value;
            //gpio_set_level(LED_RED, led_value);
            printf("BUUTON PRESSED");  
        }
  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 ===