/* Simple WiFi Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_sleep.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "esp_attr.h"
#include "esp32/ulp.h"
#include "driver/adc.h"
#include "driver/rtc_io.h"
#include "soc/rtc_cntl_reg.h"
#include "nvs.h"
#include "nvs_flash.h"

#include "lwip/err.h"
#include "apps/sntp/sntp.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "lwip/netdb.h"
#include "lwip/dns.h"
#include "soc/rtc.h"
#include "driver/gpio.h"


#include "esp_ota_ops.h"
#include "esp_http_client.h"
#include "esp_https_ota.h"

#include "driver/adc.h"
#include "esp_adc_cal.h"

#include "esp_task_wdt.h"

#include "driver/uart.h"

/* The examples use simple WiFi configuration that you can set via
   'make menuconfig'.

   If you'd rather not, just change the below entries to strings with
   the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
*/
static const char *TAG = "simple wifi";
extern const uint8_t server_cert_pem_start[] asm("_binary_ca_cert_pem_start");
extern const uint8_t server_cert_pem_end[] asm("_binary_ca_cert_pem_end");

#define BASE_TIME 1538884800 //Hong Kong time

#define EXAMPLE_ESP_WIFI_MODE_AP   CONFIG_ESP_WIFI_MODE_AP //TRUE:AP FALSE:STA
#define EXAMPLE_ESP_WIFI_SSID      CONFIG_ESP_WIFI_SSID
#define EXAMPLE_ESP_WIFI_PASS      CONFIG_ESP_WIFI_PASSWORD
//#define EXAMPLE_ESP_WIFI_SSID      "Lin_PC_Network"
//#define EXAMPLE_ESP_WIFI_PASS      "eret-0qp8-rrwa"
#define EXAMPLE_MAX_STA_CONN       CONFIG_MAX_STA_CONN

/* FreeRTOS event group to signal when we are connected*/
static EventGroupHandle_t wifi_event_group;

/* The event group allows multiple bits for each event,
   but we only care about one event - are we connected
   to the AP with an IP? */
const int WIFI_CONNECTED_BIT = BIT0;

#define GPIO_INPUT_IO26    26
#define GPIO_INPUT_PIN_SEL  1ULL<<GPIO_INPUT_IO26

#define GPIO_INPUT_IO25    25
#define GPIO_INPUT_PIN_SEL_25  1ULL<<GPIO_INPUT_IO25

#define WATER_NO_LEAK   0x55
#define WATER_LEAK      0xAA
/* Constants that aren't configurable in menuconfig */

#define WEB_SERVER "www.defond.com"
#define WEB_PORT 80
#define WEB_URL "http://www.defond.com/sigfox/saveData.ashx?id=ABCDEF&time=1538668800&data=7C0602AA01020000"
#define OTA_URL "https://192.168.1.104:8070/simple_wifi_ota.bin"
/*
#define WEB_SERVER "example.com"
#define WEB_PORT 80
#define WEB_URL "http://example.com/"
*/

unsigned char WiFi_started = 0;
unsigned char WiFI_connected =0;




/*
static const char *REQUEST_dummy = "GET " WEB_URL " HTTP/1.0\r\n"
    "Host: "WEB_SERVER"\r\n"
    "User-Agent: esp-idf/1.0 esp32\r\n"
    "\r\n";
*/
static char web_url[200];
char device_id[7] = "ABCDEF";
char data[17];
static char http_request[300];

//char battery_percentage = 100;

static void obtain_time(void);
static void initialize_sntp(void);
static void user_set_wake_up_timer(time_t now);
void UserOTA(void);
void SleepSetup(void);
/* Variable holding number of times ESP32 restarted since first boot.
 * It is placed into RTC memory using RTC_DATA_ATTR and
 * maintains its value when ESP32 wakes from deep sleep.
 */
RTC_DATA_ATTR static char FirstBoot = 1;// WakeUpTimerSet = 0;  // data retended after deep sleep wake up

//#define DEFAULT_VREF    1100        //Use adc2_vref_to_gpio() to obtain a better estimate
#define DEFAULT_VREF    1350        //Use adc2_vref_to_gpio() to obtain a better estimate
#define NO_OF_SAMPLES   64          //Multisampling

#define BATTERY_VOLT    3300.0

#define BATTERY_LOW     1800.0

