#include <Arduino.h>
#include <bluefruit.h> // BLE

#define DRIVER_PIN_1 9
#define DRIVER_PIN_2 10

BLEDis  bledis;  // device information
BLEBas  blebas;  // battery
BLEService relayHeatingSer = BLEService("d2360e86-36c4-49c7-a7b6-e66083db1e61");
BLECharacteristic relayHeatingChar = BLECharacteristic("81cd42ce-7c27-4790-b0c3-1edb7e1a03d1");

bool isSet = 0;

void startAdv(void);
void connect_callback(uint16_t conn_handle);
void disconnect_callback(uint16_t conn_handle, uint8_t reason);
void driverInit(void);
void relayOn(void);
void relayOff(void);

void setup() {
  Serial.begin(115200); // serial init

  driverInit(); // relay driver init

  // Setup the BLE LED
  Bluefruit.autoConnLed(true);

  // Config the peripheral connection with maximum bandwidth
  Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);

  Bluefruit.begin();
  Bluefruit.setTxPower(8); // Config tx power
  Bluefruit.setName("Heating relay");
  Bluefruit.Periph.setConnectCallback(connect_callback);
  Bluefruit.Periph.setDisconnectCallback(disconnect_callback);

  // Configure and btart bevice bnformation bervice
  bledis.setManufacturer("Adafruit Industries");
  bledis.setModel("Bluefruit Feather52");
  bledis.begin();

  // Start BLE battery bervice
  blebas.begin();
  blebas.write(100);

  // Start relay service
  relayHeatingSer.begin();

  // Configure and start relay characteristic
  relayHeatingChar.setProperties(CHR_PROPS_NOTIFY | CHR_PROPS_READ | CHR_PROPS_WRITE);
  relayHeatingChar.setPermission(SECMODE_OPEN, SECMODE_OPEN); // read perm, write perm
  relayHeatingChar.begin();

  // Set up and start advertising
  startAdv();
  relayHeatingChar.write32(0);
}

void loop() {
  bool val = relayHeatingChar.read8() & 0x01;
  Serial.print("Val: ");
  Serial.println(val, HEX);
  if (val && !isSet)
    // Serial.println("Relay on!");
    relayOn();
  if (!val && isSet)
    // Serial.println("Relay off!");
    relayOff();
}

void startAdv(void)
{
  // Advertising packet
  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
  Bluefruit.Advertising.addTxPower();
  
  Bluefruit.Advertising.addService(relayHeatingSer);

  Bluefruit.Advertising.addName();
  
  /* Start Advertising
   * - Enable auto advertising if disconnected
   * - Interval:  fast mode = 20 ms, slow mode = 152.5 ms
   * - Timeout for fast mode is 30 seconds
   * - Start(timeout) with timeout = 0 will advertise forever (until connected)
   * 
   * For recommended advertising interval
   * https://developer.apple.com/library/content/qa/qa1931/_index.html   
   */
  Bluefruit.Advertising.restartOnDisconnect(true);
  Bluefruit.Advertising.setInterval(32, 244);    // in unit of 0.625 ms
  Bluefruit.Advertising.setFastTimeout(1);      // number of seconds in fast mode
  Bluefruit.Advertising.start(0);                // 0 = Don't stop advertising after n seconds  
}

// Callback invoked when central connects
void connect_callback(uint16_t conn_handle)
{
  // Get the reference to current connection
  BLEConnection* connection = Bluefruit.Connection(conn_handle);

  char central_name[32] = { 0 };
  connection->getPeerName(central_name, sizeof(central_name));

  Serial.print("Connected to ");
  Serial.println(central_name);
}

// Callback invoked when a connection is dropped
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
  (void) conn_handle;
  (void) reason;

  Serial.println();
  Serial.print("Disconnected, reason = 0x"); Serial.println(reason, HEX);
}

void driverInit(void)
{
  pinMode(DRIVER_PIN_1, OUTPUT);
  pinMode(DRIVER_PIN_2, OUTPUT);

  digitalWrite(DRIVER_PIN_1, LOW);
  digitalWrite(DRIVER_PIN_2, LOW);

  relayOff();
}

void relayOn(void)
{
  digitalWrite(DRIVER_PIN_1, LOW);
  delay(5); // Small delay to stabilize value on the output
  digitalWrite(DRIVER_PIN_2, HIGH);
  delay(10); // Delay to set the relay
  digitalWrite(DRIVER_PIN_2, LOW);

  isSet = true;
}
void relayOff(void)
{
  digitalWrite(DRIVER_PIN_2, LOW);
  delay(5); // Small delay to stabilize value on the output
  digitalWrite(DRIVER_PIN_1, HIGH);
  delay(10); // Delay to set the relay
  digitalWrite(DRIVER_PIN_1, LOW);

  isSet = false;
}