
/* ===== pfod Command for Menu_1 ====
pfodApp msg {.} --> {,<bg bl><b><+5>~ESP32 Led
<-5>(Sparkfun ESP32 Thing)`0~V7|A`0~Led ~%`255`0~100~0~}
 */
// Using ESP32 based board programmed via Arduino IDE
// follow the steps given on http://www.forward.com.au/pfod/ESP32/index.html to install ESP32 support for Arduino IDE
//Based on Neil Kolban example https://github.com/nkolban/ESP32_BLE_Arduino
/* Code generated by pfodDesignerV3 V3.0.3305
 */
/*
 * (c)2014-2018 Forward Computing and Control Pty. Ltd.
 * NSW Australia, www.forward.com.au
 * This code is not warranted to be fit for any purpose. You may only use it at your own risk.
 * This generated code may be freely used for both private and commercial use
 * provide this copyright is maintained.
 */

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLE2902.h>
#define DEBUG

// Download pfodESP32BufferedClient library from http://www.forward.com.au/pfod/pfodParserLibraries/index.html
// pfodESP32BufferedClient.zip contains pfodESP32BufferedClient and pfodESP32Utils
#include <pfodESP32Utils.h>
// download the libraries from http://www.forward.com.au/pfod/pfodParserLibraries/index.html
// pfodParser.zip V3.26+ contains pfodParser, pfodSecurity, pfodBLEBufferedSerial, pfodSMS and pfodRadio
#include <pfodParser.h>

#include <pfodBLEBufferedSerial.h> // used to prevent flooding bluetooth sends
int swap01(int); // method prototype for slider end swaps

// =========== pfodBLESerial definitions
const char* localName = "ESP32 BLE";  // <<<<<<  change this string to customize the adverised name of your board 
class pfodBLESerial : public Stream, public BLEServerCallbacks, public BLECharacteristicCallbacks {
  public:
    pfodBLESerial(); void begin(); void poll(); size_t write(uint8_t); size_t write(const uint8_t*, size_t); int read();
    int available(); void flush(); int peek(); void close(); bool isConnected();
    static void addReceiveBytes(const uint8_t* bytes, size_t len);
    const static uint8_t pfodEOF[1]; const static char* pfodCloseConnection;
    volatile static bool connected;
    void onConnect(BLEServer* serverPtr);
    void onDisconnect(BLEServer* serverPtr);
    void onWrite(BLECharacteristic *pCharacteristic);

  private:
    static const int BLE_MAX_LENGTH = 20;
    static const int BLE_RX_MAX_LENGTH = 256; static volatile size_t rxHead; static volatile size_t rxTail;
    volatile static uint8_t rxBuffer[BLE_RX_MAX_LENGTH];
    size_t txIdx;  uint8_t txBuffer[BLE_MAX_LENGTH];
};
volatile size_t pfodBLESerial::rxHead = 0; volatile size_t pfodBLESerial::rxTail = 0;
volatile uint8_t pfodBLESerial::rxBuffer[BLE_RX_MAX_LENGTH]; const uint8_t pfodBLESerial::pfodEOF[1] = {(uint8_t) - 1};
const char* pfodBLESerial::pfodCloseConnection = "{!}"; volatile bool pfodBLESerial::connected = false;

#define SERVICE_UUID           "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
BLEServer *serverPtr = NULL;
BLECharacteristic * characteristicTXPtr;
// =========== end pfodBLESerial definitions

pfodParser parser("V8"); // create a parser with menu version string to handle the pfod messages
 // create a parser to handle the pfod messages
pfodBLESerial bleSerial; // create a BLE serial connection
pfodBLEBufferedSerial bleBufferedSerial; // create a BLE serial connection

// give the board pins names, if you change the pin number here you will change the pin controlled
int cmd_A_var; // name the variable for 'Led'
const int cmd_A_pin = 5; // name the output 5 for 'Led'
const int cmd_A_channel = 0; // channel to use for this PWM output 

