FastLED 3.9.15
Loading...
Searching...
No Matches
potentiometer.cpp.hpp
Go to the documentation of this file.
1#include "fl/stl/stdint.h"
2#include "platforms/is_platform.h"
3#include "fl/ui/ui.h"
4#include "fl/stl/assert.h"
6#include "fl/system/pin.h"
7#include "fl/math/math.h"
8#include "fl/stl/noexcept.h"
9
10namespace fl {
11
12// ============================================================================
13// PotentiometerLowLevel Implementation
14// ============================================================================
15
19
21
25
26// ============================================================================
27// Potentiometer Implementation
28// ============================================================================
29
30Potentiometer::Potentiometer(int pin, u16 hysteresis)
31 : mPot(pin), mListener(this), mHysteresis(hysteresis) {
32 // Initialize calibration range to full ADC range
33 mMinValue = 0;
35
36 // Read initial value
37 mCurrentValue = mPot.read();
39
40 // Auto-calculate hysteresis if not specified (1% of calibrated range)
41 if (mHysteresis == 0) {
43 }
44}
45
47 // Handle invalid range
48 if (mMaxValue <= mMinValue) {
49 return 0.0f;
50 }
51
52 // Clamp current value to calibrated range
53 u16 clamped_value;
55 clamped_value = mMinValue;
56 } else if (mCurrentValue > mMaxValue) {
57 clamped_value = mMaxValue;
58 } else {
59 clamped_value = mCurrentValue;
60 }
61
62 // Map calibrated range to [0.0, 1.0]
63 u16 range = mMaxValue - mMinValue;
64 u16 offset = clamped_value - mMinValue;
65 return static_cast<float>(offset) / static_cast<float>(range);
66}
67
69 // Handle invalid range
70 if (mMaxValue <= mMinValue) {
71 return 0;
72 }
73
74 // Clamp current value to calibrated range
75 u16 clamped_value;
77 clamped_value = mMinValue;
78 } else if (mCurrentValue > mMaxValue) {
79 clamped_value = mMaxValue;
80 } else {
81 clamped_value = mCurrentValue;
82 }
83
84 // Map calibrated range to [0, 65535]
85 // Use 32-bit intermediate to avoid overflow
86 u32 range = mMaxValue - mMinValue;
87 u32 offset = clamped_value - mMinValue;
88 u32 scaled = (offset * 65535U) / range;
89 return static_cast<u16>(scaled);
90}
91
93 // Clamp percent to valid range
94 if (percent < 0.0f) percent = 0.0f;
95 if (percent > 100.0f) percent = 100.0f;
96
97 // Calculate hysteresis based on calibrated range
98 u16 range = (mMaxValue > mMinValue) ? (mMaxValue - mMinValue) : getAdcMaxValue();
99 mHysteresis = static_cast<u16>((percent / 100.0f) * range);
100}
101
103 // Ensure min < max
104 if (min >= max) {
105 return; // Invalid range, do nothing
106 }
107 mMinValue = min;
108 mMaxValue = max;
109}
110
112 // Platform detection for ADC resolution
113 // Stub and AVR platforms use 10-bit (0-1023), modern platforms use 12-bit (0-4095)
114#if defined(FL_IS_AVR) || defined(STUB_PLATFORM) || defined(FASTLED_USE_STUB_ARDUINO)
115 return 1023; // 10-bit ADC
116#else
117 return 4095; // 12-bit ADC (ESP32, ESP8266, SAMD, STM32, etc.)
118#endif
119}
120
122 // Default: 1% of calibrated range or minimum of 10 counts (whichever is larger)
123 u16 range = (mMaxValue > mMinValue) ? (mMaxValue - mMinValue) : getAdcMaxValue();
124 u16 one_percent = range / 100;
125 return (one_percent > 10) ? one_percent : 10;
126}
127
129 // Read current value
130 u16 new_value = mOwner->mPot.read();
131 mOwner->mCurrentValue = new_value;
132
133 // Calculate absolute difference from last triggered value
134 u16 diff;
135 if (new_value > mOwner->mLastValue) {
136 diff = new_value - mOwner->mLastValue;
137 } else {
138 diff = mOwner->mLastValue - new_value;
139 }
140
141 // Check if change exceeds hysteresis threshold
142 const bool changed_beyond_hysteresis = (diff >= mOwner->mHysteresis);
143
144 mOwner->mChangedThisFrame = changed_beyond_hysteresis;
145
146 if (changed_beyond_hysteresis) {
147 mOwner->mLastValue = new_value;
148
149 // Invoke callbacks with Potentiometer reference
150 mOwner->mOnChangeCallbacks.invoke(*mOwner);
151
152 // Invoke callbacks with normalized float value
153 float normalized_value = mOwner->normalized();
154 mOwner->mOnChangeNormalizedCallbacks.invoke(normalized_value);
155 } else {
156 // Value didn't change beyond hysteresis
157 mOwner->mChangedThisFrame = false;
158 }
159}
160
164
170
172 if (added) {
173 return;
174 }
175 EngineEvents::addListener(this, 1); // Priority 1 (runs before UI elements)
176 added = true;
177}
178
180 int id = mOnChangeCallbacks.add(callback);
181 return id;
182}
183
184int Potentiometer::onChange(fl::function<void(float)> callback) {
185 int id = mOnChangeNormalizedCallbacks.add(callback);
186 return id;
187}
188
189} // namespace fl
static void removeListener(Listener *listener) FL_NOEXCEPT
static void addListener(Listener *listener, int priority=0) FL_NOEXCEPT
Potentiometer(int pin, u16 hysteresis=0)
u16 calculateDefaultHysteresis() const
void setHysteresisPercent(float percent)
void setRange(u16 min, u16 max)
int onChange(fl::function< void(Potentiometer &)> callback)
fl::function_list< void(float)> mOnChangeNormalizedCallbacks
fl::function_list< void(Potentiometer &)> mOnChangeCallbacks
PotentiometerLowLevel mPot
fl::UISlider offset("Offset", 0.0f, 0.0f, 1.0f, 0.01f)
FL_DISABLE_WARNING_PUSH U constexpr common_type_t< T, U > min(T a, U b) FL_NOEXCEPT
Definition math.h:71
constexpr common_type_t< T, U > max(T a, U b) FL_NOEXCEPT
Definition math.h:75
u16 analogRead(int pin)
Read analog value from pin.
Definition pin.cpp.hpp:59
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_NOEXCEPT
Aggregator header for the fl/ui/ family of per-element UI types.