#include <avr/sleep.h>
#define LED 1 // LED on pin 1, PB1, DIP pin 6
#define BUTTON 2 // BUTTON on pin 2, PB2, DIP pin 7
#define DEBOUNCE 30
#define TOUCH_PIN 0 // touch on PB0, DIP pin 5
#define TOUCH_DELTA 50
#define PATTERNS 2
#define HOLD_THRESHOLD 500 // ms to trigger hold event
#define BRIGHTNESS_STEP 32 // brightness decrease per hold step
#define BRIGHTNESS_STEP_DELAY 200 // ms between brightness steps
// each pattern consists of 50 intensities (0-255)
const uint8_t patterns[2][50] = {
{ 0, 0, 0, 32, 64, 128, 128, 64, 32, 0, 0, 0, 0, 32, 64, 128, 128, 64, 32, 0,0, 0, 0, 32, 64, 128, 128, 64, 32, 0,0, 0, 0, 32, 64, 128, 128, 64, 32, 0, 0, 0, 0, 32, 64, 128, 128, 64, 32, 0,},
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }
};
uint8_t currentPattern = 0;
uint8_t maxBrightness = 255;
/* ---------- Capacitive touch sensing ---------- */
uint16_t touchBaseline = 0;
uint16_t readTouchRaw() {
uint16_t count = 0;
// Charge the pad
pinMode(TOUCH_PIN, OUTPUT);
digitalWrite(TOUCH_PIN, HIGH);
delayMicroseconds(5);
// Let it float and time the discharge
pinMode(TOUCH_PIN, INPUT); // high-impedance
while (digitalRead(TOUCH_PIN) == HIGH && count < 2000) {
count++;
}
// Ensure it’s discharged before next reading
pinMode(TOUCH_PIN, OUTPUT);
digitalWrite(TOUCH_PIN, LOW);
return count;
}
void calibrateTouch() {
uint32_t sum = 0;
for (uint8_t i = 0; i < 32; i++) {
sum += readTouchRaw();
}
touchBaseline = sum / 32;
}
// Return true when the rim is being touched.
bool isTouched() {
uint16_t reading = readTouchRaw();
return reading < (touchBaseline - TOUCH_DELTA);
}
// Emulate an active-low mechanical button:
// LOW = pressed (touched)
// HIGH = released (not touched)
uint8_t readTouch() {
return isTouched() ? LOW : HIGH;
}
/* ---------- Main setup / loop ---------- */
void setup(void)
{
pinMode(BUTTON, INPUT_PULLUP); // mechanical button
pinMode(LED, OUTPUT); // LED output
pinMode(TOUCH_PIN, OUTPUT);
analogWrite(LED, 0); // LED off
digitalWrite(TOUCH_PIN, LOW);
calibrateTouch();
}
void loop(void) {
static uint8_t step = 0;
// Active mode: play pattern with scaled brightness
uint8_t intensity = patterns[currentPattern][step];
uint8_t scaled = ((uint16_t)intensity * maxBrightness) / 255;
analogWrite(LED, scaled);
delay(200); // 200 ms per step
step++;
if (step > 49) {
step = 0;
}
handleButtonPress();
}
/*
* Generic input handler with debouncing logic.
* Handles both button presses and touch events.
* Supports tap to change pattern and hold to adjust brightness.
*/
void handleInput(uint8_t &lastValue, bool &pending, unsigned long &lastEvent, uint8_t (*readFunc)()) {
static unsigned long pressStart = 0;
static unsigned long lastBrightnessStep = 0;
static bool holdHandled = false;
const uint8_t value = readFunc();
const unsigned long now = millis();
// Detect press/release transitions
if (value != lastValue) {
if (value == LOW) {
// Button just pressed
pressStart = now;
holdHandled = false;
} else {
// Button just released
if (!holdHandled && (now - pressStart) > DEBOUNCE && (now - pressStart) < HOLD_THRESHOLD) {
// Short press (tap): change pattern and reset brightness
currentPattern++;
maxBrightness = 255;
analogWrite(LED, 255); // debug flash
if (currentPattern == PATTERNS) {
calibrateTouch();
currentPattern = 0;
}
}
}
lastValue = value;
}
// Handle ongoing hold
if (value == LOW && (now - pressStart) > HOLD_THRESHOLD) {
if (!holdHandled || (now - lastBrightnessStep) > BRIGHTNESS_STEP_DELAY) {
holdHandled = true;
lastBrightnessStep = now;
// Decrease brightness in steps
if (maxBrightness >= BRIGHTNESS_STEP) {
maxBrightness -= BRIGHTNESS_STEP;
} else {
maxBrightness = 0;
}
}
}
}
/*
* Button press wrapper.
*/
uint8_t readButton() {
return digitalRead(BUTTON);
}
void handleButtonPress(void) {
static uint8_t lastValue = digitalRead(BUTTON);
static bool pending = false;
static unsigned long lastEvent = 0;
handleInput(lastValue, pending, lastEvent, readButton);
}
/*
* Touch sensor wrapper.
*/
void handleTouchEvent(void) {
static uint8_t lastValue = readTouch();
static bool pending = false;
static unsigned long lastEvent = 0;
handleInput(lastValue, pending, lastEvent, readTouch);
}