| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | /* | ||
| 3 | * This file is part of the Falcon Player (FPP) and is Copyright (C) | ||
| 4 | * 2013-2022 by the Falcon Player Developers. | ||
| 5 | * | ||
| 6 | * The Falcon Player (FPP) is free software, and is covered under | ||
| 7 | * multiple Open Source licenses. Please see the included 'LICENSES' | ||
| 8 | * file for descriptions of what files are covered by each license. | ||
| 9 | * | ||
| 10 | * This source file is covered under the LGPL v2.1 as described in the | ||
| 11 | * included LICENSE.LGPL file. | ||
| 12 | */ | ||
| 13 | #include <functional> | ||
| 14 | #include "fpp-json-fwd.h" | ||
| 15 | #include <list> | ||
| 16 | #include <map> | ||
| 17 | #include <vector> | ||
| 18 | #include "fpphttp_types.h" | ||
| 19 | #include "config.h" | ||
| 20 | |||
| 21 | constexpr uint32_t DEFAULT_GPIO_DEBOUNCE_TIME = 100000; | ||
| 22 | |||
| 23 | class PinCapabilities; | ||
| 24 | |||
| 25 | class GPIOManager { | ||
| 26 | public: | ||
| 27 | static GPIOManager INSTANCE; | ||
| 28 | // Last value set on each pin by the "GPIO" command / API. Written from | ||
| 29 | // API threads, the main loop and the LED pulse timer at once, so FPP's | ||
| 30 | // own code only goes through the locked accessors below; the member stays | ||
| 31 | // public for out-of-tree code that read it directly. | ||
| 32 | std::map<std::string, bool> fppCommandLastValue; | ||
| 33 | static void SetCommandLastValue(const std::string& pin, bool value); | ||
| 34 | static bool GetCommandLastValue(const std::string& pin, bool& value); | ||
| 35 | static bool ToggleCommandLastValue(const std::string& pin); // returns the new value | ||
| 36 | HttpResponsePtr render_GET(const HttpRequestPtr& req); | ||
| 37 | HttpResponsePtr render_POST(const HttpRequestPtr& req); | ||
| 38 | void Initialize(std::map<int, std::function<bool(int)>>& callbacks); | ||
| 39 | void CheckGPIOInputs(void); | ||
| 40 | void Cleanup(); | ||
| 41 | void AddGPIOCallback(const PinCapabilities* pin, const std::function<bool(int)>& cb); | ||
| 42 | void RemoveGPIOCallback(const PinCapabilities* pin); | ||
| 43 | // Live value of a configured GPIO Input pin (rising/falling/hold actions), | ||
| 44 | // by pin name - distinct from fppCommandLastValue, which only tracks | ||
| 45 | // pins explicitly set via the "GPIO" command/API and knows nothing about | ||
| 46 | // input-only pins. Used by the If command's "GPIO Pin" condition source | ||
| 47 | // (Condition.cpp) so checking an input's current state actually works, | ||
| 48 | // not just a previously-commanded output. | ||
| 49 | bool GetInputPinValue(const std::string& name, int& value) const; | ||
| 50 | private: | ||
| 51 | class GPIOState { | ||
| 52 | public: | ||
| 53 | 16 | GPIOState() : | |
| 54 | 16 | pin(nullptr), | |
| 55 | 16 | lastValue(0), | |
| 56 | 16 | lastTriggerTime(0), | |
| 57 | 16 | futureValue(0), | |
| 58 | 16 | pendingValue(0), | |
| 59 | 16 | pendingTime(0), | |
| 60 | 16 | file(-1), | |
| 61 | 16 | holdTime(0), | |
| 62 | 16 | holdFired(false), | |
| 63 | 16 | holdStartTime(0), | |
| 64 | 16 | ledActiveHigh(true), | |
| 65 | 16 | ledIdleMode(LEDIdleMode::Off), | |
| 66 | 16 | ledPulseRateMs(500), | |
| 67 | 16 | ledTriggerMode(LEDTriggerMode::FollowInput), | |
| 68 | 16 | ledTriggerParam(0), | |
| 69 | 16 | ledPulseState(false) {} | |
| 70 | |||
| 71 | const PinCapabilities* pin; | ||
| 72 | int lastValue; | ||
| 73 | long long lastTriggerTime; | ||
| 74 | int futureValue; | ||
| 75 | int pendingValue; // candidate value waiting to settle | ||
| 76 | long long pendingTime; // GetMonotonicTimeMicros() when pendingValue was first seen | ||
| 77 | int file; | ||
| 78 | |||
| 79 | // Multiple commands per edge — old single-object format is normalised | ||
| 80 | // into these vectors during SetupGPIOInput. | ||
| 81 | std::vector<Json::Value> risingActions; | ||
| 82 | std::vector<Json::Value> fallingActions; | ||
| 83 | |||
| 84 | // Long-press / hold: commands fired after holdTime ms of continuous press. | ||
| 85 | std::vector<Json::Value> holdActions; | ||
| 86 | uint32_t holdTime; // ms; 0 = disabled | ||
| 87 | bool holdFired; // true once hold actions have fired this press | ||
| 88 | long long holdStartTime; // GetTimeMS() when the current press began | ||
| 89 | |||
| 90 | // ── LED output associated with this illuminated button ────────────── | ||
| 91 | std::string ledPin; | ||
| 92 | bool ledActiveHigh; // true → HIGH means LED on | ||
| 93 | |||
| 94 | // What the LED does when idle (not in a trigger effect) | ||
| 95 | enum class LEDIdleMode { | ||
| 96 | Off, // LED off at rest | ||
| 97 | On, // LED on at rest | ||
| 98 | Pulse // LED blinks at ledPulseRateMs interval | ||
| 99 | }; | ||
| 100 | LEDIdleMode ledIdleMode; | ||
| 101 | uint32_t ledPulseRateMs; // ms per half-period when pulsing | ||
| 102 | bool ledPulseState; // current toggle state for pulsing | ||
| 103 | |||
| 104 | // What the LED does when a trigger fires (rising edge) | ||
| 105 | enum class LEDTriggerMode { | ||
| 106 | None, // no LED change on trigger; idle mode only | ||
| 107 | FollowInput, // LED mirrors input state (on when pressed, off when released) | ||
| 108 | Flash, // flash N times (ledTriggerParam = flash count) then restore idle | ||
| 109 | TimedOn // stay on for ledTriggerParam ms then restore idle | ||
| 110 | }; | ||
| 111 | LEDTriggerMode ledTriggerMode; | ||
| 112 | uint32_t ledTriggerParam; // flash count OR duration ms | ||
| 113 | |||
| 114 | // ── LED helpers ───────────────────────────────────────────────────── | ||
| 115 | void initLED(); | ||
| 116 | void setLEDState(bool on); | ||
| 117 | void startLEDIdle(); | ||
| 118 | void stopLEDIdle(); | ||
| 119 | void triggerLEDEffect(int v); | ||
| 120 | void doLEDFlash(int halfPeriodsRemaining); | ||
| 121 | |||
| 122 | // ── Callback (for non-command uses) ───────────────────────────────── | ||
| 123 | std::function<bool(int)> callback; | ||
| 124 | bool hasCallback = false; | ||
| 125 | |||
| 126 | // ── Debounce ──────────────────────────────────────────────────────── | ||
| 127 | uint32_t debounceTime = DEFAULT_GPIO_DEBOUNCE_TIME; | ||
| 128 | enum class DebounceEdge { | ||
| 129 | Both, // debounce rising and falling (default) | ||
| 130 | Rising, // debounce only rising; falling fires immediately | ||
| 131 | Falling // debounce only falling; rising fires immediately | ||
| 132 | }; | ||
| 133 | DebounceEdge debounceEdge = DebounceEdge::Both; | ||
| 134 | |||
| 135 | // ── Re-enable after trigger ────────────────────────────────────────── | ||
| 136 | // Controls how long the input is suppressed after commands fire. | ||
| 137 | enum class ReEnableMode { | ||
| 138 | Always, // always enabled — no suppression (default) | ||
| 139 | Timed, // re-enable after reEnableDelay ms | ||
| 140 | OnPlayerIdle // re-enable once the player returns to idle | ||
| 141 | }; | ||
| 142 | ReEnableMode reEnableMode = ReEnableMode::Always; | ||
| 143 | uint32_t reEnableDelay = 0; // ms; used by Timed mode | ||
| 144 | bool inputDisabled = false; // true while the input is suppressed post-trigger | ||
| 145 | |||
| 146 | void scheduleReEnable(); | ||
| 147 | void checkReEnable(long long startMs, bool playerHasStarted); | ||
| 148 | |||
| 149 | bool shouldDebounce(int v) const; | ||
| 150 | void doAction(int newVal); | ||
| 151 | void checkHoldTimer(long long scheduledStart); | ||
| 152 | }; | ||
| 153 | GPIOManager(); | ||
| 154 | ~GPIOManager(); | ||
| 155 | void SetupGPIOInput(std::map<int, std::function<bool(int)>>& callbacks); | ||
| 156 | void addState(GPIOState* state); | ||
| 157 | void addGPIOCallback(GPIOState* state); | ||
| 158 | void checkDebounceTimers(); | ||
| 159 | void scheduleDebounceCheck(GPIOState* state); | ||
| 160 | std::list<GPIOState*> pollStates; | ||
| 161 | std::list<GPIOState*> eventStates; | ||
| 162 | friend class GPIOCommand; | ||
| 163 | }; | ||
| 164 |