/***************************************************************************//**
 * @file app.c
 * @brief Silicon Labs Empty Example Project
 *
 * This example demonstrates the bare minimum needed for a Blue Gecko C application
 * that allows Over-the-Air Device Firmware Upgrading (OTA DFU). The application
 * starts advertising after boot and restarts advertising after a connection is closed.
 *******************************************************************************
 * # License
 * <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
 *******************************************************************************
 *
 * The licensor of this software is Silicon Laboratories Inc. Your use of this
 * software is governed by the terms of Silicon Labs Master Software License
 * Agreement (MSLA) available at
 * www.silabs.com/about-us/legal/master-software-license-agreement. This
 * software is distributed to you in Source Code format and is governed by the
 * sections of the MSLA applicable to Source Code.
 *
 ******************************************************************************/

/* Bluetooth stack headers */
#include "bg_types.h"
#include "native_gecko.h"
#include "gatt_db.h"

#include "app.h"
#include "em_i2c.h"
#include "em_cmu.h"

/* Print boot message */
static void bootMessage(struct gecko_msg_system_boot_evt_t *bootevt);
static void SI7021_startConversion(void);
static void SI7021_getTempHumi(void);
static void setAdvScanData(void);
//static struct gecko_msg_le_gap_bt5_set_adv_data_rsp_t *rr;

/* Flag for indicating DFU Reset must be performed */
static uint8_t boot_to_dfu = 0;

// Si7021 stuff
#define TIMER_ID_SENSOR		0
#define TIMER_ID_CONVERT	1
#define SI7021_SLAVE_ADDRESS       	0x80    // sensor slave address
static int32_t temp;
static uint32_t humi;
static uint8 temp_humi[2];

typedef struct
{
	uint8 len_flags;
	uint8 type_flags;
	uint8 val_flags;

	uint8 len_manuf;
	uint8 type_manuf;
	/* First two bytes must contain the manufacturer ID (little-endian order) */
	uint8 company_LO;
	uint8 company_HI;
	char manuf_name[13];
} customAdv_t;

typedef struct
{
	/* length of the name AD element is variable, adding it last to keep things simple */
	uint8 len_name;
	uint8 type_name;
	char name[16];
} customScanResp_t;

customAdv_t advData;
customScanResp_t scanData;