static esp_adc_cal_characteristics_t *adc_chars;
static const adc_channel_t channel = ADC_CHANNEL_6;     //GPIO34 if ADC1, GPIO14 if ADC2
static const adc_atten_t atten = ADC_ATTEN_DB_11;
static const adc_unit_t unit = ADC_UNIT_1;

#define TWDT_TIMEOUT_S          60


#define CHECK_ERROR_CODE(returned, expected) ({                        \
            if(returned != expected){                                  \
                printf("TWDT ERROR\n");                                \
                abort();                                               \
            }                                                          \
})

//static TaskHandle_t task_handles[portNUM_PROCESSORS];

//Callback for user tasks created in app_main()
void reset_task(void *arg)
{
    //Subscribe this task to TWDT, then check if it is subscribed
    CHECK_ERROR_CODE(esp_task_wdt_add(NULL), ESP_OK);
    CHECK_ERROR_CODE(esp_task_wdt_status(NULL), ESP_OK);

 //   while(1){
        //reset the watchdog every 2 seconds
        CHECK_ERROR_CODE(esp_task_wdt_reset(), ESP_OK);  //Comment this line to trigger a TWDT timeout
  //      vTaskDelay(pdMS_TO_TICKS(TASK_RESET_PERIOD_S * 1000));
//    }
}
static void check_efuse()
{
    //Check TP is burned into eFuse
    if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP) == ESP_OK) {
        printf("eFuse Two Point: Supported\n");
    } else {
        printf("eFuse Two Point: NOT supported\n");
    }

    //Check Vref is burned into eFuse
    if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_VREF) == ESP_OK) {
        printf("eFuse Vref: Supported\n");
    } else {
        printf("eFuse Vref: NOT supported\n");
    }
}

static void print_char_val_type(esp_adc_cal_value_t val_type)
{
    if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) {
        printf("Characterized using Two Point Value\n");
    } else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) {
        printf("Characterized using eFuse Vref\n");
    } else {
        printf("Characterized using Default Vref\n");
    }
}


void UserADCInit(void)
{
    //Check if Two Point or Vref are burned into eFuse
    check_efuse();

    //Configure ADC
    if (unit == ADC_UNIT_1) {
        adc1_config_width(ADC_WIDTH_BIT_12);
        adc1_config_channel_atten(channel, atten);
    } else {
        adc2_config_channel_atten((adc2_channel_t)channel, atten);
    }

    //Characterize ADC
    adc_chars = calloc(1, sizeof(esp_adc_cal_characteristics_t));
    esp_adc_cal_value_t val_type = esp_adc_cal_characterize(unit, atten, ADC_WIDTH_BIT_12, DEFAULT_VREF, adc_chars);
    print_char_val_type(val_type);
}

uint32_t ADCGetBatteryLevel(void)
{
    uint32_t adc_reading = 0;
    uint32_t BatteryLevel;
  //  float f_BatteryLevel;

    //Multisampling
    for (int i = 0; i < NO_OF_SAMPLES; i++) {
        if (unit == ADC_UNIT_1) {
            adc_reading += adc1_get_raw((adc1_channel_t)channel);
        } else {
            int raw;
            adc2_get_raw((adc2_channel_t)channel, ADC_WIDTH_BIT_12, &raw);
            adc_reading += raw;
        }
    }
    adc_reading /= NO_OF_SAMPLES;
    //Convert adc_reading to voltage in mV
    uint32_t voltage = esp_adc_cal_raw_to_voltage(adc_reading, adc_chars);
    printf("Raw: %d\tVoltage: %dmV\n", adc_reading, voltage);

    if(voltage < BATTERY_LOW)
    {
    	BatteryLevel = 0;
     //   BatteryLow = 1;
    }
    else
    {
    	BatteryLevel = voltage - BATTERY_LOW;
    }
    BatteryLevel = (BatteryLevel / (float)(BATTERY_VOLT - BATTERY_LOW)) *100;
    if(BatteryLevel>100)
    	BatteryLevel=100;
    printf("battery level: %d \n", BatteryLevel);
    if(BatteryLevel>100)
    	BatteryLevel=100;
    return BatteryLevel;
}

