////////////////////////////////////////////////////////////////////////////////
// Name:       klok-op-tijd-wifi.ino                                          //
//             01 = Ziet er leuk uit en loopt strak op tijd. Gebruikt WiFi    //
//                  om verbinding te maken met een tijdserver.                //
// http://robotigs.nl/robots/includes/parts.php?idpart=113                    //
// Created by: HARB rboek2@gmail.com September 2020 GPL copyrights            //
// Original:   https://github.com/ThingPulse/esp8266-oled-ssd1306             //
// Platform:   TTGO T-Beam V1.0                                               //
////////////////////////////////////////////////////////////////////////////////
// As outputs the following modules are mounted:                              //
//                                                                            //
// As inputs the following modules are mounted:                               //
//                                                                            //
// For communications and statistics are mounted:                             //
////////////////////////////////////////////////////////////////////////////////

  //Define the needed header files for the precompiler, no charge if not used --
  #include <TimeLib.h>                 // https://github.com/PaulStoffregen/Time
  #include <Wire.h>                                      // Only needed for oled
  #include "SSD1306Wire.h" // https://github.com/ThingPulse/esp8266-oled-ssd1306
  #include "OLEDDisplayUi.h"                               // Include the UI lib
  #include <WiFi.h>                          //Gebruikt door tijd synchronisatie
  #include "axp20x.h"             // https://github.com/lewisxhe/AXP202X_Library
  
#define GPS_POWER_CTRL_CH     3
#define LORA_POWER_CTRL_CH    2
#define PMU_IRQ               35
 #define I2C_SDA         21
#define I2C_SCL         22 
#define SSD1306_ADDRESS 0x3C

  const char*   ssid       = "H369AAA781A";
  const char*   password   = "mispoes";
  
  bool pmu_irq       = false;
  String baChStatus  = "No charging";
  bool ssd1306_found = false;
  bool axp192_found  = false;
  bool packetSent, packetQueued;
  long timezone      = 1; 
  byte daysavetime   = 1;
  int screenW        = 128;
  int screenH        = 64;
  int clockCenterX   = screenW/2;
  int clockCenterY   = ((screenH-16)/2)+16;     // top yellow part is 16 px height
  int clockRadius    = 25; //23
  char buffer[20];
  String twoDigits(int digits){  // utility function for digital clock display: prints leading 0
    if(digits < 10) {
      String i = '0'+String(digits);
      return i;
    }else{
      return String(digits);
    }
  }
  //const uint8_t axp_irq_pin = 35;  //Ok voor T-Beam V1.0 zie ook configuration.h
const unsigned char activeSymbol[] PROGMEM = {
    B00000000,
    B00000000,
    B00011000,
    B00100100,
    B01000010,
    B01000010,
    B00100100,
    B00011000
};

const unsigned char inactiveSymbol[] PROGMEM = {
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B00011000,
    B00011000,
    B00000000,
    B00000000
};

  //Initialize OBJECTS ---------------------------------------------------------
  AXP20X_Class axp;
  RTC_DATA_ATTR int bootCount = 0;                         // deep sleep support
  esp_sleep_source_t wakeCause;                // the reason we booted this time
  SSD1306Wire display(SSD1306_ADDRESS, I2C_SDA,I2C_SCL);//T-Beam V1.0 0x3c,21,22
  OLEDDisplayUi ui (&display);



void GPSframe(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
  display->setTextAlignment(TEXT_ALIGN_CENTER);
  display->setFont(ArialMT_Plain_24);
  display->drawString(clockCenterX + x , clockCenterY + y, "Accustatus" );
}



