This is an old revision of the document!
TailCoNTROL Command Protocol
Command list for the Tail Company TailCoNTROL firmware. TailCoNTROL is the unified firmware platform (version 5.x.x and higher) for the MiTail, MiTail Mini, FlutterWings, and EarGear 2, and covers the shared features that exist across all those devices.
All commands are case-sensitive. Trailing whitespace (e.g., `NULL`, `CR`, etc.) are ignored. The space between a command keyword and the parameters is mandatory.
IMPORTANT NOTICE: TailCoNTROL and the TailCoNTROL Protocol is still under development. Some commands and features described here relate to a future release and are subject to change at this time, especially EarGear 2 features.
Bluetooth Low Energy (BLE)
The hardware platform uses the “Just Works” Bluetooth Low Energy (BLE) pairing method to connect. If Conference Mode (see below) is used, “Passkey” pairing is used.
Device Advertisement
Device name is one of the following:
mitail
for MiTail,minitail
for MiTail Mini,flutter
for FlutterWings, andEG2
for EarGear 2.
The BLE Device Service for the MiTail, MiTail Mini, and FlutterWings is 3af2108b-d066-42da-a7d4-55648fa0a9b6
:
- RX Characteristic is
5bfd6484-ddee-4723-bfe6-b653372bbfd6
, - TX Characteristic is
c6612b64-0087-4974-939e-68968ef294b0
, and the - Battery Voltage is
b08fed02-0584-40ef-b006-aff7e0d24e13
.
For tail-series v3.6 controller hardware, the battery charging characteristic is unset (charging state is not available).
The BLE Device Service for the EarGear 2 is 927dee04-ddd4-4582-8e42-69dc9fbfae66
:
- RX Characteristic is
05e026d8-b395-4416-9f8a-c00d6c3781b9
, - TX Characteristic is
0b646a19-371e-4327-b169-9632d56c0e84
, and the - Battery Voltage is
54fa919d-e8a8-4841-b280-c5461161304f
.
TailCoNTROL Device Indicators
Blue LED
The Blue LED (D4) has five states:
- Off when powered off.
- Blink on-off when BLE is disconnected.
- Fade-off when BLE is disconnected and Autonomous Mode (either iOS Casual Mode or No-phone Mode, see below) is active.
- Constant on when BLE is connected.
- Fade-on when BLE is connected and Autonomous Mode is active.
- Blink fast three times when the button is held for more than 3 seconds (but less than 13), indicates the device is ready to power off when the button is released.
- Blink fast continuously when the button is held for more than 13 seconds, indicates the device is ready to factory reset when the button is released.
Red LED
For v3.2 through v3.5 tail-series controller hardware, the Red LED (D5) has four states:
- Off during normal operation.
- Breathing while charging.
- Constant on when charging is complete.
- Short flash on, followed by pause to indicate a low (⇐ 10%) battery condition.
For v3.6 tail-series controller hardware, the Red LED (D5) has two states:
- Off when charging is disconnected and when charging complete.
- Constant on when charging.
For the EarGear 2, the Red LED has two states:
- Off when charging is disconnected.
- Constant on when charging.
Note: Unlike the DIGITAiL, MiTail, MiTail Mini, and FlutterWings devices can be used while charging.
Green LED
The Green LED (D8) is only present on v3.2 through v3.5 tail-series controller hardware, and is not visible from outside the device case. The LED is a visual indicator for the STAT
pin of the battery charger:
- Constant on is charge in progress.
- Off is charge completed.
- Blink on-off indicates a fault condition.
The EarGear 2 does not have a Green LED.
Automatic Actions
TailCoNTROL will automatically shut down the device if there is no BLE connection for 5 minutes. This behavior is suspended if a PD-capable adapter is attached (to allow for charging and/or operating from a external battery) OR if the firmware is compiled with a compile-time option to disable the timer (to facilitate wired serial console control where BLE connectivity is not used) [Note: to be moved to a configuration option in NVS in a future release].
Additional automatic actions are described in No-phone Mode, below.
Tail Moves
Move commands return <movename> BEGIN
upon start, and <movename> END
upon completion. If the move is set to return home after completion (all firmware moves are), then it will be immediately followed by a TAILHM BEGIN
and a TAILHM END
as well.
A command will return LOWBATT
and reject the command if the battery is less than 10%. ERR
will be returned if the command can't be parsed or otherwise executed.
A new command that is received before the currently executing command is finished will immediately end the current command and start the new one.
Easing functions are as per the ServoEasing libary EaseTypes. However, those functions are presented in Hexidecimal and must be converted to a Decimal number before being used in command syntax. The Easing Functions Cheat Sheet is useful to help visualize what easing functions do.
TAILHM | HoMe position |
TAILS1 | Slow wag 1 |
TAILS2 | Slow wag 2 |
TAILS3 | Slow wag 3 |
TAILFA | FAst wag |
TAILSH | SHort wag |
TAILHA | HAppy wag |
TAILER | ERect |
TAILEP | Erect Pulse |
TAILT1 | Tremble 1 |
TAILT2 | Tremble 2 |
TAILET | Erect Trem |
TAILU1 | User defined 1 |
TAILU2 | User defined 2 |
TAILU3 | User defined 3 |
TAILU4 | User defined 4 |
Glow Tip LED Patterns
LED pattern commands return <movename> BEGIN
upon start, and <movename> END
upon completion.
A command will return LOWBATT
and reject the command if the battery is less than 10%. ERR
will be returned if the command can't be parsed or otherwise executed.
A new command that is received before the currently executing command is finished will immediately end the current command and start the new one.
LEDOFF | LEDs OFF |
LEDREC | RECtangle wave pattern (blink 1 second on, 1 second off) |
LEDTRI | TRIangle wave pattern (fade in 1 second, fade out 1 second) |
LEDSAW | SAWtooth wave pattern (fade in 2 seconds, off) |
LEDSOS | Morse SOS pattern |
LEDBEA | BEAcon (100ms on every 2 seconds) |
LEDFLA | FLAme |
LEDSTR | STRobe |
LEDUS1 | USer defined 1 |
LEDUS2 | USer defined 2 |
LEDUS3 | USer defined 3 |
LEDUS4 | USer defined 4 |
EarGear 2-only Commands
Other Commands
AUTOMODE | AUTOnomous MOde; see below, returns OK |
DSSP | Directly Set Servo Position; see below, returns OK |
HWVER | Returns HardWare VERsion; returns HWVER LEGACY MITAIL or HWVER MITAIL x.x for tail-series controller hardware, HWVER EG2 x.x for EarGear 2 controller hardware |
PING | Keepalive heartbeat (from application), returns PONG |
SETPUSSKEY | SET PaSSKEY, enable Conference Mode, and restart after 3 seconds; see below, returns OK |
SHUTDOWN | SHUT DOWN the unit (will lose the BLE connection), returns SHUTDOWN BEGIN or ERR if charger is attached |
STOPAUTO | STOP AUTOnomous Mode, returns OK , followed by AUTO END |
STOPNPM | STOP No-Phone Mode and disables it in NVS configuration; returns OK , followed by AUTO END |
USERMOVE | See below, returns OK |
STOPPUSSKEY | STOP PaSSKEY, disable Conference Mode and restart after 3 seconds; see below, returns OK |
USERLEDS | See below, returns OK |
VER | Returns the firmware VERsion number; VER x.x.x , followed by a second message: either GLOWTIP FALSE if a Glow Tip is not connected, or GLOWTIP TRUE if one is |
Developer Commands (use at own risk)
BATT | BATTery percentage, returns the integer value of estimated battery capacity remaining (e.g., 61 ) |
FORMATNVS | FORMAT NVS (erase all contents of the default NVS partition, including BLE bonds) and reboot |
OTA | Starts firmware Over The Air update process (e.g., OTA <expected size in bytes> <expected MD5> ); returns BEGIN OTA or ERR on failure to start, OTA SUCCESS on success, or OTA ERR on error during OTA process |
READCONF | READ running CONFiguration; returns space-delimited running configuration parameters (e.g., READCONF 1 5 0 15 40 3 8 0 0 0 3 0 0 0 1 0 123456 ) [ver minsToSleep minsToNPM minNPMPauseSec maxNPMPauseSec groupsNPM servo1home servo2home listenModeNPMEnabled listenModeResponseOnly groupsLM tiltModeNPMEnabled tiltModeResponseOnly disconnectedCountdownEnabled homeOnAppPoweroff conferenceModeEnabled securityPasskey ] |
READNVS | READ CONFiguration from NVS; returns space-delimited configuration parameters stored in NVS (e.g., READNVS 1 5 0 15 40 3 8 0 0 0 3 0 0 0 1 0 123456 ) [ver minsToSleep minsToNPM minNPMPauseSec maxNPMPauseSec groupsNPM servo1home servo2home listenModeNPMEnabled listenModeResponseOnly groupsLM tiltModeNPMEnabled tiltModeResponseOnly disconnectedCountdownEnabled homeOnAppPoweroff conferenceModeEnabled securityPasskey ] |
REBOOT | REBOOT after 3 seconds, returns OK |
SETHOME | SET HOME position (0 through 8) for each servo (e.g., SETHOME 4 4 ), NOTE: only available on FlutterWings, and EarGear 2; returns OK |
TASKU | Prints to the hardware serial console the minimum amount in words of remaining stack space that was available to the task since the task started executing, returns OK |
WRITECONF | WRITE CONFiguration to NVS and set running configuration to match (e.g., WRITECONF 1 5 0 15 40 3 8 0 0 0 3 0 0 0 1 0 123456 ), returns OK [ver minsToSleep minsToNPM minNPMPauseSec maxNPMPauseSec groupsNPM servo1home servo2home listenModeNPMEnabled listenModeResponseOnly groupsLM tiltModeNPMEnabled tiltModeResponseOnly disconnectedCountdownEnabled homeOnAppPoweroff conferenceModeEnabled securityPasskey ] |
Directly Set Servo Position
DSSP (Directly Set Servo Positions) allows a single-position move to be commanded and executed immediately. This is faster (and simpler) than defining a USERMOVE
and then executing it. It also has the advantage of keeping the debug console output synchronous to the move flow, instead of interleaving the command and move flow responses.
The syntax is similar to USERMOVE
, but fewer parameters:
DSSP [E<easeType1>F<easeType1>] A<point1>B<point1> L<numticks1>M<numticks1> [H{0|1}]
Prefix | Parameter Type | Range of Values for Moves |
---|---|---|
E | Easing function to apply to Servo 1 | Default is Linear (no easing), if not specified. This is the decimal representation of the (hexadecimal) easing type enumeration, as per EaseTypes (e.g., 0 = Linear, 129 = Quadratic in/out, 130 = Cubic in/out, 131 = Quartic in/out). |
F | Easing function to apply to Servo 2 | (Same as E) |
A | Point for Servo 1 | <0 … 8> 0 → 25 degrees 1 → 41 degrees 2 → 58 degrees … 8 → 160 degrees |
B | Point for Servo 2 | (Same as A) |
L | Time between the current Servo 1 point and the next in ticks (in 20 ms increments) L will move from the current position to the next, over the time specified | 0 … 127 (time * 20 ms) |
M | Time between the current Servo 2 point and the next in ticks (in 20 ms increments) M will move from the current position to the next, over the time specified | (Same as L) |
H | Move to home position at end of move | 0 = false (default), 1 = true |
Notes:
- Parameters of type A, B, E, F, H, L, and M can appear in any order (e.g.,
AABBSS
,ABABSS
,ABSABS
). - Parameters can be separated by any character that is not a number or a letter (e.g., space, comma, semicolon). However, the letters themselves act as separators, and it is recommended that no additional characters are used, as the serial buffer has a limited capacity and may not be able to store the whole instruction.
- Depending on the tail and position, it can sag out of the commanded position when servo power is released at the end of the DSSP move. This may cause a visual defect of jerking back into the previous position when the next move starts.
DSSP Example
DSSP E130F130 A1B7 L75M75 H1
DSSP A1B7 L75M75 H1
User-defined Tail Moves and LEDs Patterns
Up to 4 user-defined move definitions and 4 LED patterns can be sent over the BT/BLE connection and assigned to user
presets (callable with the TAILU1
… TAILU4
commands). These presets will be lost once the unit is powered off.
The two instructions used to send a tail move or a Glow Tip LED pattern definition follow the same syntax and consist of a
keyword (USERMOVE
or USERLEDS
) followed by a number of parameters. These are letter-number pairs, where the letter
defines the type of parameter (see table, below).
USERMOVE U<preset> P<numpoints> N<numcycles> [E<easeType1>F<easeType1> … E<easeTypen>F<easeTypen>] A<point1>B<point1> … A<pointn>B<pointn> L<numticks1>M<numticks1> … L<numticksn>M<numticksn> [H{0|1}]
Prefix | Parameter Type | Range of Values for Moves |
---|---|---|
U | User preset number | <1 … 4> |
E | Easing function to apply to Servo 1 | Default is Linear (no easing), if not specified. This is the decimal representation of the (hexadecimal) easing type enumeration, as per EaseTypes (e.g., 0 = Linear, 129 = Quadratic in/out, 130 = Cubic in/out, 131 = Quartic in/out). |
F | Easing function to apply to Servo 2 | (Same as E) |
A | Point for Servo 1 | <0 … 8> 0 → 25 degrees 1 → 41 degrees 2 → 58 degrees … 8 → 160 degrees |
B | Point for Servo 2 | (Same as A) |
L | Time between the current Servo 1 point and the next in ticks (in 20 ms increments) L will move from the current position to the next, over the time specified | 0 … 127 (time * 20 ms) |
M | Time between the current Servo 2 point and the next in ticks (in 20 ms increments) M will move from the current position to the next, over the time specified | (Same as L) |
H | Move to home position at end of move | 0 = false (default), 1 = true |
USERLEDS U<preset> P<numpoints> N<numcycles> A<point1> … A<pointn> [S|L]<numticks1> … [S|L]<numticksn>
Prefix | Parameter Type | Range of Values for Glow Tip LEDs |
---|---|---|
U | User preset number | <1 … 4> |
P | Number of points in the Glow Tip pattern | <1 … 32> |
N | Number of cycles (times the pattern will be repeated) | <0 … 255> |
A | Brightness point for Glow Tip | <0 … 8> 0 → LEDs off … 4 → 50% intensity … 8→ LEDs max intensity |
S/L | Time between the current point and the next (in 20 ms increments) S will wait in the current position, then move to the next when the time has elapsed L will gradually move from the current position to the next, over the time specified | 0 … 127 (time * 20 ms) |
Notes:
- Parameters of type A, B, E, F, H, L, M, and S can appear in any order (e.g., `EEAABBSS`, `ABAEBESS`, etc.).
- Parameters can be separated by any character that is not a number or a letter (e.g., space, comma, semicolon). However, the letters themselves act as separators, and it is recommended that no additional characters are used, as the serial buffer has a limited capacity and may not be able to store the whole instruction.
- Note the upper limit on the P parameter (and the consequential limit of moves in a move or LED pattern).
- There is a 128-character limit on the serial input buffer.
Position limits on the tail can be visualized as such:
USERMOVE Examples
Example 1 – Slow Wag 1 (same as the ''TAILS1'' command)
Both servos move from 143° to 41° (position 7 to 1) and back, for 3 times; each cycle is (75 + 75) * 20 ms = 3 s long.
USERMOVE U1 P2 N3 E0E66 F0F66 A7A1 B7B1 L75L75 M75M75 H1
- U1 Store into user preset 1
- P2 The move consists of 2 points
- N3 Repeat the sequence 3 times
- E0E66 Servo 1 move to Position 1 has no easing, move to Position 2 uses EASE_CUBIC_OUT (0x42)
- F0F66 Servo 2 move to Position 1 has no easing, move to Position 2 uses EASE_CUBIC_OUT (0x42)
- A7A1 Servo 1 moves from 7 to 1 (143° to 41°)
- B7B1 Servo 2 moves exactly as Servo 1
- L75L75 Position 2 is reached in 75 * 20 ms = 1.5 s; return to Position 1 in the same time
- L75L75 Position 2 is reached in 75 * 20 ms = 1.5 s; return to Position 1 in the same time
- H1 Home at end of move
Example 2 – Test Servos
Moves servos in steps of 90° every 2 seconds; Servo 2 is delayed by 90°.
USERMOVE U2 P4 N3 A4A8A4A0 B4B8B4B0 L100L100L100L100 M100M100M100M100 H1 TAILU2
- U2 Store into user preset 2
- P4 The move consists of 4 points
- N3 Repeat the sequence 3 times
- A0A4A8A4 Move Servo 1 90° at a time, starting from 0°
- B4B8B4B0 Move Servo 2 90° at a time, starting from 90°
- L100L100L100L100 Each Servo 1 move takes 100 * 20 ms = 2 s to complete
- M100M100M100M100 Each Servo 2 move takes 100 * 20 ms = 2 s to complete
- H1 Home at end of move
Because no easing parameters (E
or F
) are specified, no easing is used by default.
USERLEDS Examples
Example 1 – Beacon (same as the ''LEDBEA'' command)
The Glow Tip LEDs light up for 100 ms every 1 s (Airbus A320 tail strobe).
USERLEDS U1 P2 N5 A8A0 S5S50 LEDUS1
- U1 Store into user preset 1
- P2 The pattern consists of 2 brightness points
- N5 Repeat pattern 5 times
- A8A0 Start at full brightness, then turn off
- S5S95 On for 5 * 20 ms = 100 ms; off for 50 * 20ms = 1 s
Example 2 – Fade in/out (similar to ''LEDTRI'')
The Glow Tip LEDs light up slowly, then dim until completely off; this is repeated 3 times.
USERLEDS U2 P2 N3 A0A8 L100L100
- U2 Store into user preset 2
- P2 The pattern consists of 2 brightness points
- N3 Repeat pattern 3 times
- A0A8 Start off, finish at full brightness
- L100L100 Each brightness point is reached in 100 * 20 ms = 2 s
Autonomous Mode
Defines a sequence of random moves, selected from any of three groups as below; interval between moves varies randomly from T1 to T2.
AUTOMODE G<n>[G<n> … [G<n>]] T<nnn>T<nnn>T<nnn>
The tail will select a random move from the specified group(s), pausing for a random number of seconds between each move (min and max specified by the first and second T
parameter, respectively). The tail will stop completely after the time passed by the 3rd T
parameter.
Autonomous Mode is ended (after completing any ongoing move) if a BLE connection is made.
Group | Move Group Name | Move List |
---|---|---|
1 | Calm and Relaxed | Slow Wag 1, Slow Wag 2, Slow Wag 3 |
2 | Fast and Excited | Fast Wag, Short Wag, Happy Wag, Erect |
3 | Frustrated and Tense | Tremble 1, Tremble 2, Tremble Erect, High Wag |
The parameter order is not important, except that the three T
parameters are interpreted in the order specified below.
Prefix | Parameter Type | Range of Values |
---|---|---|
G | Move Group (see table above) | <1 … 3> |
T1 | Minimum random pause between moves in 1-second increments | <1 … 240> (1 s … 4 min) |
T2 | Maximum random pause between moves in 1-second increments | <1 … 240> (1 s … 4 min) |
T3 | Total duration in 15-second increments | <1 … 240> (15 s … 60 min); <251 … 254> are reserved, see No-phone Mode, below) |
STOPAUTO
STOPAUTO
will abort the current Autonomous Mode after completing any ongoing move.
Conference Mode
Conference mode pairs the user's phone with the TailCoNTROL device and establishes a bond that performs long-term storage of encryption info (particularly keys) so that the devices “know” each other and can easily reconnect in an encrypted way. This protects your device in a number of different ways:
- Authentication: verifying the identity of the device connecting.
- MITM (Man In The Middle): process by which a third device impersonates the other two legitimate devices, in order to fool them into connecting to it. Both BLE central and peripheral will connect to the malicious device, which in turn routes the communication between the other two devices. The malicious device intercepts all data being sent and can inject false data or remove data before it reaches its recipient.
- Replay Attack: a type of network attack in which an attacker captures a valid network transmission and then retransmits it later. The main objective is to trick the system into accepting the retransmission of the data as legitimate.
Enabling Conference Mode (Pairing and Binding)
Pairing is performed with a 6-digit number entered on each of the devices. In the case of TailCoNTROL, the passkey is set on the device in a “safe” location and saved to the firmware configuration.
SETPUSSKEY <nnnnnn>
The device will then reboot in 3 seconds. Conference Mode is then enabled, and pairing proceeds. The user's phone (or other device) will prompt for the passkey (which was set earlier), and the bond will be established on both devices. After that, the passkey or a previous bond (up to 3 can be stored) will be required to connect to the device.
Disabling Conference Mode
Conference Mode can be disabled by sending the STOPPASSKEY
command. This disables Conference Mode, and sets the stored and running configuration passkey to the default (123456
).
STOPPUSSKEY
The device will then reboot in 3 seconds. If the bond needs to be removed or reset, see Unbinding, below.
Unbinding
Resetting/removing bonds requires two separate actions: resetting the TailCoNTROL device, and removing the bond on the phone or other device.
There are two different methods of factory-resetting a TailCoNTROL device (which resets all persistent options to defaults and removes all bonding information):
- Sending a
FORMATNVS
command to the TailCoNTROL console. This will factory-reset and reboot. - Performing a factory reset using the single button:
- Press and hold the power button.
- At approximately 3 seconds, the blue LED will flash three times and turn off. Continue to hold without releasing the button. If you Wish to cancel the factory reset, release at any time before the next step and the device will power off.
- Ten seconds after the triple-flash (13 seconds total of continuous holding), the blue LED will blink quickly continuously, release to perform the factory reset. The blue LED will go solid, then the ESP will restart, with the blue LED flashing slowly (ready to connect).
The bond will also need to be removed on the phone or other device as well, or it will attempt to continue to use the bond (which will fail, because those stored credentials will no longer be valid).
- On Android, enter Settings, Connections, Bluetooth and find the device to remove (e.g., “mitail”). Select the gear icon, and then select “Unpair”. Confirm the dialog to unpair and forget the device.
- On iOS, enter Settings, Bluetooth and find the device to remove under “My Devices”. Select the circle-i icon next to the device name, and then select “Forget This Device”. Confirm by selecting “Forget Device”.
iOS Casual Mode
This feature is pending removal in a future release.
Because the CRUMPET application is not currently permitted to manage the tail while the iOS application is in the background, the application uses the AUTOMODE
command to have the tail emulate the Casual Mode behavior on-board the tail (Note there is a typo in the string the iOS app sends: there is no space between the command and the first parameter):
AUTOMODEG1 T15 T100 T240
Note that the G
and the first two T
parameters will depend on the selections made in the app.
Important! The minimum random pause cannot be less that 15 seconds.
iOS Casual Mode Example
AUTOMODE T15T60T240 G1G2
- T15 15-second minimum random pause between moves
- T60 60-second (1 minute) maximum random pause between moves
- T240 3600-second (240 * 15 = 3600 s = 1 hour) total duration of Autonomous Mode
- G1G2 Randomly select a move from groups 1 and 2
Listen Mode
Listen Mode is only available on the EarGear 2. When Listen Mode is active, a detected sound above ambient will trigger a random move and a LISTEN BANG
message will be sent back to the client. If listenModeMovEn
is set (default), then the firmware move will be triggered, otherwise the controlling app will be expected to send a move (if desired).
Development support for basic sound localization for future hardware is supported, replacing the LISTEN BANG
response with
LISTEN BANG CENTER
, LISTEN BANG LEFT
, and LISTEN BANG RIGHT
, respectively.
LISTEN ARMED
is sent to the debug console (but not currently to the BLE terminal) after all conditions to re-trigger have been met.
Triggering by Listen Mode is inhibited when the ears are in motion, as well as a 3-second counter that runs once a sound trigger has finished executing a move.
No-phone Mode
No-phone mode is a feature exclusive of MiTail firmware 4.0.0 or greater or TailCoNTROL.
AUTOMODE G<n>[G<n>[G<n>]] T<nnn>T<nnn>T<251 … 254>
No-phone Mode (NPM) will cause the tail to delay one to four minutes (based on the setting of the third T
parameter of 251 through 254) to wait for a BLE connection. If no BLE connection is made during that time, the tail will select a random move from the specified group(s), pausing for a random number of seconds between each move (min and max specified by the first and second T
parameter, respectively).
No-phone Mode is suspended (after completing any ongoing move) if a BLE connection is made. It will resume after BLE is disconnected.
STOPNPM
Stops No-phone mode and disables it in NVS configuration.
Tilt Mode
Tilt Mode is only available on the EarGear 2. When Listen Mode is active, a detected sound above ambient will trigger a random move and a TILT FORWARD
, TILT BACKWARD
, TILT LEFT
, or TILT RIGHT
message (depending on the tilt direction) will be sent back to the client. If tiltModeMovesEnabled is set (default), then the firmware move will be triggered, otherwise the controlling app will be expected to send a move (if desired).
Triggering by Tilt Mode is inhibited when the ears are in motion, as well as a 3-second counter that runs once a sound trigger has finished executing a move AND the EarGear 2 has returned to the neutral tilt position during that time. This prevents tilt re-triggering if the user stays in the tilt position for an extended time.