diff --git a/radio/src/haptic.cpp b/radio/src/haptic.cpp index ed1d7cbdf..845d3090b 100644 --- a/radio/src/haptic.cpp +++ b/radio/src/haptic.cpp @@ -36,6 +36,8 @@ hapticQueue::hapticQueue() t_queueWidx = 0; hapticTick = 0; + + intensity = userHapticStrength; } void hapticQueue::heartbeat() @@ -46,7 +48,11 @@ void hapticQueue::heartbeat() if (buzzTimeLeft > 0) { buzzTimeLeft--; // time gets counted down #if defined(HAPTIC_PWM) - hapticOn(HAPTIC_STRENGTH() * 20); + if (intensity < userHapticStrength) { + hapticOn(intensity); + } else { + hapticOn(HAPTIC_STRENGTH() * 20); + } #else if (hapticTick-- > 0) { hapticOn(); @@ -65,6 +71,7 @@ void hapticQueue::heartbeat() else if (t_queueRidx != t_queueWidx) { buzzTimeLeft = queueHapticLength[t_queueRidx]; buzzPause = queueHapticPause[t_queueRidx]; + intensity = queueHapticIntensity[t_queueRidx]; if (!queueHapticRepeat[t_queueRidx]--) { t_queueRidx = (t_queueRidx + 1) & (HAPTIC_QUEUE_LENGTH-1); } @@ -73,13 +80,16 @@ void hapticQueue::heartbeat() #endif // defined(SIMU) } -void hapticQueue::play(uint8_t tLen, uint8_t tPause, uint8_t tFlags) +void hapticQueue::play(uint8_t tLen, uint8_t tPause, uint8_t tFlags, uint8_t tIntensity) { - tLen = getHapticLength(tLen); + if(tLen > 0){ + tLen = getHapticLength(tLen); + } if ((tFlags & PLAY_NOW) || (!busy() && empty())) { buzzTimeLeft = tLen; buzzPause = tPause; + intensity = tIntensity; t_queueWidx = t_queueRidx; } else { @@ -93,6 +103,7 @@ void hapticQueue::play(uint8_t tLen, uint8_t tPause, uint8_t tFlags) queueHapticLength[t_queueWidx] = tLen; queueHapticPause[t_queueWidx] = tPause; queueHapticRepeat[t_queueWidx] = tFlags-1; + queueHapticIntensity[t_queueWidx] = tIntensity; t_queueWidx = next_queueWidx; } } diff --git a/radio/src/haptic.h b/radio/src/haptic.h index 219beeb6f..ad7d5a8fa 100644 --- a/radio/src/haptic.h +++ b/radio/src/haptic.h @@ -23,18 +23,23 @@ #define HAPTIC_QUEUE_LENGTH 4 +// sentinel: use HAPTIC_STRENGTH() setting instead of an explicit intensity +constexpr uint8_t userHapticStrength = 255; + class hapticQueue { public: hapticQueue(); - // only difference between these two functions is that one does the - // interupt queue (Now) and the other queues for playing ASAP. - void play(uint8_t tLen, uint8_t tPause, uint8_t tRepeat=0); + // The PLAY_NOW flag interrupts and plays immediately; otherwise this + // queues to play as soon as the current buzz finishes. + void play(uint8_t tLen, uint8_t tPause, uint8_t tFlags=0, uint8_t tIntensity=userHapticStrength); inline bool busy() { return (buzzTimeLeft > 0); } + inline uint8_t getIntensity() { return intensity; } + void event(uint8_t e); // heartbeat is responsibile for issueing the haptic buzzs and general square waves @@ -61,10 +66,13 @@ class hapticQueue uint8_t hapticTick; + uint8_t intensity; + // queue arrays uint8_t queueHapticLength[HAPTIC_QUEUE_LENGTH]; uint8_t queueHapticPause[HAPTIC_QUEUE_LENGTH]; uint8_t queueHapticRepeat[HAPTIC_QUEUE_LENGTH]; + uint8_t queueHapticIntensity[HAPTIC_QUEUE_LENGTH]; }; extern hapticQueue haptic; diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index e2f5542a7..c136907b2 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -1658,7 +1658,7 @@ static int luaScreenshot(lua_State * L) } /*luadoc -@function playHaptic(duration, pause [, flags]) +@function playHaptic(duration, pause [, flags [, intensity]]) Generate haptic feedback @@ -1670,7 +1670,11 @@ Generate haptic feedback * `0 or not present` play with normal priority * `PLAY_NOW` play immediately -@status current Introduced in 2.2.0 +@param intensity (number) [optional] haptic motor strength, 0-100 (only +effective on boards with PWM-driven haptic support); defaults to the user's +configured haptic strength setting + +@status current Introduced in 2.2.0, intensity added in 3.0 */ static int luaPlayHaptic(lua_State * L) { @@ -1678,7 +1682,12 @@ static int luaPlayHaptic(lua_State * L) int length = luaL_checkinteger(L, 1); int pause = luaL_checkinteger(L, 2); int flags = luaL_optinteger(L, 3, 0); - haptic.play(length, pause, flags); + int intensity = luaL_optinteger(L, 4, userHapticStrength); + if (intensity != userHapticStrength) { + if (intensity < 0) intensity = 0; + else if (intensity > 100) intensity = 100; + } + haptic.play(length, pause, flags, intensity); #else UNUSED(L); #endif diff --git a/radio/src/tests/haptic.cpp b/radio/src/tests/haptic.cpp new file mode 100644 index 000000000..375a0c176 --- /dev/null +++ b/radio/src/tests/haptic.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "gtests.h" + +TEST(Haptic, DefaultIntensityUsesUserStrength) +{ + hapticQueue hq; + EXPECT_EQ(userHapticStrength, hq.getIntensity()); +} + +TEST(Haptic, PlayWithoutIntensityKeepsUserStrength) +{ + hapticQueue hq; + hq.play(15, 3, PLAY_NOW); + EXPECT_EQ(userHapticStrength, hq.getIntensity()); +} + +TEST(Haptic, PlayAcceptsCustomIntensityWithCustomDuration) +{ + hapticQueue hq; + hq.play(15, 3, PLAY_NOW, 50); + EXPECT_TRUE(hq.busy()); + EXPECT_EQ(50, hq.getIntensity()); +} + +TEST(Haptic, PlayAcceptsMinAndMaxIntensity) +{ + hapticQueue hq; + hq.play(15, 3, PLAY_NOW, 0); + EXPECT_EQ(0, hq.getIntensity()); + + hq.play(15, 3, PLAY_NOW, 100); + EXPECT_EQ(100, hq.getIntensity()); +} + +TEST(Haptic, QueuedPlayDoesNotChangeCurrentIntensity) +{ + hapticQueue hq; + hq.play(15, 3, PLAY_NOW, 50); + ASSERT_TRUE(hq.busy()); + ASSERT_EQ(50, hq.getIntensity()); + + // queue busy -> gets queued rather than applied immediately + hq.play(5, 1, PLAY_REPEAT(1), 80); + EXPECT_EQ(50, hq.getIntensity()); +}