esp_err_t _http_event_handler(esp_http_client_event_t *evt)
{
    switch(evt->event_id) {
        case HTTP_EVENT_ERROR:
            ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
            break;
        case HTTP_EVENT_ON_CONNECTED:
            ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
            break;
        case HTTP_EVENT_HEADER_SENT:
            ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
            break;
        case HTTP_EVENT_ON_HEADER:
            ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
            break;
        case HTTP_EVENT_ON_DATA:
            ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
            break;
        case HTTP_EVENT_ON_FINISH:
            ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
            break;
        case HTTP_EVENT_DISCONNECTED:
            ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
            break;
    }
    return ESP_OK;
}
static esp_err_t event_handler(void *ctx, system_event_t *event)
{
    switch(event->event_id) {
    case SYSTEM_EVENT_STA_START:
        esp_wifi_connect();
        break;
    case SYSTEM_EVENT_STA_GOT_IP:
        ESP_LOGI(TAG, "got ip:%s",
                 ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip));
        xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT);
        WiFI_connected =1;
        break;
    case SYSTEM_EVENT_AP_STACONNECTED:
        ESP_LOGI(TAG, "station:"MACSTR" join, AID=%d",
                 MAC2STR(event->event_info.sta_connected.mac),
                 event->event_info.sta_connected.aid);
        break;
    case SYSTEM_EVENT_AP_STADISCONNECTED:
        ESP_LOGI(TAG, "station:"MACSTR"leave, AID=%d",
                 MAC2STR(event->event_info.sta_disconnected.mac),
                 event->event_info.sta_disconnected.aid);
        break;
    case SYSTEM_EVENT_STA_DISCONNECTED:
        esp_wifi_connect();
        xEventGroupClearBits(wifi_event_group, WIFI_CONNECTED_BIT);
        WiFI_connected =0;
        break;
    default:
        break;
    }
    return ESP_OK;
}

void wifi_init_softap()
{
    wifi_event_group = xEventGroupCreate();

    tcpip_adapter_init();
    ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    wifi_config_t wifi_config = {
        .ap = {
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
            .password = EXAMPLE_ESP_WIFI_PASS,
            .max_connection = EXAMPLE_MAX_STA_CONN,
            .authmode = WIFI_AUTH_WPA_WPA2_PSK
        },
    };
    if (strlen(EXAMPLE_ESP_WIFI_PASS) == 0) {
        wifi_config.ap.authmode = WIFI_AUTH_OPEN;
    }

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());
    WiFi_started=1;
    ESP_LOGI(TAG, "wifi_init_softap finished.SSID:%s password:%s",
             EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
}

void wifi_init_sta()
{
    wifi_event_group = xEventGroupCreate();

    tcpip_adapter_init();
    ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL) );

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    wifi_config_t wifi_config = {
        .sta = {
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .password = EXAMPLE_ESP_WIFI_PASS
        },
    };

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
    ESP_ERROR_CHECK(esp_wifi_start() );
    WiFi_started=1;
    ESP_LOGI(TAG, "wifi_init_sta finished.");
    ESP_LOGI(TAG, "connect to ap SSID:%s password:%s",
             EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
}