// the setup routine runs once on reset:
void setup() {
  cmd_A_var = 0;
  ledcAttachPin(cmd_A_pin,cmd_A_channel); // assign pin to channel
  ledcSetup(cmd_A_channel, 490, 8); // 490hz PWM, 8-bit resolution to match Arduino Uno
  pfodESP32Utils::analogWrite(cmd_A_channel,cmd_A_var); // set PWM output

#ifdef DEBUG
  Serial.begin(115200);
  Serial.println();
#endif

  // Create the BLE Device
  BLEDevice::init(localName);
  // Create the BLE Server
  serverPtr = BLEDevice::createServer();
  serverPtr->setCallbacks(&bleSerial);
  // Create the BLE Service
  BLEService *servicePtr = serverPtr->createService(SERVICE_UUID);
  // Create a BLE Characteristic
  characteristicTXPtr = servicePtr->createCharacteristic(CHARACTERISTIC_UUID_TX, BLECharacteristic::PROPERTY_NOTIFY);
  characteristicTXPtr->addDescriptor(new BLE2902());
  BLECharacteristic * characteristicRXPtr = servicePtr->createCharacteristic(CHARACTERISTIC_UUID_RX, BLECharacteristic::PROPERTY_WRITE);
  characteristicRXPtr->setCallbacks(&bleSerial);

  // Start the service
  servicePtr->start();
  // Start advertising
  serverPtr->getAdvertising()->start();
#ifdef DEBUG
  Serial.println("BLE Server and Advertising started");
#endif

  bleSerial.begin();
  // connect parser
  parser.connect(bleBufferedSerial.connect(&bleSerial)); // connect the parser to the i/o stream via buffer


  // <<<<<<<<< Your extra setup code goes here
}

// the loop routine runs over and over again forever:
void loop() {
  uint8_t cmd = parser.parse(); // parse incoming data from connection
  // parser returns non-zero when a pfod command is fully parsed
  if (cmd != 0) { // have parsed a complete msg { to }
    uint8_t* pfodFirstArg = parser.getFirstArg(); // may point to \0 if no arguments in this msg.
    pfod_MAYBE_UNUSED(pfodFirstArg); // may not be used, just suppress warning
    long pfodLongRtn; // used for parsing long return arguments, if any
    pfod_MAYBE_UNUSED(pfodLongRtn); // may not be used, just suppress warning
    if ('.' == cmd) {
      // pfodApp has connected and sent {.} , it is asking for the main menu
      if (!parser.isRefresh()) {
        sendMainMenu(); // send back the menu designed
      } else {
        sendMainMenuUpdate(); // menu is cached just send update
      }

    // now handle commands returned from button/sliders
    } else if('A'==cmd) { // user moved PWM slider -- 'Led'
      // in the main Menu of Menu_1 
      parser.parseLong(pfodFirstArg,&pfodLongRtn); // parse first arg as a long
      cmd_A_var = (int)pfodLongRtn; // set variable
      pfodESP32Utils::analogWrite(cmd_A_channel,cmd_A_var); // set PWM output
      sendMainMenuUpdate(); // always send back a pfod msg otherwise pfodApp will disconnect.

    } else if ('!' == cmd) {
      // CloseConnection command
      closeConnection(parser.getPfodAppStream());
    } else {
      // unknown command
      parser.print(F("{}")); // always send back a pfod msg otherwise pfodApp will disconnect.
    }
  }
  //  <<<<<<<<<<<  Your other loop() code goes here 
  
}

void closeConnection(Stream *io) {
    // nothing special here
}

void sendMainMenu() {
  // !! Remember to change the parser version string
  //    every time you edit this method
  parser.print(F("{,"));  // start a Menu screen pfod message
  // send menu background, format, prompt, refresh and version
  parser.print(F("<bg bl><b><+5>~ESP32 Led\n<-5>(Sparkfun ESP32 Thing)`0"));
  parser.sendVersion(); // send the menu version 
  // send menu items
  parser.print(F("|A"));
  parser.print('`');
  parser.print(cmd_A_var); // output the current PWM setting
  parser.print(F("~Led ~%`255`0~100~0~"));
  parser.print(F("}"));  // close pfod message
}

