/*
----- Smart Bead Prototype ------
Button Behaviors:
- Long hold turns on and off power. Will turn off when counting also.
! Double Short Buzz
- Single press starts counting if the device is not already counting.
! Single Long Buzz
- Single press while running does nothing
! Single Short buzz
- Double press while running stops the countdown
! Single Long Buzz
- Finish Sequence: Single press will stop buzz, double press turns off power
- Dead Battery: Will turn off and halt MCU
! Triple Short Buzzes
Counter Behaviors:
- Counting Starts
! Single Long Buzz
- Counting is Cancelled
! Single Long Buzz
- When the counter finishes
! 5x Repeating 2 Secong ramping sequence
Power LED Behaviors:
- If device is off, when button is held, LED will fade in, then breathe for 5 seconds
- If device is on, when button is held, LED will fade out
- When device is IDLE the LED will breathe 3 times every 10 minutes
- When device is COUNTING, device will breathe once every 5 minutes
Buzz Types:
1) Short Buzz
2) Long Buzz
3) Double Short Buzz
4) Double Long Buzz
5) Repeating Finish Sequence
6) Triple Short Buzz
LED Fade Types:
1) Fade in
2) Fade out
3) Breathe
*/
#include <Arduino.h>
#include "Button2.h" //https://github.com/LennartHennigs/Button2
#include <Ramp.h> //https://github.com/siteswapjuggler/RAMP
#include "uptime_formatter.h" //https://github.com/YiannisBourkelis/Uptime-Library
/***********/
/* DEFINES */
/***********/
#define BUTTON_PIN 12 // (D7 )Hardware pin for main button
#define POWER_LED_PIN 14 // (D4) Pin for soft power LED
#define VIBE_PIN 15 // (D9) Pin for vibration motor
#define BATTERY_PIN 4 // (A0) Pin for monitoring battery voltage. Connect with voltage divider resistor network
#define MS_MINUTE 60000 // 1000 * 60 = 60,000
#define MS_FIVE_MINUTES 300000 // 1000 * 60 * 5 = 300,000
#define MS_TEN_MINUTES 600000 // 1000 * 60 * 10 = 600,000
#define MS_HOUR 3600000 // 60,000 * 60 = 3,600,000
#define TEST_TARGET 3000 // 1000 * 3
#define COUNTER_TARGET TEST_TARGET // Counter Target
#define ANALOG_RES 12
#define BATTERY_MAX 4.2
#define ANALOG_MAX 4095
#define LONG_BUZZ 500
#define SHORT_BUZZ 1000
/*************/
/* VARIABLES */
/*************/
Button2 button; // Creates event driven button library instance
bool counting_state = false; // Holds counting state
bool power_state = false; // Holds soft power state
bool buzz_state = false;
bool ramp_dirty = false;
bool idle_state = false;
bool debug = false; // When true more verbose debug data is shown
static unsigned long lastCountMillis = 0; // Holds the counter start millis
static unsigned long batteryCheckPeriod = 5000UL;
static bool checkWaiting = false;
static unsigned long idlePeriod = 10000UL;
// Default Ramp Parameters
int max_pwm = 4095;
int delta_ms = 500;
int grain_ms = 1;
loop_mode mode_loop = FORTHANDBACK;
ramp_mode mode_ramp = SINUSOIDAL_IN;
int ramp_repeat = 1;
rampInt myRamp;
int lastValue = NULL;
/********************/
/* HELPER FUNCTIONS */
/********************/
uint32_t checkBattery() {
// Battery level check
// https://wiki.seeedstudio.com/XIAO_ESP32C3_Getting_Started/
uint32_t Vbatt = 0;
Vbatt = Vbatt + analogReadMilliVolts(BATTERY_PIN); // ADC with correction
float Vbattf = Vbatt / 1000.0; // attenuation ratio 1/2, mV --> V
Serial.println("Vbatt: " + String(Vbatt));
Serial.println("Vbattf: " + String(Vbattf));
return Vbatt;
}
/*************************/
/* BUTTON EVENT HANDLERS */
/*************************/
// Fired on all button releases
void handleTap(Button2& b) {
if (debug)
Serial.println("(b.wasPressedFor(): " + String(b.wasPressedFor()));
}
//Stops and resets running counter
void handleDoubleClick(Button2& b) {
Serial.println("handleDoubleClick");
counting_state = false;
// Test code related to battery checking and vibrator motor operation - sets vibe speed to battery voltage ADC reading
//int val = checkBattery();
//Serial.println("checkBattery() Val: " + String(val));
if (buzz_state == false) {
buzz_state = true;
ramp_dirty = true;
ramp_repeat = 1;
Serial.println("buzz_state: " + String(buzz_state));
//analogWrite(VIBE_PIN, val);
} else {
//analogWrite(VIBE_PIN, 0);
buzz_state = false;
ramp_dirty = true;
Serial.println("buzz_state: " + String(buzz_state));
}
}
// Soft on/off handler. Long press toggles on\off state.
void handleLongClick(Button2& b) {
Serial.println("handleLongClick");
if (power_state == true) {
Serial.println("-- Powering off");
digitalWrite(POWER_LED_PIN, 0); // Turn off Power LED
power_state = false;
} else {
Serial.println("-- Powering on");
digitalWrite(POWER_LED_PIN, 1); // Turn on Power LED
power_state = true;
}
}
// If device is not already counting, a single button press starts the counting.
void handleSingleClick(Button2& b) {
Serial.println("handleSingleClick");
if (power_state == false) {
Serial.println("!! Cant start. Soft power is off.");
return;
}
if (counting_state == true) {
Serial.println("!! Cant start. Already counting.");
return;
}
if (counting_state == false) {
unsigned long start_ms = millis();
Serial.println("-- Starting millis: " + String(start_ms));
lastCountMillis = start_ms; // Set initial time
counting_state = true;
}
}
/********************/
/* SERVICE ROUTINES */
/********************/
// Counts to target time
void countingService() {
if (counting_state == false) {
idle_state = true;
return;
}
if (millis() - lastCountMillis < COUNTER_TARGET) {
idle_state = false;
return;
}
Serial.println("-- Done Counting millis: " + String(millis()));
lastCountMillis = millis();
counting_state = false;
idle_state = true;
}
// Checks current battery voltage via resistor voltage divider
void batteryCheckService() {
static unsigned long lastMillis = 0;
if (power_state == false) {
return;
}
if (millis() - lastMillis < batteryCheckPeriod) {
return;
}
lastMillis = millis();
Serial.println("-- batteryCheckService() | Uptime: " + uptime_formatter::getUptime());
int val = checkBattery();
// checkWaiting = !checkWaiting;
}
void idleService() {
static unsigned long lastMillis = 0;
if (power_state == false) {
return;
}
if (idle_state == false) {
return;
}
if (millis() - lastMillis < idlePeriod) {
return;
}
lastMillis = millis();
Serial.println("-- idleService() | Uptime: " + uptime_formatter::getUptime());
}
void buzzService() {
static int repeats = 0;
if (ramp_dirty == true) {
ramp_dirty = false;
lastValue = NULL;
Serial.println("-- Setting up ramp generator");
myRamp.go(max_pwm, delta_ms, mode_ramp, mode_loop); // go to 0 in 15 ms
myRamp.setGrain(grain_ms); // set grain to 1 ms
}
if (buzz_state == false) {
analogWrite(VIBE_PIN, 0);
lastValue = NULL;
return;
}
// LOOP
int val = myRamp.update(); // store updated ramp value
if (val != lastValue) {
Serial.println("Update: " + String(val));
analogWrite(VIBE_PIN, val);
if (val == 0) {
Serial.println("One cycle. Reps: " + String(repeats));
if (repeats == ramp_repeat) {
Serial.println("All cycles. Reps: " + String(repeats));
repeats = 0;
buzz_state = false;
ramp_dirty = true;
}
repeats = repeats + 1;
}
}
lastValue = val;
}
/******************/
/* SETUP FUNCTION */
/******************/
void setup() {
pinMode(POWER_LED_PIN, OUTPUT);
pinMode(VIBE_PIN, OUTPUT);
pinMode(BATTERY_PIN, INPUT);
digitalWrite(POWER_LED_PIN, power_state); // Will be off until long press
Serial.begin(115200);
Serial.println("-- Sancalpa Smart Intention Bead Prototype 1 --");
Serial.println("-- Power-Up millis: " + String(millis()));
button.begin(BUTTON_PIN);
button.setTapHandler(handleTap);
button.setDoubleClickHandler(handleDoubleClick);
button.setLongClickDetectedHandler(handleLongClick);
button.setClickHandler(handleSingleClick);
button.setLongClickTime(500);
// Configure ADC pin for reading battery voltage
analogReadResolution(12);
//analogSetAttenuation(ADC_11db); // Default is ADC_11db
analogWriteResolution(12);
}
/* MAIN LOOP FUNCTION */
void loop() {
button.loop();
countingService();
batteryCheckService();
buzzService();
idleService();
delay(10); // this speeds up the simulation
}