static void http_get_task(void *pvParameters)
{
    const struct addrinfo hints = {
        .ai_family = AF_INET,
        .ai_socktype = SOCK_STREAM,
    };
    struct addrinfo *res;
    struct in_addr *addr;
    int s, r;
    char recv_buf[64];
    ESP_LOGI(TAG, "Starting again!");

    const int ext_wakeup_pin_2 = 26;
    const uint64_t ext_wakeup_pin_2_mask = 1ULL << ext_wakeup_pin_2;
    const int ext_wakeup_pin_1 = 25;
  //  const uint64_t ext_wakeup_pin_1_mask = 1ULL << ext_wakeup_pin_1;
//    esp_sleep_ext1_wakeup_mode_t ext1_waitup_mode_var;
    int GPIO_level;
    unsigned char waterState;
    uint32_t batteryLevel;
    time_t now;
  //  struct timeval now;
  //  gettimeofday(&now, NULL);
    esp_sleep_wakeup_cause_t my_wake_up_cause;
/*
    //Subscribe this task to TWDT, then check if it is subscribed
    CHECK_ERROR_CODE(esp_task_wdt_add(NULL), ESP_OK);
    CHECK_ERROR_CODE(esp_task_wdt_status(NULL), ESP_OK);


        //reset the watchdog every 2 seconds
    CHECK_ERROR_CODE(esp_task_wdt_reset(), ESP_OK);  //Comment this line to trigger a TWDT timeout
*/


    time(&now);
    gpio_config_t io_conf;
  //  struct timeval now;
  //  gettimeofday(&now, NULL);
  //  long current_time = now.tv_sec + BASE_TIME;
    ESP_LOGE(TAG, "Current timestamp: %10ld", now);

    UserADCInit();

    while(1) {

        my_wake_up_cause=esp_sleep_get_wakeup_cause();
        ESP_LOGI(TAG, "Wake up cause:%d %d", my_wake_up_cause, FirstBoot);
  //      if(my_wake_up_cause == ESP_SLEEP_WAKEUP_TIMER)
 //       {
   //     	ESP_LOGI(TAG, "TimerWakeup");
    //    	user_set_wake_up_timer(now);

//	        esp_deep_sleep_start();
//	        continue;
    //    }

	    if(my_wake_up_cause == ESP_SLEEP_WAKEUP_EXT0)
	    {
//			ESP_LOGI(TAG, "TimerWakeup");
	//		user_set_wake_up_timer(now);

	//		esp_deep_sleep_start();
	//		continue;
	    	UserOTA();
	    }
    	rtc_gpio_deinit(GPIO_INPUT_IO26);

        io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
        //bit mask of the pins, use GPIO4/5 here
        io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL;
        //set as input mode
        io_conf.mode = GPIO_MODE_INPUT;
        //enable pull-up mode
   //     io_conf.pull_up_en = 1;
        gpio_config(&io_conf);

    	rtc_gpio_deinit(GPIO_INPUT_IO25);
        io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
        //bit mask of the pins, use GPIO4/5 here
        io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL_25;
        //set as input mode
        io_conf.mode = GPIO_MODE_INPUT;
        //enable pull-up mode
        io_conf.pull_down_en = 1;
        gpio_config(&io_conf);



        GPIO_level= gpio_get_level(GPIO_INPUT_IO26);
        if(FirstBoot)
        	FirstBoot = 0;
		if(GPIO_level)
		{
			waterState = WATER_LEAK;
			ESP_LOGI(TAG, "Water Leak");
		}
		else
		{
			waterState = WATER_NO_LEAK;
			ESP_LOGI(TAG, "Water No Leak");
		}

  //      if(!FirstBoot)
   //     {

	//	    esp_light_sleep_start();


  //      }

    //    else if(1)  //light sleep
 //       else if(my_wake_up_cause == ESP_SLEEP_WAKEUP_EXT1 || FirstBoot) // deep sleep
   //     else    {

		batteryLevel=ADCGetBatteryLevel();

		if(!WiFi_started)
		{
			ESP_ERROR_CHECK(esp_wifi_start() );
			WiFi_started=1;
		}
		ESP_LOGI(TAG, "FirstBootOrExternalInterruptWakeup");

		/* Wait for the callback to set the WIFI_CONNECTED_BIT in the
		   event group.
		*/
		xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_BIT,
							false, true, portMAX_DELAY);
		ESP_LOGI(TAG, "Connected to AP");

		int err = getaddrinfo(WEB_SERVER, "80", &hints, &res);

		if(err != 0 || res == NULL) {
			ESP_LOGE(TAG, "DNS lookup failed err=%d res=%p", err, res);
			vTaskDelay(1000 / portTICK_PERIOD_MS);
			continue;
		}

		/* Code to print the resolved IP.

		   Note: inet_ntoa is non-reentrant, look at ipaddr_ntoa_r for "real" code */
		addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
		ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", inet_ntoa(*addr));

		s = socket(res->ai_family, res->ai_socktype, 0);
		if(s < 0) {
			ESP_LOGE(TAG, "... Failed to allocate socket.");
			freeaddrinfo(res);
			vTaskDelay(1000 / portTICK_PERIOD_MS);
			continue;
		}
		ESP_LOGI(TAG, "... allocated socket");

		if(connect(s, res->ai_addr, res->ai_addrlen) != 0) {
			ESP_LOGE(TAG, "... socket connect failed errno=%d", errno);
			close(s);
			freeaddrinfo(res);
			vTaskDelay(4000 / portTICK_PERIOD_MS);
			continue;
		}

		ESP_LOGI(TAG, "... connected");
		freeaddrinfo(res);
		sprintf(data,"7C0602%02X%02X020000",waterState,batteryLevel);
		sprintf(web_url, "http://www.defond.com/sigfox/saveData.ashx?id=%s&time=%10ld&data=%s",device_id,now,data);
		sprintf(http_request, "GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: esp-idf/1.0 esp32\r\n\r\n",web_url, WEB_SERVER);

		ESP_LOGI(TAG, "http_request:%s",http_request);
//		ESP_LOGI(TAG, "REQUEST_dummy:%s",REQUEST_dummy);


		if (write(s, http_request, strlen(http_request)) < 0) {
			ESP_LOGE(TAG, "... socket send failed");
			close(s);
			vTaskDelay(4000 / portTICK_PERIOD_MS);
			continue;
		}
		ESP_LOGI(TAG, "... socket send success");

		struct timeval receiving_timeout;
		receiving_timeout.tv_sec = 5;
		receiving_timeout.tv_usec = 0;
		if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &receiving_timeout,
				sizeof(receiving_timeout)) < 0) {
			ESP_LOGE(TAG, "... failed to set socket receiving timeout");
			close(s);
			vTaskDelay(4000 / portTICK_PERIOD_MS);
			continue;
		}
		ESP_LOGI(TAG, "... set socket receiving timeout success");

		/* Read HTTP response */
		do {
			bzero(recv_buf, sizeof(recv_buf));
			r = read(s, recv_buf, sizeof(recv_buf)-1);
			for(int i = 0; i < r; i++) {
				putchar(recv_buf[i]);
			}
		} while(r > 0);

		ESP_LOGI(TAG, "... done reading from socket. Last read return=%d errno=%d\r\n", r, errno);
		close(s);
		/*
		for(int countdown = 10; countdown >= 0; countdown--) {
			ESP_LOGI(TAG, "%d... ", countdown);
			vTaskDelay(1000 / portTICK_PERIOD_MS);
		}
*/
		vTaskDelay(1000 / portTICK_PERIOD_MS);

		printf("Enabling EXT1 wakeup on pins GPIO%d\n", ext_wakeup_pin_2);
		if(waterState == WATER_NO_LEAK)
			esp_sleep_enable_ext1_wakeup(ext_wakeup_pin_2_mask, ESP_EXT1_WAKEUP_ANY_HIGH);
		else esp_sleep_enable_ext1_wakeup(ext_wakeup_pin_2_mask, ESP_EXT1_WAKEUP_ALL_LOW);

		esp_sleep_enable_ext0_wakeup(ext_wakeup_pin_1,1);  //OTA button

		if(WiFI_connected)
			ESP_ERROR_CHECK(esp_wifi_disconnect());
		if(WiFi_started)
		{
			ESP_ERROR_CHECK(esp_wifi_stop() );
			WiFi_started = 0;
		}