// This array keeps function pointers to all frames
FrameCallback frames[] = {analogClockFrame, digitalClockFrame, GPSframe}; // frames are the single views that slide in
int frameCount = 3;                                                       // how many frames are there?
OverlayCallback overlays[] = { clockOverlay };                            //Overlays are statically drawn on top of a frame eg. a clock
int overlaysCount = 1;




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

  Wire.begin(I2C_SDA, I2C_SCL);
  scanI2Cdevice();
  axp192Init();
  
  Serial.println();
  Serial.print("WiFi connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.println("Contacting Time Server");
  configTime(3600*timezone, daysavetime*3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
  struct tm tmstruct ;
  delay(2000);
  tmstruct.tm_year = 0;
  getLocalTime(&tmstruct, 5000);
  int uur     = tmstruct.tm_hour;
  int minuut  = tmstruct.tm_min;
  int sekonde = tmstruct.tm_sec;
  int dag     = tmstruct.tm_sec;
  int maand   = tmstruct.tm_mon +1;
  int jaar    = (tmstruct.tm_year)+1900;  
   
	// The ESP is capable of rendering 60fps in 80Mhz mode
	// but that won't give you much time for anything else
	// run it in 160Mhz mode or just set it to 30 fps
  ui.setTargetFPS(60);
  ui.setActiveSymbol(activeSymbol);    // Customize the active and inactive symbol
  ui.setInactiveSymbol(inactiveSymbol);
  ui.setIndicatorPosition(TOP);  // TOP, LEFT, BOTTOM, RIGHT
  ui.setIndicatorDirection(LEFT_RIGHT);   // Defines where the first frame is located in the bar.
  ui.setFrameAnimation(SLIDE_RIGHT);   // SLIDE_LEFT, SLIDE_RIGHT, SLIDE_UP, SLIDE_DOWN
  ui.setFrames(frames, frameCount);   // Add frames
  ui.setOverlays(overlays, overlaysCount);   // Add overlays
  ui.init();      // Initialising the UI will init the display too.

  display.flipScreenVertically();
  setTime(uur, minuut, sekonde, dag, maand, jaar);
}


void loop() {
  int remainingTimeBudget = ui.update();
  if (remainingTimeBudget > 0) {    // You can do some work here
    delay(remainingTimeBudget);     // Don't do stuff if you are below your time budget.
  }
}






void clockOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) {}


void analogClockFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
     //  ui.disableIndicator();
     //  Draw the clock face
     //  display->drawCircle(clockCenterX + x, clockCenterY + y, clockRadius);
  display->drawCircle(clockCenterX + x, clockCenterY + y, 2);
     //  hour ticks
  for( int z=0; z < 360;z= z + 30 ){
  //Begin at 0° and stop at 360°
    float angle = z ;
    angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
    int x2 = ( clockCenterX + ( sin(angle) * clockRadius ) );
    int y2 = ( clockCenterY - ( cos(angle) * clockRadius ) );
    int x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 8 ) ) ) );
    int y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 8 ) ) ) );
    display->drawLine( x2 + x , y2 + y , x3 + x , y3 + y);
  }

  // display second hand
  float angle = second() * 6 ;
  angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
  int x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 5 ) ) ) );
  int y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 5 ) ) ) );
  display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y);
  //
  // display minute hand
  angle = minute() * 6 ;
  angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
  x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 4 ) ) ) );
  y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 4 ) ) ) );
  display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y);
  //
  // display hour hand
  angle = hour() * 30 + int( ( minute() / 12 ) * 6 )   ;
  angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
  x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 2 ) ) ) );
  y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 2 ) ) ) );
  display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y);
}

void digitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
  String timenow = String(hour())+":"+twoDigits(minute())+":"+twoDigits(second());
  display->setTextAlignment(TEXT_ALIGN_CENTER);
  display->setFont(ArialMT_Plain_24);
  display->drawString(clockCenterX + x , clockCenterY + y, timenow );
}


void scanI2Cdevice(void){
    byte err, addr;
    int nDevices = 0;
    for (addr = 1; addr < 127; addr++) {
        Wire.beginTransmission(addr);
        err = Wire.endTransmission();
        if (err == 0) {
            Serial.print("I2C device found at address 0x");
            if (addr < 16)
                Serial.print("0");
            Serial.print(addr, HEX);
            Serial.println(" !");
            nDevices++;

            if (addr == SSD1306_ADDRESS) {
                ssd1306_found = true;
                Serial.println("ssd1306 display found");
            }
            if (addr == AXP192_SLAVE_ADDRESS) {
                axp192_found = true;
                Serial.println("axp192 PMU found");
            }
        } else if (err == 4) {
            Serial.print("Unknow error at address 0x");
            if (addr < 16)
                Serial.print("0");
            Serial.println(addr, HEX);
        }
    }
    if (nDevices == 0)
        Serial.println("No I2C devices found\n");
    else
        Serial.println("done\n");
}