void sendMainMenuUpdate() {
  parser.print(F("{;"));  // start an Update Menu pfod message
  // send menu items
  parser.print(F("|A"));
  parser.print('`');
  parser.print(cmd_A_var); // output the current PWM setting
  parser.print(F("}"));  // close pfod message
  // ============ end of menu ===========
}
// ========== pfodBLESerial methods
pfodBLESerial::pfodBLESerial() {}

bool pfodBLESerial::isConnected() {
  return (connected);
}
void pfodBLESerial::begin() {}

void pfodBLESerial::close() {}

void pfodBLESerial::poll() {}

size_t pfodBLESerial::write(const uint8_t* bytes, size_t len) {
  for (size_t i = 0; i < len; i++) {  write(bytes[i]);  }
  return len; // just assume it is all written
}

size_t pfodBLESerial::write(uint8_t b) {
  if (!isConnected()) { return 1; }
  txBuffer[txIdx++] = b;
  if ((txIdx == sizeof(txBuffer)) || (b == ((uint8_t)'\n')) || (b == ((uint8_t)'}')) ) {
    flush(); // send this buffer if full or end of msg or rawdata newline
  }
  return 1;
}

int pfodBLESerial::read() {
  if (rxTail == rxHead) { return -1; }
  // note increment rxHead befor writing
  // so need to increment rxTail befor reading
  rxTail = (rxTail + 1) % sizeof(rxBuffer);
  uint8_t b = rxBuffer[rxTail];
  return b;
}

// called as part of parser.parse() so will poll() each loop()
int pfodBLESerial::available() {
  flush(); // send any pending data now. This happens at the top of each loop()
  int rtn = ((rxHead + sizeof(rxBuffer)) - rxTail ) % sizeof(rxBuffer);
  return rtn;
}

void pfodBLESerial::flush() {
  if (txIdx == 0) { return; }
  characteristicTXPtr->setValue((uint8_t*)txBuffer, txIdx);
  txIdx = 0;
  characteristicTXPtr->notify();
}

int pfodBLESerial::peek() {
  if (rxTail == rxHead) { return -1; }
  size_t nextIdx = (rxTail + 1) % sizeof(rxBuffer);
  uint8_t byte = rxBuffer[nextIdx];
  return byte;
}

void pfodBLESerial::addReceiveBytes(const uint8_t* bytes, size_t len) {
  // note increment rxHead befor writing
  // so need to increment rxTail befor reading
  for (size_t i = 0; i < len; i++) {
    rxHead = (rxHead + 1) % sizeof(rxBuffer);
    rxBuffer[rxHead] = bytes[i];
  }
}

//=========== ESP32 BLE callback methods
void pfodBLESerial:: onConnect(BLEServer* serverPtr) {
  // clear parser with -1 in case partial message left, should not be one
  addReceiveBytes(bleSerial.pfodEOF, sizeof(pfodEOF));
  connected = true;
}

void pfodBLESerial::onDisconnect(BLEServer* serverPtr) {
  // clear parser with -1 and insert {!} incase connection just lost
  addReceiveBytes(bleSerial.pfodEOF, sizeof(pfodEOF));
  addReceiveBytes((const uint8_t*)pfodCloseConnection, sizeof(pfodCloseConnection));
  connected = false;
}

void pfodBLESerial::onWrite(BLECharacteristic *pCharacteristic) {
  std::string rxValue = pCharacteristic->getValue();
  uint8_t *data = (uint8_t*)rxValue.data();
  size_t len = rxValue.length();
  addReceiveBytes((const uint8_t*)data, len);
}
//======================= end pfodBLESerial methods


int swap01(int in) {
  return (in==0)?1:0;
}
// ============= end generated code =========
 