//		if(!WakeUpTimerSet)
	//	{
	//		WakeUpTimerSet = 1;
		user_set_wake_up_timer(now);
	//	}

		ESP_LOGI(TAG,"Firmware Version V1\r\n");
		rtc_gpio_isolate(GPIO_NUM_12);
   //     CHECK_ERROR_CODE(esp_task_wdt_reset(), ESP_OK);  //Comment this line to trigger a TWDT timeout
        SleepSetup();
		esp_deep_sleep_start();

//      }
//     else  ESP_LOGI(TAG, "Accidential wake up:%d", my_wake_up_cause);

    }

//    while (1);
}


void app_main()
{
    //Initialize NVS

    ESP_LOGI(TAG, "***********System Start***********************");
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
      ESP_ERROR_CHECK(nvs_flash_erase());
      ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);
    
#if EXAMPLE_ESP_WIFI_MODE_AP
    ESP_LOGI(TAG, "ESP_WIFI_MODE_AP");
    wifi_init_softap();
#else
    ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
    wifi_init_sta();
#endif /*EXAMPLE_ESP_WIFI_MODE_AP*/


    time_t now;
    struct tm timeinfo;
    time(&now);
    localtime_r(&now, &timeinfo);
    // Is time set? If not, tm_year will be (1970 - 1900).
    if (timeinfo.tm_year < (2016 - 1900)) {
        ESP_LOGI(TAG, "Time is not set yet. Connecting to WiFi and getting time over NTP.");
        obtain_time();
        // update 'now' variable with current time
        time(&now);
    }
    char strftime_buf[64];

    // Set timezone to China Standard Time
    setenv("TZ", "CST-8", 1);
    tzset();
    localtime_r(&now, &timeinfo);
    strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
    ESP_LOGI(TAG, "The current date/time in Hong Kong is: %s", strftime_buf);

