/*
 * A demo sketch that uses the AceButton Library
 * (https://github.com/bxparks/AceButton) and the CapitiveSense Library
 * (https://github.com/PaulStoffregen/CapacitiveSensor) to detect touches and
 * clicks of a capacitive switch.
 *
 * Prompted by questions from Gaston Loos.
 *
 * ESP8266 and ESP32 requires the development version of
 * https://github.com/PaulStoffregen/CapacitiveSensor (directly from GitHub)
 * because the fixes for those boards have not been released as of 2019-08-11.
 *
 * This program cannot be compiled using
 * https://github.com/bxparks/EpoxyDuino because the external
 * CapacitiveSensor library cannot be compiled with it.
 *
 * Brian T. Park 2018
 */

#include <CapacitiveSensor.h>
#include <AceButton.h>
using namespace ace_button;

/**
 * A subclass of ButtonConfig that allows a CapacitiveSensor to emulate a
 * mechanical switch connected to a pull-up resistor on the input pin. A "touch"
 * sends a LOW signal, just like a mechnical switch.
 */
class CapacitiveConfig: public ButtonConfig {
  public:
    CapacitiveConfig(CapacitiveSensor& sensor):
      mSensor(sensor) {}

  protected:
    // Number of iterations to sample the capacitive switch. Higher number
    // provides better smoothing but increases the time taken for a single read.
    static const uint8_t kSamples = 30;

    // The threshold value which is considered to be a "touch" on the switch.
    static const long kTouchThreshold = 100;

    int readButton(uint8_t /*pin*/) override {
      long total =  mSensor.capacitiveSensor(kSamples);
      return (total > kTouchThreshold) ? LOW : HIGH;
    }

  private:
    CapacitiveSensor& mSensor;
};

// Timeout for a single read of the capacitive switch.
static const unsigned long TIMEOUT_MILLIS = 10;

// I used a 1M resistor between pins 4 (send) & metal plate, and a 1K resistor
// between the plate and pin 6 (receive). Try adjusting the
// CapacitiveConfig::kTouchThreshold value for other resistor values.
CapacitiveSensor capSensor(4, 6);

CapacitiveConfig buttonConfig(capSensor);
AceButton button(&buttonConfig);

void handleEvent(AceButton* /* button */, uint8_t eventType,
    uint8_t /* buttonState */) {
  switch (eventType) {
    case AceButton::kEventPressed:
      Serial.println(F("Pressed"));
      break;
    case AceButton::kEventClicked:
      Serial.println(F("Clicked"));
      break;
    case AceButton::kEventDoubleClicked:
      Serial.println(F("DoubleClicked"));
      break;
  }
}

void setup() {
  Serial.begin(115200);
  while (!Serial); // Leonardo/Micro

  // Set the timeout to 10 millisecond so that AceButton::check()
  // can have about 4-5 iterations during the 50 millisecond debouncing time.
  capSensor.set_CS_Timeout_Millis(TIMEOUT_MILLIS);

  // Configure the button using CapacitiveConfig.
  buttonConfig.setFeature(ButtonConfig::kFeatureClick);
  buttonConfig.setFeature(ButtonConfig::kFeatureDoubleClick);
  buttonConfig.setEventHandler(handleEvent);
}

void loop() {
  unsigned long start = millis();
  button.check();

  // check on performance in milliseconds
  unsigned long duration = millis() - start;
  if (duration > TIMEOUT_MILLIS) {
    Serial.print(F("duration: "));
    Serial.println(duration);
  }
}
