2019-05-10 05:17:29 +00:00
|
|
|
#pragma once
|
2021-03-29 08:10:55 +00:00
|
|
|
#include <Arduino.h>
|
2019-05-10 05:17:29 +00:00
|
|
|
#include "./Geometry.h"
|
|
|
|
#include "./Figment.h"
|
2021-03-28 21:50:31 +00:00
|
|
|
#include "./Ringbuf.h"
|
2021-03-29 08:10:55 +00:00
|
|
|
#include <FastLED.h>
|
2019-05-10 05:17:29 +00:00
|
|
|
|
|
|
|
typedef Vector3d<int> MotionVec;
|
|
|
|
|
|
|
|
struct Variant {
|
|
|
|
enum Type {
|
|
|
|
Null,
|
|
|
|
Integer,
|
|
|
|
String,
|
|
|
|
Color,
|
2023-02-18 17:44:17 +00:00
|
|
|
Pointer,
|
2019-05-10 05:17:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Variant(int v)
|
|
|
|
: type(Integer), m_value{.asInt=v} {}
|
|
|
|
|
|
|
|
Variant(const char* v)
|
|
|
|
: type(String), m_value{.asString=v} {}
|
|
|
|
|
2021-03-29 08:10:55 +00:00
|
|
|
Variant(const CRGB &v)
|
2019-05-10 05:17:29 +00:00
|
|
|
: type(Color), m_value{.asRGB={v.r, v.g, v.b}} {}
|
|
|
|
|
2023-02-18 17:44:17 +00:00
|
|
|
Variant(void* p)
|
|
|
|
: type(Pointer), m_value{.asPointer=p} {}
|
|
|
|
|
2019-05-10 05:17:29 +00:00
|
|
|
Variant()
|
|
|
|
: type(Null) {}
|
|
|
|
|
|
|
|
Type type;
|
|
|
|
|
|
|
|
const char* asString() const;
|
2021-03-29 08:10:55 +00:00
|
|
|
CRGB asRGB() const;
|
2019-05-10 05:17:29 +00:00
|
|
|
int asInt() const;
|
2022-06-11 09:02:27 +00:00
|
|
|
bool asBool() const;
|
2023-02-18 17:44:17 +00:00
|
|
|
template<typename T> T* as() const {
|
|
|
|
return (T*)m_value.asPointer;
|
|
|
|
}
|
2019-05-10 05:17:29 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
union {
|
|
|
|
int asInt;
|
|
|
|
const char* asString;
|
|
|
|
uint8_t asRGB[3];
|
2023-02-18 17:44:17 +00:00
|
|
|
void* asPointer;
|
2019-05-10 05:17:29 +00:00
|
|
|
} m_value;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct InputEvent: public Variant {
|
|
|
|
enum Intent {
|
2021-03-28 01:19:55 +00:00
|
|
|
// An empty non-event
|
2019-05-10 05:17:29 +00:00
|
|
|
None,
|
2021-03-28 01:19:55 +00:00
|
|
|
|
|
|
|
// An input from the user, for other tasks to translate into canonical
|
|
|
|
// types. Makes for easy button re-mapping on the fly.
|
|
|
|
UserInput,
|
|
|
|
|
|
|
|
//
|
|
|
|
// The canonical types
|
|
|
|
//
|
|
|
|
// Hardware inputs
|
|
|
|
ButtonPress,
|
|
|
|
Acceleration,
|
|
|
|
NetworkStatus,
|
|
|
|
NetworkActivity,
|
|
|
|
|
|
|
|
// Power management
|
2019-05-10 05:17:29 +00:00
|
|
|
PowerToggle,
|
|
|
|
SetPower,
|
|
|
|
SetBrightness,
|
2021-03-28 01:19:55 +00:00
|
|
|
|
|
|
|
// Animation sequencing
|
2019-05-10 05:17:29 +00:00
|
|
|
PreviousPattern,
|
|
|
|
NextPattern,
|
|
|
|
SetPattern,
|
2021-03-28 01:19:55 +00:00
|
|
|
PreviousScene,
|
|
|
|
NextScene,
|
|
|
|
SetScene,
|
|
|
|
|
|
|
|
// Timekeeping
|
|
|
|
ScheduleChange,
|
2022-06-11 09:02:27 +00:00
|
|
|
Beat,
|
|
|
|
BeatDetect,
|
2021-03-28 01:19:55 +00:00
|
|
|
|
|
|
|
// Task management
|
2019-05-10 05:17:29 +00:00
|
|
|
StartThing,
|
|
|
|
StopThing,
|
2021-03-28 01:19:55 +00:00
|
|
|
|
|
|
|
// Configuration
|
|
|
|
SetDisplayOffset,
|
|
|
|
SetDisplayLength,
|
2023-02-18 15:20:36 +00:00
|
|
|
LoadConfigurationByName,
|
2021-03-28 01:19:55 +00:00
|
|
|
SetColor,
|
|
|
|
SaveConfigurationRequest,
|
2023-02-18 17:44:17 +00:00
|
|
|
ConfigurationChanged,
|
2021-03-28 01:19:55 +00:00
|
|
|
|
|
|
|
// Firmware events
|
|
|
|
FirmwareUpdate,
|
2022-06-11 09:02:27 +00:00
|
|
|
|
|
|
|
ReadyToRoll,
|
2019-05-10 05:17:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
template<typename Value>
|
|
|
|
InputEvent(Intent s, Value v)
|
|
|
|
: Variant(v), intent(s) {}
|
|
|
|
|
|
|
|
InputEvent(Intent s)
|
|
|
|
: Variant(), intent(s) {}
|
|
|
|
|
|
|
|
InputEvent()
|
|
|
|
: Variant(), intent(None) {}
|
|
|
|
|
|
|
|
Intent intent;
|
|
|
|
};
|
|
|
|
|
2022-06-11 09:02:27 +00:00
|
|
|
struct MainLoop;
|
|
|
|
|
2019-05-10 05:17:29 +00:00
|
|
|
class InputSource: public Task {
|
|
|
|
public:
|
2022-06-11 09:02:27 +00:00
|
|
|
InputSource() : Task() {init();}
|
|
|
|
explicit InputSource(const char* name) : Task(name) {init();}
|
2019-05-10 05:17:29 +00:00
|
|
|
void loop() override;
|
2022-06-11 09:02:27 +00:00
|
|
|
void onStart() override;
|
2019-05-10 05:17:29 +00:00
|
|
|
virtual InputEvent read() = 0;
|
2022-06-11 09:02:27 +00:00
|
|
|
private:
|
|
|
|
void init();
|
|
|
|
#ifdef CONFIG_THREADED_INPUTS
|
|
|
|
static void readThread(void* data);
|
|
|
|
MainLoop* m_threadLoop;
|
|
|
|
static uint8_t m_threadBuf[sizeof(InputEvent) * 32];
|
|
|
|
QueueHandle_t m_queue;
|
|
|
|
StaticQueue_t m_threadQueue;
|
|
|
|
#endif
|
2019-05-10 05:17:29 +00:00
|
|
|
};
|
|
|
|
|
2020-01-28 21:48:06 +00:00
|
|
|
class InputFunc : public InputSource {
|
|
|
|
public:
|
|
|
|
InputFunc(std::function<InputEvent(void)> f) : InputSource(), m_func(f) {}
|
|
|
|
InputFunc(std::function<InputEvent(void)> f, const char* name) : InputSource(name), m_func(f) {}
|
|
|
|
|
|
|
|
InputEvent read() override {
|
|
|
|
return m_func();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::function<InputEvent(void)> m_func;
|
|
|
|
};
|
|
|
|
|
2019-05-10 05:17:29 +00:00
|
|
|
class BufferedInputSource: public InputSource {
|
|
|
|
public:
|
|
|
|
BufferedInputSource(const char* name) : InputSource(name) {}
|
|
|
|
InputEvent read() override;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
void setEvent(InputEvent &&evt);
|
2021-03-28 01:19:55 +00:00
|
|
|
void setEvent(InputEvent::Intent intent, Variant &&v);
|
2019-05-10 05:17:29 +00:00
|
|
|
|
|
|
|
private:
|
2021-04-10 18:10:25 +00:00
|
|
|
Ringbuf<InputEvent, 12> m_eventQueue;
|
2019-05-10 05:17:29 +00:00
|
|
|
};
|
2021-03-28 01:19:55 +00:00
|
|
|
|
|
|
|
class InputMapper: public BufferedInputSource {
|
|
|
|
public:
|
2021-03-29 08:10:55 +00:00
|
|
|
InputMapper(std::function<InputEvent(const InputEvent)> f, const char* name) : BufferedInputSource(name), m_func(f) {}
|
2021-03-28 01:19:55 +00:00
|
|
|
|
|
|
|
void handleEvent(const InputEvent& evt) override {
|
|
|
|
setEvent(m_func(evt));
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::function<InputEvent(const InputEvent)> m_func;
|
|
|
|
};
|
2021-03-29 08:10:55 +00:00
|
|
|
|
2023-02-18 17:44:17 +00:00
|
|
|
class ConfigTaskMixin : public virtual Loopable {
|
|
|
|
public:
|
|
|
|
void handleEvent(const InputEvent &evt) override {
|
|
|
|
if (evt.intent == InputEvent::ConfigurationChanged) {
|
|
|
|
handleConfigChange(evt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void handleConfigChange(const InputEvent& evt) {}
|
|
|
|
};
|
|
|
|
|
2021-03-29 08:10:55 +00:00
|
|
|
class OnlineTaskMixin : public virtual Loopable {
|
|
|
|
public:
|
|
|
|
void handleEvent(const InputEvent &evt) override {
|
|
|
|
if (evt.intent == InputEvent::NetworkStatus) {
|
|
|
|
m_online = evt.asInt();
|
2021-04-10 18:10:25 +00:00
|
|
|
if (m_online) {
|
|
|
|
onOnline();
|
|
|
|
} else {
|
|
|
|
onOffline();
|
|
|
|
}
|
2021-03-29 08:10:55 +00:00
|
|
|
}
|
|
|
|
if (m_online) {
|
|
|
|
handleEventOnline(evt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-10 18:10:25 +00:00
|
|
|
virtual void onOnline() {}
|
|
|
|
virtual void onOffline() {}
|
|
|
|
|
2021-03-31 18:50:00 +00:00
|
|
|
virtual void handleEventOnline(const InputEvent &evt) {}
|
2021-03-29 08:10:55 +00:00
|
|
|
|
|
|
|
void loop() override {
|
|
|
|
if (m_online) {
|
|
|
|
loopOnline();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-31 18:50:00 +00:00
|
|
|
virtual void loopOnline() {}
|
2021-03-29 08:10:55 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
bool m_online = false;
|
|
|
|
};
|