/*
    CHECK_ERROR_CODE(esp_task_wdt_init(TWDT_TIMEOUT_S, true), ESP_OK);

    //Subscribe Idle Tasks to TWDT if they were not subscribed at startup
#ifndef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0
    esp_task_wdt_add(xTaskGetIdleTaskHandleForCPU(0));
#endif
#ifndef CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1
    esp_task_wdt_add(xTaskGetIdleTaskHandleForCPU(1));
#endif

    //Create user tasks and add them to watchdog
    for(int i = 0; i < 2; i++){
        xTaskCreatePinnedToCore(reset_task, "reset task", 1024, NULL, 10, &task_handles[i], i);
    }
*/
    xTaskCreate(&http_get_task, "http_get_task", 8192, NULL, 5, NULL); // incrase buffer size 8192 if ota fail

}


static void obtain_time(void)
{
//    ESP_ERROR_CHECK( nvs_flash_init() );
 //   initialise_wifi();
    xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_BIT,
                        false, true, portMAX_DELAY);
    initialize_sntp();

    // wait for time to be set
    time_t now = 0;
    struct tm timeinfo = { 0 };
    int retry = 0;
    const int retry_count = 10;
    while(timeinfo.tm_year < (2016 - 1900) && ++retry < retry_count) {
        ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, retry_count);
        vTaskDelay(2000 / portTICK_PERIOD_MS);
        time(&now);
        localtime_r(&now, &timeinfo);
    }

 //   ESP_ERROR_CHECK( esp_wifi_stop() );
}

static void initialize_sntp(void)
{
    ESP_LOGI(TAG, "Initializing SNTP");
    sntp_setoperatingmode(SNTP_OPMODE_POLL);
    sntp_setservername(0, "pool.ntp.org");
    sntp_init();
}

void user_set_wake_up_timer(time_t now)
{
	struct tm *info;
	int wakeup_time_sec;
	info = localtime( &now );

	wakeup_time_sec = 24*60*60 - info->tm_hour*60*60 - info->tm_min*60 - info->tm_sec;

//	wakeup_time_sec

//	wakeup_time_sec = 10;
	printf("Enabling timer wakeup, %ds\n", wakeup_time_sec);

	esp_sleep_enable_timer_wakeup(1000000LL * wakeup_time_sec);

}

void UserOTA(void)
{
    ESP_LOGI(TAG, "Starting OTA example...");

    /* Wait for the callback to set the CONNECTED_BIT in the
       event group.
    */

	xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_BIT,
						false, true, portMAX_DELAY);

    ESP_LOGI(TAG, "Connect to Wifi ! Start to Connect to Server....");

    esp_http_client_config_t config = {
        .url = OTA_URL,
        .cert_pem = (char *)server_cert_pem_start,
        .event_handler = _http_event_handler,
    };
    esp_err_t ret = esp_https_ota(&config);
    if (ret == ESP_OK) {
        esp_restart();
    } else {
        ESP_LOGE(TAG, "Firmware Upgrades Failed");
    }
    while (1) {
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

void SleepSetup(void)
{

	nvs_flash_deinit();
	uart_driver_delete(UART_NUM_0);
    gpio_config_t io_conf;

    io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
    //bit mask of the pins, use GPIO4/5 here
    io_conf.pin_bit_mask = 1ULL<<1;
    //set as input mode
    io_conf.mode = GPIO_MODE_INPUT;
    //enable pull-up mode
    io_conf.pull_up_en = 1;
    gpio_config(&io_conf);


    io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
    //bit mask of the pins, use GPIO4/5 here
    io_conf.pin_bit_mask = 1ULL<<3;
    //set as input mode
    io_conf.mode = GPIO_MODE_INPUT;
    //enable pull-up mode
    io_conf.pull_up_en = 1;
    gpio_config(&io_conf);


}
