initial commit
This commit is contained in:
120
libraries/FastLED/src/sensors/button.cpp
Normal file
120
libraries/FastLED/src/sensors/button.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
|
||||
|
||||
#include "fl/stdint.h"
|
||||
|
||||
#include "fl/memory.h"
|
||||
#include "fl/ui.h"
|
||||
|
||||
#include "fl/assert.h"
|
||||
#include "fl/namespace.h"
|
||||
#include "sensors/button.h"
|
||||
#include "sensors/digital_pin.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
ButtonLowLevel::ButtonLowLevel(int pin, ButtonStrategy strategy)
|
||||
: mPin(pin) {
|
||||
setStrategy(strategy);
|
||||
}
|
||||
|
||||
ButtonLowLevel::~ButtonLowLevel() {}
|
||||
|
||||
bool ButtonLowLevel::highLowFloating() {
|
||||
// FastLED doesn't have reliable support for pullups/pulldowns.
|
||||
// So we instead use a strategy where the pin is set to high, then
|
||||
// checked if it's high, then set to low, and then checked if it's low
|
||||
// if this is the case, then the pin is floating and thefore the button is
|
||||
// not being pressed.
|
||||
mPin.setPinMode(DigitalPin::kOutput);
|
||||
mPin.write(true); // set pin to high
|
||||
mPin.setPinMode(DigitalPin::kInput);
|
||||
const bool was_high = mPin.high(); // check if pin is high
|
||||
mPin.setPinMode(DigitalPin::kOutput);
|
||||
mPin.write(false); // set pin to low
|
||||
mPin.setPinMode(DigitalPin::kInput);
|
||||
const bool was_low = !mPin.high(); // check if pin is low
|
||||
const bool floating =
|
||||
was_high && was_low; // if both are true, then the pin is floating
|
||||
const bool pressed =
|
||||
!floating; // if the pin is floating, then the button is not pressed
|
||||
return pressed;
|
||||
}
|
||||
|
||||
bool ButtonLowLevel::isPressed() {
|
||||
// FastLED doesn't have reliable support for pullups/pulldowns.
|
||||
// So we instead use a strategy where the pin is set to high, then
|
||||
// checked if it's high, then set to low, and then checked if it's low
|
||||
// if this is the case, then the pin is floating and thefore the button is
|
||||
// not being pressed. return (mStrategy == kHighLowFloating) ?
|
||||
// highLowFloating() :
|
||||
// (mStrategy == kPullUp) ? mPin.high() : // not implemented yet
|
||||
// (mStrategy == kPullDown) ? !mPin.high() : // not implemented yet
|
||||
// false; // unknown strategy, return false
|
||||
switch (mStrategy) {
|
||||
case kHighLowFloating:
|
||||
return highLowFloating();
|
||||
case kPullUp:
|
||||
return mPin.high(); // not implemented yet
|
||||
default:
|
||||
FASTLED_ASSERT(false, "Unknown ButtonLowLevel strategy");
|
||||
return false; // unknown strategy, return false
|
||||
}
|
||||
}
|
||||
|
||||
Button::Button(int pin, ButtonStrategy strategy)
|
||||
: mButton(pin, strategy), mListener(this) {}
|
||||
|
||||
void Button::Listener::onEndFrame() {
|
||||
const bool pressed_curr_frame = mOwner->mButton.isPressed();
|
||||
const bool pressed_last_frame = mOwner->mPressedLastFrame;
|
||||
const bool changed_this_frame = pressed_curr_frame != pressed_last_frame;
|
||||
mOwner->mPressedLastFrame = pressed_curr_frame;
|
||||
if (changed_this_frame && pressed_curr_frame) {
|
||||
mOwner->mClickedThisFrame = true;
|
||||
mOwner->mOnClickCallbacks.invoke();
|
||||
}
|
||||
}
|
||||
|
||||
Button::Listener::Listener(Button *owner) : mOwner(owner) {
|
||||
addToEngineEventsOnce();
|
||||
}
|
||||
|
||||
Button::Listener::~Listener() {
|
||||
if (added) {
|
||||
EngineEvents::removeListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Button::Listener::addToEngineEventsOnce() {
|
||||
if (added) {
|
||||
return;
|
||||
}
|
||||
EngineEvents::addListener(this, 1); // One high priority so that it runs before UI elements.
|
||||
added = true;
|
||||
}
|
||||
|
||||
int Button::onClick(function<void()> callback) {
|
||||
int id = mOnClickCallbacks.add(callback);
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ButtonLowLevel::setStrategy(ButtonStrategy strategy) {
|
||||
mStrategy = strategy;
|
||||
switch (mStrategy) {
|
||||
case kHighLowFloating:
|
||||
mPin.setPinMode(DigitalPin::kInput); // Set pin to input mode
|
||||
break;
|
||||
case kPullUp:
|
||||
mPin.setPinMode(
|
||||
DigitalPin::kInputPullup); // Set pin to input pullup mode
|
||||
break;
|
||||
default:
|
||||
// Unknown strategy, do nothing
|
||||
FASTLED_ASSERT(false, "Unknown ButtonLowLevel strategy");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fl
|
||||
102
libraries/FastLED/src/sensors/button.h
Normal file
102
libraries/FastLED/src/sensors/button.h
Normal file
@@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
|
||||
#include "fl/stdint.h"
|
||||
|
||||
#include "fl/function_list.h"
|
||||
#include "fl/namespace.h"
|
||||
#include "fl/memory.h"
|
||||
#include "fl/ui.h"
|
||||
#include "sensors/digital_pin.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
enum ButtonStrategy {
|
||||
|
||||
// FastLED doesn't have reliable support for pullups/pulldowns.
|
||||
// So we instead use a strategy where the pin is set to high, then
|
||||
// checked if it's high, then set to low, and then checked if it's low
|
||||
// if this is the case, then the pin is floating and thefore the button
|
||||
// is not
|
||||
// being pressed.
|
||||
kHighLowFloating,
|
||||
kPullUp,
|
||||
|
||||
};
|
||||
|
||||
// A simple digital pin. If we are compiling in an Arduino environment, then
|
||||
// this class will bind to that. Otherwise it will fall back to the platform
|
||||
// api. Note that this class does not support analog mode nor pullups/pulldowns.
|
||||
class ButtonLowLevel {
|
||||
public:
|
||||
ButtonLowLevel(int pin, ButtonStrategy strategy = kHighLowFloating);
|
||||
~ButtonLowLevel();
|
||||
ButtonLowLevel(const ButtonLowLevel &other) = default;
|
||||
ButtonLowLevel &operator=(const ButtonLowLevel &other) = delete;
|
||||
ButtonLowLevel(ButtonLowLevel &&other) = delete;
|
||||
bool isPressed();
|
||||
|
||||
bool highLowFloating();
|
||||
|
||||
void setStrategy(ButtonStrategy strategy);
|
||||
|
||||
private:
|
||||
fl::DigitalPin mPin;
|
||||
ButtonStrategy mStrategy = kHighLowFloating;
|
||||
};
|
||||
|
||||
// The default button type hooks into the FastLED EngineEvents to monitor
|
||||
// whether the button is pressed or not. You do not need to run an update
|
||||
// function. If you need more control, use ButtonLowLevel directly.
|
||||
class Button {
|
||||
public:
|
||||
Button(int pin,
|
||||
ButtonStrategy strategy = ButtonStrategy::kHighLowFloating);
|
||||
|
||||
int onClick(fl::function<void()> callback);
|
||||
void removeOnClick(int id) {
|
||||
mOnClickCallbacks.remove(id);
|
||||
}
|
||||
|
||||
void setStrategy(ButtonStrategy strategy) {
|
||||
mButton.setStrategy(strategy);
|
||||
}
|
||||
|
||||
bool isPressed() {
|
||||
return mButton.isPressed();
|
||||
}
|
||||
|
||||
bool clicked() const {
|
||||
// If we have a real button, check if it's pressed
|
||||
return mClickedThisFrame;
|
||||
}
|
||||
|
||||
protected:
|
||||
struct Listener : public EngineEvents::Listener {
|
||||
Listener(Button *owner);
|
||||
~Listener();
|
||||
void addToEngineEventsOnce();
|
||||
|
||||
// We do an experiment here, what about listening to the end frame event
|
||||
// instea do of the begin frame event? This will put the activation of
|
||||
// this button **before** the next frame. I think this pattern should be
|
||||
// used for all UI elements, so that the button state is updated before
|
||||
// the next frame is drawn. This seems like the only way to do this, or
|
||||
// by using platform pre loop, but not all platforms support that.
|
||||
void onEndFrame() override;
|
||||
|
||||
private:
|
||||
Button *mOwner;
|
||||
bool added = false;
|
||||
};
|
||||
|
||||
private:
|
||||
ButtonLowLevel mButton;
|
||||
Listener mListener;
|
||||
bool mPressedLastFrame = false; // Don't read this variale, it's used internally.
|
||||
bool mClickedThisFrame = false; // This is true if clicked this frame.
|
||||
|
||||
fl::FunctionList<void> mOnClickCallbacks;
|
||||
// fl::FunctionList<void(Button&)> mOnChangedCallbacks;
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
106
libraries/FastLED/src/sensors/digital_pin.cpp
Normal file
106
libraries/FastLED/src/sensors/digital_pin.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
|
||||
#include "fl/stdint.h"
|
||||
|
||||
#include "fl/ui.h"
|
||||
#include "fl/memory.h"
|
||||
|
||||
#include "fl/namespace.h"
|
||||
#include "digital_pin.h"
|
||||
|
||||
|
||||
|
||||
#if !defined(USE_ARDUINO) && __has_include(<Arduino.h>)
|
||||
#define USE_ARDUINO 1
|
||||
#else
|
||||
#define USE_ARDUINO 0
|
||||
#endif
|
||||
|
||||
|
||||
#if USE_ARDUINO
|
||||
#include <Arduino.h> // ok include
|
||||
#else
|
||||
// Fallback
|
||||
#define FASTLED_INTERNAL
|
||||
#include "FastLED.h"
|
||||
#include "fastpin.h"
|
||||
#endif
|
||||
|
||||
|
||||
namespace fl {
|
||||
|
||||
|
||||
#if USE_ARDUINO
|
||||
class DigitalPinImpl : public Referent {
|
||||
public:
|
||||
DigitalPinImpl(int DigitalPin) : mDigitalPin(DigitalPin) {}
|
||||
~DigitalPinImpl() = default;
|
||||
|
||||
void setPinMode(DigitalPin::Mode mode) {
|
||||
switch (mode) {
|
||||
case DigitalPin::kInput:
|
||||
::pinMode(mDigitalPin, INPUT);
|
||||
break;
|
||||
case DigitalPin::kOutput:
|
||||
::pinMode(mDigitalPin, OUTPUT);
|
||||
break;
|
||||
case DigitalPin::kInputPullup:
|
||||
::pinMode(mDigitalPin, INPUT_PULLUP);
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool high() { return HIGH == ::digitalRead(mDigitalPin); }
|
||||
void write(bool value) { ::digitalWrite(mDigitalPin, value ? HIGH : LOW); }
|
||||
|
||||
private:
|
||||
int mDigitalPin;
|
||||
};
|
||||
|
||||
#else
|
||||
class DigitalPinImpl : public Referent {
|
||||
public:
|
||||
DigitalPinImpl(int pin) : mPin(pin) {}
|
||||
~DigitalPinImpl() = default;
|
||||
|
||||
void setPinMode(DigitalPin::Mode mode) {
|
||||
switch (mode) {
|
||||
case DigitalPin::kInput:
|
||||
mPin.setInput();
|
||||
break;
|
||||
case DigitalPin::kOutput:
|
||||
mPin.setOutput();
|
||||
break;
|
||||
case DigitalPin::kInputPullup:
|
||||
mPin.setInputPullup();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool high() { return mPin.hival(); }
|
||||
void write(bool value) { value ? mPin.hi(): mPin.lo(); }
|
||||
// define pin
|
||||
Pin mPin;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
DigitalPin::DigitalPin(int DigitalPin) {
|
||||
mImpl = fl::make_shared<DigitalPinImpl>(DigitalPin);
|
||||
}
|
||||
DigitalPin::~DigitalPin() = default;
|
||||
DigitalPin::DigitalPin(const DigitalPin &other) = default;
|
||||
|
||||
DigitalPin& DigitalPin::operator=(const DigitalPin &other) = default;
|
||||
|
||||
void DigitalPin::setPinMode(Mode mode) {
|
||||
mImpl->setPinMode(mode);
|
||||
}
|
||||
|
||||
bool DigitalPin::high() const {
|
||||
return mImpl->high();
|
||||
}
|
||||
|
||||
void DigitalPin::write(bool is_high) {
|
||||
mImpl->write(is_high);
|
||||
}
|
||||
|
||||
} // namespace fl
|
||||
39
libraries/FastLED/src/sensors/digital_pin.h
Normal file
39
libraries/FastLED/src/sensors/digital_pin.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "fl/stdint.h"
|
||||
|
||||
#include "fl/memory.h"
|
||||
|
||||
|
||||
namespace fl {
|
||||
|
||||
FASTLED_SMART_PTR(DigitalPinImpl);
|
||||
|
||||
|
||||
// A simple digital pin. If we are compiling in an Arduino environment, then
|
||||
// this class will bind to that. Otherwise it will fall back to the platform api.
|
||||
// Note that this class does not support analog mode nor pullups/pulldowns.
|
||||
class DigitalPin {
|
||||
public:
|
||||
enum Mode {
|
||||
kInput = 0,
|
||||
kOutput,
|
||||
kInputPullup,
|
||||
// kInputPulldown, Not implemented in Arduino.h
|
||||
};
|
||||
|
||||
DigitalPin(int pin);
|
||||
~DigitalPin();
|
||||
DigitalPin(const DigitalPin &other);
|
||||
DigitalPin &operator=(const DigitalPin &other);
|
||||
|
||||
DigitalPin(DigitalPin &&other) = delete;
|
||||
|
||||
void setPinMode(Mode mode);
|
||||
bool high() const; // true if high, false if low
|
||||
void write(bool is_high);
|
||||
private:
|
||||
DigitalPinImplPtr mImpl;
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
63
libraries/FastLED/src/sensors/pir.cpp
Normal file
63
libraries/FastLED/src/sensors/pir.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#ifndef FASTLED_INTERNAL
|
||||
#define FASTLED_INTERNAL
|
||||
#endif
|
||||
|
||||
#include "FastLED.h"
|
||||
|
||||
#include "fastpin.h"
|
||||
#include "fl/strstream.h"
|
||||
#include "fl/warn.h"
|
||||
#include "fl/assert.h"
|
||||
#include "sensors/pir.h"
|
||||
|
||||
namespace fl {
|
||||
|
||||
namespace {
|
||||
int g_counter = 0;
|
||||
Str getButtonName(const char *button_name) {
|
||||
if (button_name) {
|
||||
return Str(button_name);
|
||||
}
|
||||
int count = g_counter++;
|
||||
if (count == 0) {
|
||||
return Str("PIR");
|
||||
}
|
||||
StrStream s;
|
||||
s << "PirLowLevel " << g_counter++;
|
||||
return s.str();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
PirLowLevel::PirLowLevel(int pin): mPin(pin) {
|
||||
mPin.setPinMode(DigitalPin::kInput);
|
||||
}
|
||||
|
||||
bool PirLowLevel::detect() {
|
||||
return mPin.high();
|
||||
}
|
||||
|
||||
|
||||
Pir::Pir(int pin, uint32_t latchMs, uint32_t risingTime,
|
||||
uint32_t fallingTime, const char* button_name)
|
||||
: mPir(pin), mRamp(risingTime, latchMs, fallingTime), mButton(getButtonName(button_name).c_str()) {
|
||||
mButton.onChanged([this](UIButton&) {
|
||||
this->mRamp.trigger(millis());
|
||||
});
|
||||
}
|
||||
|
||||
bool Pir::detect(uint32_t now) {
|
||||
bool currentState = mPir.detect();
|
||||
if (currentState && !mLastState) {
|
||||
mRamp.trigger(now);
|
||||
}
|
||||
mLastState = currentState;
|
||||
return mRamp.isActive(now);
|
||||
}
|
||||
|
||||
uint8_t Pir::transition(uint32_t now) {
|
||||
// ensure detect() logic runs so we trigger on edges
|
||||
detect(now);
|
||||
return mRamp.update8(now);
|
||||
}
|
||||
|
||||
} // namespace fl
|
||||
75
libraries/FastLED/src/sensors/pir.h
Normal file
75
libraries/FastLED/src/sensors/pir.h
Normal file
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
|
||||
#include "fl/stdint.h"
|
||||
|
||||
#include "digital_pin.h"
|
||||
#include "fl/memory.h"
|
||||
#include "fl/ui.h"
|
||||
#include "fl/time_alpha.h"
|
||||
|
||||
#include "fl/namespace.h"
|
||||
|
||||
|
||||
namespace fl {
|
||||
|
||||
// A passive infrared sensor common on amazon.
|
||||
// For best results set the PIR to maximum sensitive and minimum delay time before retrigger.
|
||||
// Instantiating this class will create a ui UIButton when
|
||||
// compiling using the FastLED web compiler.
|
||||
class PirLowLevel {
|
||||
public:
|
||||
PirLowLevel(int pin);
|
||||
bool detect();
|
||||
operator bool() { return detect(); }
|
||||
|
||||
private:
|
||||
|
||||
DigitalPin mPin;
|
||||
};
|
||||
|
||||
// An passive infrared sensor that incorporates time to allow for latching and transitions fx. This is useful
|
||||
// for detecting motion and the increasing the brightness in response to motion.
|
||||
// Example:
|
||||
// #define PIR_LATCH_MS 15000 // how long to keep the PIR sensor active after a trigger
|
||||
// #define PIR_RISING_TIME 1000 // how long to fade in the PIR sensor
|
||||
// #define PIR_FALLING_TIME 1000 // how long to fade out the PIR sensor
|
||||
// Pir pir(PIN_PIR, PIR_LATCH_MS, PIR_RISING_TIME, PIR_FALLING_TIME);
|
||||
// void loop() {
|
||||
// uint8_t bri = pir.transition(millis());
|
||||
// FastLED.setBrightness(bri * brightness.as<float>());
|
||||
// }
|
||||
|
||||
class Pir {
|
||||
public:
|
||||
/// @param pin GPIO pin for PIR sensor
|
||||
/// @param latchMs total active time (ms)
|
||||
/// @param risingTime ramp‑up duration (ms)
|
||||
/// @param fallingTime ramp‑down duration (ms)
|
||||
Pir(int pin,
|
||||
uint32_t latchMs = 5000,
|
||||
uint32_t risingTime = 1000,
|
||||
uint32_t fallingTime = 1000,
|
||||
const char* button_name = nullptr);
|
||||
|
||||
/// Returns true if the PIR is “latched on” (within latchMs of last trigger).
|
||||
bool detect(uint32_t now);
|
||||
|
||||
/// Returns a 0–255 ramp value:
|
||||
/// • ramps 0→255 over risingTime
|
||||
/// • holds 255 until latchMs–fallingTime
|
||||
/// • ramps 255→0 over fallingTime
|
||||
/// Outside latch period returns 0.
|
||||
uint8_t transition(uint32_t now);
|
||||
|
||||
/// Manually start the latch cycle (e.g. on startup)
|
||||
void activate(uint32_t now) { mRamp.trigger(now); }
|
||||
|
||||
private:
|
||||
PirLowLevel mPir;
|
||||
TimeRamp mRamp;
|
||||
bool mLastState = false;
|
||||
UIButton mButton;
|
||||
|
||||
};
|
||||
|
||||
} // namespace fl
|
||||
Reference in New Issue
Block a user