/* Main application */
void appMain(gecko_configuration_t *pconfig)
{
#if DISABLE_SLEEP > 0
  pconfig->sleep.flags = 0;
#endif

  /* Initialize debug prints. Note: debug prints are off by default. See DEBUG_LEVEL in app.h */
  initLog();

  I2C_Init_TypeDef i2cInit = I2C_INIT_DEFAULT;
  CMU_ClockEnable(cmuClock_HFPER, true);
  CMU_ClockEnable(cmuClock_I2C1, true);
  GPIO_PinModeSet(gpioPortF, 9, gpioModePushPull, 1);
  // Use location 17: SDA - Pin C4, SCL - Pin C5
  // Output value must be set to 1 to not drive lines low... We set
  // SCL first, to ensure it is high before changing SDA.
  GPIO_PinModeSet(gpioPortC, 5, gpioModeWiredAnd, 1);
  GPIO_PinModeSet(gpioPortC, 4, gpioModeWiredAnd, 1);
  // Enable pins at location 17 (which is used on the DVK)
  I2C1->ROUTELOC0 = I2C_ROUTELOC0_SDALOC_LOC17 |
      			  I2C_ROUTELOC0_SCLLOC_LOC17;
  I2C1->ROUTEPEN = 3;
  I2C_Init(I2C1, &i2cInit);

  // Init LEDs  LED0=red  LED1=green
  GPIO_PinModeSet(BSP_LED0_PORT, BSP_LED0_PIN, gpioModePushPull, 0);
  GPIO_PinModeSet(BSP_LED1_PORT, BSP_LED1_PIN, gpioModePushPull, 0);

  setAdvScanData();
  /* Initialize stack */
  gecko_init(pconfig);

  while (1) {
    /* Event pointer for handling events */
    struct gecko_cmd_packet* evt;

    /* if there are no events pending then the next call to gecko_wait_event() may cause
     * device go to deep sleep. Make sure that debug prints are flushed before going to sleep */
    if (!gecko_event_pending()) {
      flushLog();
    }

    /* Check for stack event. This is a blocking event listener. If you want non-blocking please see UG136. */
    evt = gecko_wait_event();

    /* Handle events */
    switch (BGLIB_MSG_ID(evt->header)) {
      /* This boot event is generated when the system boots up after reset.
       * Do not call any stack commands before receiving the boot event.
       * Here the system is set to start advertising immediately after boot procedure. */
      case gecko_evt_system_boot_id:
        bootMessage(&(evt->data.evt_system_boot));
//        ("boot event - starting advertising\r\n");
        gecko_cmd_system_set_tx_power(0);								// set 0 dBm Tx power
        gecko_cmd_le_gap_set_advertise_tx_power(0, 0);					// set adv. power
        gecko_cmd_le_gap_set_advertise_timing(0, 1600, 1600, 0, 0);		// set 1 sec adv. period
        gecko_cmd_le_gap_set_conn_timing_parameters(80, 180, 5, 100, 0, 0xffff); // set min connect interval 100ms
//        gecko_cmd_le_gap_start_advertising(0, le_gap_general_discoverable, le_gap_connectable_scannable);
        gecko_cmd_le_gap_bt5_set_adv_data(0, 0, 19, (const uint8_t*)&advData);
        gecko_cmd_le_gap_bt5_set_adv_data(0, 1, 17, (const uint8_t*)&scanData);
        gecko_cmd_le_gap_start_advertising(0, le_gap_user_data, le_gap_connectable_scannable);
      break;

      case gecko_evt_le_connection_opened_id:
//        ("connection opened\r\n");
        gecko_cmd_hardware_set_soft_timer(32768,TIMER_ID_SENSOR,0);  // start software timer for temp/humi measurement
        GPIO_PinOutSet(BSP_LED0_PORT, BSP_LED0_PIN);
      break;

      case gecko_evt_le_connection_closed_id:
//        ("connection closed, reason: 0x%2.2x\r\n", evt->data.evt_le_connection_closed.reason);
        GPIO_PinOutClear(BSP_LED0_PORT, BSP_LED0_PIN);
        gecko_cmd_hardware_set_soft_timer(0,0,0);  			// stop software timer

        /* Check if need to boot to OTA DFU mode */
        if (boot_to_dfu) {
          /* Enter to OTA DFU mode */
          gecko_cmd_system_reset(2);
        } else {
          /* Restart advertising after client has disconnected */
          gecko_cmd_le_gap_start_advertising(0, le_gap_general_discoverable, le_gap_connectable_scannable);
        }
      break;

      /* Events related to OTA upgrading
         ----------------------------------------------------------------------------- */

      /* Check if the user-type OTA Control Characteristic was written.
       * If ota_control was written, boot the device into Device Firmware Upgrade (DFU) mode. */
      case gecko_evt_gatt_server_user_write_request_id:

        if (evt->data.evt_gatt_server_user_write_request.characteristic == gattdb_ota_control) {
          /* Set flag to enter to OTA mode */
          boot_to_dfu = 1;
          /* Send response to Write Request */
          gecko_cmd_gatt_server_send_user_write_response(
            evt->data.evt_gatt_server_user_write_request.connection,
            gattdb_ota_control,
            bg_err_success);

          /* Close connection to enter to DFU OTA mode */
          gecko_cmd_le_connection_close(evt->data.evt_gatt_server_user_write_request.connection);
        }
      break;

      /* Add additional event handlers as your application requires */
      //===============================================================================================
      case gecko_evt_hardware_soft_timer_id:
    	  switch (evt->data.evt_hardware_soft_timer.handle) {
    	  	  case TIMER_ID_SENSOR:
    	  		SI7021_startConversion();
    	  		gecko_cmd_hardware_set_soft_timer(1638,TIMER_ID_CONVERT,1);  // start single shot 50ms software timer temp/humi conversion
    	  	  break;

    	  	  case TIMER_ID_CONVERT:
    	  		  SI7021_getTempHumi();
//    	  		  printLog("%dC   %d%%\r\n", (int)temp, (int)humi);
    	  		  gecko_cmd_gatt_server_write_attribute_value(gattdb_air_temp, 0, 1, (const uint8*)&temp);
    	    	  gecko_cmd_gatt_server_write_attribute_value(gattdb_air_humi, 0, 1, (const uint8*)&humi);
    	    	  gecko_cmd_gatt_server_send_characteristic_notification(0xFF, gattdb_temp_humi_notification, 2, (const uint8*)&temp_humi);
    	  	  break;
    	  }
      break;
      default:
        break;
    }
  }
}