/**
 * Init the power manager chip
 * 
 * axp192 power 
    DCDC1 0.7-3.5V @ 1200mA max -> OLED // If you turn this off you'll lose comms to the axp192 because the
    // OLED and the axp192 share the same i2c bus, instead use ssd1306 sleep mode
    DCDC2 -> unused
    DCDC3 0.7-3.5V @ 700mA max -> ESP32 (keep this on!)
    LDO1 30mA -> charges GPS backup battery // charges the tiny J13 battery by the GPS 
    //to power the GPS ram (for a couple of days), can not be turned off
    LDO2 200mA -> LORA
    LDO3 200mA -> GPS
 */

void axp192Init() {   //https://github.com/kizniche/ttgo-tbeam-ttn-tracker
    if (axp192_found) {
        if (!axp.begin(Wire, AXP192_SLAVE_ADDRESS)) {
            Serial.println("AXP192 Begin PASS");
        } else {
            Serial.println("AXP192 Begin FAIL");
        }
        // axp.setChgLEDMode(LED_BLINK_4HZ);
        Serial.printf("DCDC1: %s\n", axp.isDCDC1Enable() ? "ENABLE" : "DISABLE");
        Serial.printf("DCDC2: %s\n", axp.isDCDC2Enable() ? "ENABLE" : "DISABLE");
        Serial.printf("LDO2: %s\n", axp.isLDO2Enable() ? "ENABLE" : "DISABLE");
        Serial.printf("LDO3: %s\n", axp.isLDO3Enable() ? "ENABLE" : "DISABLE");
        Serial.printf("DCDC3: %s\n", axp.isDCDC3Enable() ? "ENABLE" : "DISABLE");
        Serial.printf("Exten: %s\n", axp.isExtenEnable() ? "ENABLE" : "DISABLE");
        Serial.println("----------------------------------------");

        axp.setPowerOutPut(AXP192_LDO2, AXP202_ON); // LORA radio
        axp.setPowerOutPut(AXP192_LDO3, AXP202_OFF); // GPS main power
        axp.setPowerOutPut(AXP192_DCDC2, AXP202_ON);
        axp.setPowerOutPut(AXP192_EXTEN, AXP202_ON);
        axp.setPowerOutPut(AXP192_DCDC1, AXP202_ON);
        axp.setDCDC1Voltage(3300); // for the OLED power

        Serial.printf("DCDC1: %s\n", axp.isDCDC1Enable() ? "ENABLE" : "DISABLE");
        Serial.printf("DCDC2: %s\n", axp.isDCDC2Enable() ? "ENABLE" : "DISABLE");
        Serial.printf("LDO2: %s\n", axp.isLDO2Enable() ? "ENABLE" : "DISABLE");
        Serial.printf("LDO3: %s\n", axp.isLDO3Enable() ? "ENABLE" : "DISABLE");
        Serial.printf("DCDC3: %s\n", axp.isDCDC3Enable() ? "ENABLE" : "DISABLE");
        Serial.printf("Exten: %s\n", axp.isExtenEnable() ? "ENABLE" : "DISABLE");

        axp.setChgLEDMode(AXP20X_LED_OFF); // LED off
        //axp.setChgLEDMode(AXP20X_LED_BLINK_1HZ); // 1blink/sec, low rate
        //axp.setChgLEDMode(AXP20X_LED_BLINK_4HZ); // 4blink/sec, high rate
        //axp.setChgLEDMode(AXP20X_LED_LOW_LEVEL); // LED full on

        pinMode(PMU_IRQ, INPUT_PULLUP);
        attachInterrupt(PMU_IRQ, [] {
            pmu_irq = true;
        }, FALLING);

        axp.adc1Enable(AXP202_BATT_CUR_ADC1, 1);
        axp.enableIRQ(AXP202_VBUS_REMOVED_IRQ | AXP202_VBUS_CONNECT_IRQ | AXP202_BATT_REMOVED_IRQ | AXP202_BATT_CONNECT_IRQ, 1);
        axp.clearIRQ();

        if (axp.isChargeing()) {
            baChStatus = "Charging";
        }
    } else {
        Serial.println("AXP192 not found");
    }
}