#include <esp32-hal-timer.h>
// Event class
class Event {
public:
int id;
Event(int id) : id(id) {}
bool operator==(const Event& other) const { return id == other.id; }
};
// Command class
class Command {
public:
int id;
Command(int id) : id(id) {}
bool operator==(const Command& other) const { return id == other.id; }
};
// EventHandler interface
class EventHandler {
public:
virtual void on(Event event) = 0;
virtual ~EventHandler() {}
};
// CommandHandler interface
class CommandHandler {
public:
virtual void handle(Command command) = 0;
virtual ~CommandHandler() {}
};
// Sensor abstract class
class Sensor : public EventHandler {
protected:
int pin;
EventHandler* handler;
public:
Sensor(int pin, EventHandler* handler) : pin(pin), handler(handler) {
if (pin >= 0 && pin < 40) { // ESP32 GPIO range
pinMode(pin, INPUT_PULLUP);
}
}
virtual void on(Event event) override { if (handler) handler->on(event); }
void setHandler(EventHandler* h) { handler = h; }
virtual ~Sensor() {}
};
// Actuator abstract class
class Actuator : public CommandHandler {
protected:
int pin;
CommandHandler* handler;
public:
Actuator(int pin, CommandHandler* handler) : pin(pin), handler(handler) {
if (pin >= 0 && pin < 40) {
pinMode(pin, OUTPUT);
}
}
virtual void handle(Command command) override { if (handler) handler->handle(command); }
void setHandler(CommandHandler* h) { handler = h; }
virtual ~Actuator() {}
};
// Device abstract class (ModestIoT Nano-framework base class)
class Device : public EventHandler, public CommandHandler {
public:
virtual void on(Event event) override = 0;
virtual void handle(Command command) override = 0;
virtual ~Device() {}
void triggerSensorEvent(Event event) {
on(event);
}
};
// Button class
class Button : public Sensor {
public:
static constexpr int BUTTON_PRESSED_EVENT_ID = 1;
static inline const Event BUTTON_PRESSED_EVENT = Event(BUTTON_PRESSED_EVENT_ID);
Button(int pin, EventHandler* handler) : Sensor(pin, handler) {}
};
// MotionSensor class
class MotionSensor : public Sensor {
public:
static constexpr int MOTION_DETECTED_EVENT_ID = 2;
static inline const Event MOTION_DETECTED_EVENT = Event(MOTION_DETECTED_EVENT_ID);
MotionSensor(int pin, EventHandler* handler) : Sensor(pin, handler) {}
};
// Led class
class Led : public Actuator {
private:
bool state;
public:
static constexpr int TOGGLE_LED_COMMAND_ID = 1;
static constexpr int TURN_ON_COMMAND_ID = 2;
static constexpr int TURN_OFF_COMMAND_ID = 3;
static inline const Command TOGGLE_LED_COMMAND = Command(TOGGLE_LED_COMMAND_ID);
static inline const Command TURN_ON_COMMAND = Command(TURN_ON_COMMAND_ID);
static inline const Command TURN_OFF_COMMAND = Command(TURN_OFF_COMMAND_ID);
Led(int pin, bool initialState, CommandHandler* handler) : Actuator(pin, handler), state(initialState) {
if (pin >= 0 && pin < 40) {
digitalWrite(pin, state ? HIGH : LOW);
}
}
void handle(Command command) override {
if (command == TOGGLE_LED_COMMAND) {
state = !state;
} else if (command == TURN_ON_COMMAND) {
state = true;
} else if (command == TURN_OFF_COMMAND) {
state = false;
}
if (pin >= 0 && pin < 40) {
digitalWrite(pin, state ? HIGH : LOW);
}
Actuator::handle(command);
}
bool getState() const { return state; }
void setState(bool s) {
state = s;
if (pin >= 0 && pin < 40) {
digitalWrite(pin, state ? HIGH : LOW);
}
}
};
// ToggableLedDevice class (inherits from Device)
class ToggableLedDevice : public Device {
private:
Button button;
Led led;
public:
static constexpr int BUTTON_PIN = 2; // ESP32 GPIO
static constexpr int LED_PIN = 13; // ESP32 GPIO
ToggableLedDevice(int buttonPin, int ledPin, bool initialLedState)
: button(buttonPin, this), led(ledPin, initialLedState, this) {}
void on(Event event) override {
if (event == Button::BUTTON_PRESSED_EVENT) {
led.handle(Led::TOGGLE_LED_COMMAND);
}
}
void handle(Command command) override {
led.handle(command);
}
Led& getLed() { return led; }
};
// MotionActivatedLedDevice class (inherits from Device)
class MotionActivatedLedDevice : public Device {
private:
Button button;
MotionSensor motion;
Led led;
public:
static constexpr int BUTTON_PIN = 2; // ESP32 GPIO
static constexpr int MOTION_PIN = 4; // ESP32 GPIO
static constexpr int LED_PIN = 13; // ESP32 GPIO
MotionActivatedLedDevice(int buttonPin, int motionPin, int ledPin, bool initialLedState)
: button(buttonPin, this), motion(motionPin, this), led(ledPin, initialLedState, this) {}
void on(Event event) override {
if (event == Button::BUTTON_PRESSED_EVENT) {
led.handle(Led::TOGGLE_LED_COMMAND);
} else if (event == MotionSensor::MOTION_DETECTED_EVENT) {
led.handle(Led::TURN_ON_COMMAND);
}
}
void handle(Command command) override {
led.handle(command);
}
Led& getLed() { return led; }
};
// Static device instance (using MotionActivatedLedDevice)
static MotionActivatedLedDevice device(
MotionActivatedLedDevice::BUTTON_PIN,
MotionActivatedLedDevice::MOTION_PIN,
MotionActivatedLedDevice::LED_PIN,
false
);
// Timer and ISR variables
static hw_timer_t* debounceTimer = nullptr;
static volatile bool sensorTriggered = false; // Domain-driven name
static const uint32_t debounceDelayUs = 50000; // 50ms in microseconds
// Timer ISR for event checking
void IRAM_ATTR checkForEvents() {
if (digitalRead(MotionActivatedLedDevice::BUTTON_PIN) == LOW) {
device.triggerSensorEvent(Button::BUTTON_PRESSED_EVENT);
} else if (digitalRead(MotionActivatedLedDevice::MOTION_PIN) == HIGH) {
device.triggerSensorEvent(MotionSensor::MOTION_DETECTED_EVENT);
}
sensorTriggered = false;
timerStop(debounceTimer);
}
// Shared sensor ISR
void IRAM_ATTR checkIfSensorTriggered() {
if (!sensorTriggered) {
sensorTriggered = true;
timerRestart(debounceTimer);
timerAlarm(debounceTimer, 80, true, 0);
}
}
void setup() {
// Set up interrupts using shared ISR
attachInterrupt(digitalPinToInterrupt(MotionActivatedLedDevice::BUTTON_PIN), checkIfSensorTriggered, FALLING);
attachInterrupt(digitalPinToInterrupt(MotionActivatedLedDevice::MOTION_PIN), checkIfSensorTriggered, RISING);
// Set up timer (80MHz clock, prescaler 80 -> 1 tick = 1us)
//debounceTimer = timerBegin(0, 80, true);
debounceTimer = timerBegin(80);
timerAttachInterrupt(debounceTimer, &checkForEvents);
timerWrite(debounceTimer, debounceDelayUs); // One-shot timer
}
void loop() {
// Empty loop; reactive via interrupts
}