/* Print stack version and local Bluetooth address as boot message */
static void bootMessage(struct gecko_msg_system_boot_evt_t *bootevt)
{
#if DEBUG_LEVEL
  bd_addr local_addr;
  int i;

  printLog("stack version: %u.%u.%u\r\n", bootevt->major, bootevt->minor, bootevt->patch);
  local_addr = gecko_cmd_system_get_bt_address()->address;

  printLog("local BT device address: ");
  for (i = 0; i < 5; i++) {
    printLog("%2.2x:", local_addr.addr[5 - i]);
  }
  printLog("%2.2x\r\n", local_addr.addr[0]);
#endif
}

static void SI7021_startConversion()             // request new temp/humi conversion
{
    I2C_TransferSeq_TypeDef seq;
    uint8_t data[1];
    seq.addr = SI7021_SLAVE_ADDRESS;
    seq.flags = I2C_FLAG_WRITE;
    data[0] = 0xF5;								// command to measure temp & humi
    seq.buf[0].data = data;
    seq.buf[0].len = 1;
    I2C_TransferReturn_TypeDef ret = I2C_TransferInit(I2C1, &seq);
    while (ret == i2cTransferInProgress)
      ret = I2C_Transfer(I2C1);
}

static void SI7021_getTempHumi()                // read temp/humi data from sensor
{
    I2C_TransferSeq_TypeDef seq;
    uint8_t dataW[1];
    uint8_t dataR[2];
    seq.addr = SI7021_SLAVE_ADDRESS;
    seq.flags = I2C_FLAG_READ;
    seq.buf[0].data = dataR;
    seq.buf[0].len = 2;							// read 2 bytes of humi
    I2C_TransferReturn_TypeDef ret = I2C_TransferInit(I2C1, &seq);
    while (ret == i2cTransferInProgress)
      ret = I2C_Transfer(I2C1);
    humi = ((((dataR[0] << 8) + dataR[1])*125 + 0x8000) >> 16);	// compute humi
    if (humi > 99) humi = 99;
    temp_humi[1] = humi;

	dataW[0] = 0xE0;							// get temp from the last humi conversion
    seq.flags = I2C_FLAG_WRITE_READ;
    seq.buf[0].data = dataW;
    seq.buf[0].len = 1;
    seq.buf[1].data = dataR;
    seq.buf[1].len = 2;							// read 2 bytes of temp
    ret = I2C_TransferInit(I2C1, &seq);
    while (ret == i2cTransferInProgress)
      ret = I2C_Transfer(I2C1);
	temp = ((((dataR[0] << 8) + dataR[1])*176 + 0x8000) >> 16) - 47;	// compute temp in Celcius
//    temp = 9*temp/5 + 32;                                          // convert temp to Fahrenheit
	temp_humi[0] = temp;
}

static void setAdvScanData(void)
{
	advData.len_flags = 0x02;
	advData.type_flags = 0x01;
	advData.val_flags = 0x06;					// general discoverable mode

	advData.len_manuf = 15;
	advData.type_manuf = 0xFF;
	advData.company_LO = 0xFF;					// Silabs company ID (0x02FF)
	advData.company_HI = 0x02;
	strncpy(advData.manuf_name, "Silicon Labs", 12);

	scanData.len_name = 16;
	scanData.type_name = 9;						// full device name
	strncpy(scanData.name, "Sergei's server", 15);
}
