Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
en:dev:tailcontrol-command-protocol [2024/06/13 00:22] – darkgrue | en:dev:tailcontrol-command-protocol [2025/06/21 14:27] (current) – [Conference Mode] darkgrue | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== | + | ====== |
- | Command list for the Tail Company | + | Command list for the Tail Company |
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. | 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: | + | <WRAP round todo 90%> |
+ | **IMPORTANT NOTICE: | ||
+ | </ | ||
===== Bluetooth Low Energy (BLE) ===== | ===== Bluetooth Low Energy (BLE) ===== | ||
- | The hardware platform uses the "Just Works" Bluetooth Low Energy (BLE) pairing method to connect. | + | The hardware platform uses the "Just Works" Bluetooth Low Energy (BLE) pairing method to connect. If Conference Mode ([[# |
Line 26: | Line 28: | ||
* TX Characteristic is '' | * TX Characteristic is '' | ||
* Battery Voltage is '' | * Battery Voltage is '' | ||
- | |||
- | **Note:** 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 '' | The BLE Device Service for the EarGear 2 is '' | ||
Line 34: | Line 34: | ||
* Battery Voltage is '' | * Battery Voltage is '' | ||
+ | <WRAP round todo 90%> | ||
+ | TailControl will use a new Service and Characteristics UUIDs going forward, to distinguish it from previous firmwares (and to further enforce the “forklift upgrade” required). | ||
+ | |||
+ | The BLE Device Service for TailControl is '' | ||
+ | * RX Characteristic is '' | ||
+ | * TX Characteristic is '' | ||
+ | * Battery Voltage is '' | ||
+ | </ | ||
+ | |||
+ | <WRAP round important 90%> | ||
+ | For tail-series (MiTail, MiTail Mini, and FlutterWings) v3.6 controller hardware, the battery charging characteristic is unset (charging state is not available). | ||
+ | </ | ||
- | ===== TailCoNTROL | + | ===== TailControl |
==== Blue LED ==== | ==== Blue LED ==== | ||
Line 83: | Line 95: | ||
==== Automatic Actions ==== | ==== Automatic Actions ==== | ||
- | TailCoNTROL | + | TailControl |
Additional automatic actions are described in [[en: | Additional automatic actions are described in [[en: | ||
- | ===== Tail Moves ===== | + | ===== Move Commands |
Move commands return ''< | Move commands return ''< | ||
Line 140: | Line 152: | ||
===== EarGear 2-only Commands ===== | ===== EarGear 2-only Commands ===== | ||
- | | **LISTENMODE** | Starts Listen Mode; [[#Listen Mode|see below]], returns '' | + | | **LISTENMODE** |
- | | **STOPLISTEN** | Stops Listen Mode; [[#Listen Mode|see below]], returns '' | + | | **STOPLISTEN** |
- | | **STOPTILT** | Stops Tilt Mode; [[#Tilt Mode|see below]], returns '' | + | | **STOPTILT** |
- | | **TILTMODE** | Starts Tilt Mode; [[#Tilt Mode|see below]], returns '' | + | | **TILTMODE** |
+ | ===== RGB LED Commands ===== | ||
+ | |||
+ | | **RGBOFF** | **RGB** LEDs **OFF**, returns '' | ||
+ | | **RGBRBO** | **RGB** **R**ain**B**ow pattern, returns '' | ||
+ | | **RGBRB2** | **RGB** **R**ain**B**ow pattern with random sparkly glitter, returns '' | ||
+ | | **RGBCON** | **RGB** **CON**fetti; | ||
+ | | **RGBSIN** | **RGB** **SIN**e; a colored dot sweeping back and forth, with fading trails, returns '' | ||
+ | | **RGBJUG** | **RGB** **JUG**gle; eight colored dots, weaving in and out of sync with each other, returns '' | ||
+ | | **RGBBPM** | **RGB** **BPM**; colored stripes pulsing at a defined Beats-Per-Minute, | ||
+ | | **RGBDMO** | **RGB** **DE**m**O**; | ||
+ | | **RGBTST** | **RGB** **TE**s**T**; | ||
+ | | **SETRGB** | **SET** **RGB** configuration and restart after 3 seconds; (e.g., '' | ||
===== Other Commands ===== | ===== Other Commands ===== | ||
- | | **AUTOMODE** | **AUTO**nomous **MO**de; [[# | + | | **AUTOMODE** | **AUTO**nomous **MO**de |
- | | **DSSP** | **D**irectly **S**et **S**ervo **P**osition; [[#Directly Set Servo Position|see below]], returns '' | + | | **DSSP** | **D**irectly **S**et **S**ervo **P**osition |
| **HWVER** | Returns **H**ard**W**are **VER**sion; | | **HWVER** | Returns **H**ard**W**are **VER**sion; | ||
| **PING** | Keepalive heartbeat (from application), | | **PING** | Keepalive heartbeat (from application), | ||
- | | **SETPUSSKEY** | **SET** **P**a**SSKEY**, | + | | **SETPUSSKEY** | **SET** **P**a**SSKEY**, |
| **SHUTDOWN** | **SHUT DOWN** the unit (will lose the BLE connection), | | **SHUTDOWN** | **SHUT DOWN** the unit (will lose the BLE connection), | ||
| **STOPAUTO** | **STOP AUTO**nomous Mode, returns '' | | **STOPAUTO** | **STOP AUTO**nomous Mode, returns '' | ||
| **STOPNPM** | **STOP** **N**o-**P**hone **M**ode and disables it in NVS configuration; | | **STOPNPM** | **STOP** **N**o-**P**hone **M**ode and disables it in NVS configuration; | ||
- | | **USERMOVE** | [[# | + | | **USERMOVE** | Set user-defined move ([[# |
- | | **STOPPUSSKEY** | **STOP** **P**a**SSKEY**, | + | | **STOPPUSSKEY** | **STOP** **P**a**SSKEY**, |
- | | **USERLEDS** | [[# | + | | **USERLEDS** | Set user-defined Glow Tip pattern ([[# |
- | | **VER** | Returns the firmware **VER**sion number; '' | + | | **VER** | Returns the firmware **VER**sion number; '' |
Line 165: | Line 189: | ||
| **BATT** | **BATT**ery percentage, returns the integer value of estimated battery capacity remaining | | **BATT** | **BATT**ery percentage, returns the integer value of estimated battery capacity remaining | ||
+ | | **READCONF** | **READ** running **CONF**iguration; | ||
+ | | **WRITECONF** | **WRITE** **CONF**iguration to NVS and set running configuration to match (e.g., '' | ||
+ | | **SETDISCONNECTEDCOUNT** | **SET** BLE **DISCONNECTED** power off **COUNT**down parameter in NVS and running configuration (in minutes, '' | ||
+ | | **SETHOLDONSTOP** | **SET** servo **HOLD** is maintained when the servos **STOP** (maintains servo PWM when not moving, default for EarGear 2); returns '' | ||
+ | | **UNSETHOLDONSTOP** | **UNSET** servo **HOLD** when the servos **STOP** (servo PWM is dropped when not moving, default for tail-based devices); returns '' | ||
+ | | **SETHOME** | **SET** **HOME** position (0 through 8) for each servo (e.g., '' | ||
+ | | **OTA** | Starts firmware **O**ver **T**he **A**ir update process (e.g., '' | ||
| **FORMATNVS** | **FORMAT** **NVS** (erase all contents of the default NVS partition, including BLE bonds) and reboot | | | **FORMATNVS** | **FORMAT** **NVS** (erase all contents of the default NVS partition, including BLE bonds) and reboot | | ||
- | | **OTA** | Starts firmware **O**ver **T**he **A**ir update process (e.g., '' | + | | **READNVS** | **READ** **CONF**iguration |
- | | **READCONF** | **READ** | + | |
- | | **READNVS** | **READ** **CONF**iguration from NVS; returns space-delimited configuration parameters (e.g., | + | |
| **REBOOT** | **REBOOT** after 3 seconds, returns '' | | **REBOOT** | **REBOOT** after 3 seconds, returns '' | ||
- | | **SETHOME** | **SET** **HOME** position (0 through 8) for each servo (e.g., '' | + | | **TASKU** | Prints to the hardware serial console the minimum amount of remaining stack space that was available to the task since the task started executing (high water mark), returns '' |
- | | **TASKU** | Prints to the hardware serial console the minimum amount | + | |
- | | **WRITECONF** | **WRITE** **CONF**iguration to NVS and set running configuration to match (e.g., '' | + | |
===== Directly Set Servo Position ===== | ===== Directly Set Servo Position ===== | ||
Line 183: | Line 209: | ||
^ Prefix ^ Parameter Type ^ Range of Values for Moves ^ | ^ Prefix ^ Parameter Type ^ Range of Values for Moves ^ | ||
- | | **E** | Easing function to apply to Servo 1 | Default is Linear, if not specified.\\ | + | | **E** | Easing function to apply to Servo 1 | Default is Linear |
| **F** | Easing function to apply to Servo 2 | (Same as **E**) | | | **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 | | | **A** | Point for Servo 1 | <0 ... 8>\\ 0 -> 25 degrees\\ 1 -> 41 degrees\\ 2 -> 58 degrees\\ ...\\ 8 -> 160 degrees | | ||
Line 194: | Line 220: | ||
* Parameters of type **A**, **B**, **E**, **F**, **H**, **L**, and **M** can appear in any order (e.g., '' | * Parameters of type **A**, **B**, **E**, **F**, **H**, **L**, and **M** can appear in any order (e.g., '' | ||
* 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. | * 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 jerking back into position when the next move starts. | + | * 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 |
==== DSSP Example ==== | ==== DSSP Example ==== | ||
Line 202: | Line 228: | ||
- | ===== User-defined | + | ===== User-defined Moves and Glow Tip Patterns ===== |
- | Up to 4 user-defined move definitions and 4 LED patterns can be sent over the BT/BLE connection and assigned to user | + | Up to 4 user-defined move definitions and 4 Glow Tip LED patterns can be sent over the BT/BLE connection and assigned to user |
presets (callable with the '' | presets (callable with the '' | ||
- | The two instructions used to send a tail move or a Glow Tip LED pattern definition follow the same syntax and consist of a | + | The two instructions used to send a move or a Glow Tip LED pattern definition follow the same syntax and consist of a |
keyword ('' | keyword ('' | ||
defines the type of parameter (see table, below). | defines the type of parameter (see table, below). | ||
Line 215: | Line 241: | ||
^ Prefix ^ Parameter Type ^ Range of Values for Moves ^ | ^ Prefix ^ Parameter Type ^ Range of Values for Moves ^ | ||
| **U** | User preset number | <1 ... 4> | | | **U** | User preset number | <1 ... 4> | | ||
- | | **E** | Easing function to apply to Servo 1 | Default is Linear, if not specified.\\ | + | | **E** | Easing function to apply to Servo 1 | Default is Linear |
| **F** | Easing function to apply to Servo 2 | (Same as **E**) | | | **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 | | | **A** | Point for Servo 1 | <0 ... 8>\\ 0 -> 25 degrees\\ 1 -> 41 degrees\\ 2 -> 58 degrees\\ ...\\ 8 -> 160 degrees | | ||
Line 223: | Line 249: | ||
| **H** | Move to home position at end of move | 0 = false (default), 1 = true | | | **H** | Move to home position at end of move | 0 = false (default), 1 = true | | ||
- | ===== USERMOVE Examples | + | **USERLEDS U< |
+ | |||
+ | ^ Prefix | ||
+ | | **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 '' | + | === Example 1 – Slow Wag 1 (same as the '' |
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. | 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 E2E42 F2F42 A7A1 B7B1 L75L75 M75M75 H1** | + | **USERMOVE U1 P2 N3 E0E66 F0F66 A7A1 B7B1 L75L75 M75M75 H1** |
* **U1** Store into user preset 1 | * **U1** Store into user preset 1 | ||
* **P2** The move consists of 2 points | * **P2** The move consists of 2 points | ||
* **N3** Repeat the sequence 3 times | * **N3** Repeat the sequence 3 times | ||
- | * **E2E42** No easing | + | * **E0E66** Servo 1 move to Position 1 has no easing, move to Position 2 uses EASE_CUBIC_OUT (0x42) |
- | * **F2F42** No easing | + | * **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°) | * **A7A1** Servo 1 moves from 7 to 1 (143° to 41°) | ||
* **B7B1** Servo 2 moves exactly as Servo 1 | * **B7B1** Servo 2 moves exactly as Servo 1 | ||
Line 242: | Line 287: | ||
* **H1** Home at end of move | * **H1** Home at end of move | ||
- | ==== Example 2 – Test Servos | + | === Example 2 – Test Servos === |
Moves servos in steps of 90° every 2 seconds; Servo 2 is delayed by 90°. | Moves servos in steps of 90° every 2 seconds; Servo 2 is delayed by 90°. | ||
Line 258: | Line 303: | ||
* **H1** Home at end of move | * **H1** Home at end of move | ||
- | **USERLEDS U<preset> P< | + | <WRAP round info 90%> |
+ | Because no easing parameters ('' | ||
+ | </WRAP> | ||
- | ^ Prefix | + | ==== USERLEDS Examples ==== |
- | | **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. | + | |
- | + | ||
- | ===== USERLEDS Examples | + | |
- | ==== Example 1 – Beacon (same as the '' | + | === Example 1 – Beacon (same as the '' |
The Glow Tip LEDs light up for 100 ms every 1 s (Airbus A320 tail strobe). | The Glow Tip LEDs light up for 100 ms every 1 s (Airbus A320 tail strobe). | ||
Line 288: | Line 322: | ||
* **S5S95** On for 5 * 20 ms = 100 ms; off for 50 * 20ms = 1 s | * **S5S95** On for 5 * 20 ms = 100 ms; off for 50 * 20ms = 1 s | ||
- | ==== Example 2 – Fade in/out (similar to '' | + | === Example 2 – Fade in/out (similar to '' |
The Glow Tip LEDs light up slowly, then dim until completely off; this is repeated 3 times. | The Glow Tip LEDs light up slowly, then dim until completely off; this is repeated 3 times. | ||
Line 332: | Line 366: | ||
===== Conference Mode ===== | ===== Conference Mode ===== | ||
- | Conference mode pairs the user's phone with the TailCoNTROL | + | Conference mode pairs the user's phone with the TailControl |
- | * Authentication: | + | |
- | * 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. | + | |
+ | <WRAP round important 90%> | ||
+ | Do NOT attempt to use Conference Mode on TailControl firmware prior to 5.3.0, or a condition requiring repair of the control board can occur.</ | ||
==== Enabling Conference Mode (Pairing and Binding) ==== | ==== 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 " | + | 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 " |
**SETPUSSKEY < | **SETPUSSKEY < | ||
Line 353: | Line 389: | ||
The device will then reboot in 3 seconds. If the bond needs to be removed or reset, see [[en: | The device will then reboot in 3 seconds. If the bond needs to be removed or reset, see [[en: | ||
+ | |||
==== Unbinding ==== | ==== Unbinding ==== | ||
- | Resetting/ | + | Resetting/ |
- | There are two different methods of factory-resetting a TailCoNTROL | + | There are two different methods of factory-resetting a TailControl |
- | * Sending a '' | + | * Sending a '' |
* Performing a factory reset using the single button: | * Performing a factory reset using the single button: | ||
- Press and hold the power button. | - Press and hold the power button. | ||
Line 365: | Line 402: | ||
- Ten seconds after the triple-flash (13 seconds total of continuous holding), the blue LED will blink quickly continuously, | - Ten seconds after the triple-flash (13 seconds total of continuous holding), the blue LED will blink quickly continuously, | ||
- | 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 | + | <WRAP round info 90%> |
+ | Note that 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 | ||
* On Android, enter Settings, Connections, | * On Android, enter Settings, Connections, | ||
* On iOS, enter Settings, Bluetooth and find the device to remove under "My Devices" | * On iOS, enter Settings, Bluetooth and find the device to remove under "My Devices" | ||
+ | </ | ||
- | ==== iOS Casual Mode ==== | ||
- | [This feature is pending removal in a future release.] | + | ===== iOS Casual Mode ===== |
+ | |||
+ | <WRAP round todo 90%> | ||
+ | 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 '' | Because the CRUMPET application is not currently permitted to manage the tail while the iOS application is in the background, the application uses the '' | ||
Line 382: | Line 424: | ||
Important! The minimum random pause cannot be less that 15 seconds. | Important! The minimum random pause cannot be less that 15 seconds. | ||
- | |||
==== iOS Casual Mode Example ==== | ==== iOS Casual Mode Example ==== | ||
Line 396: | Line 437: | ||
===== Listen Mode ===== | ===== 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 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 '' |
+ | |||
+ | Development support for basic sound localization for future hardware is supported, replacing the '' | ||
+ | '' | ||
+ | |||
+ | '' | ||
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. | 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. | ||
Line 403: | Line 449: | ||
===== No-phone Mode ===== | ===== No-phone Mode ===== | ||
- | No-phone mode is a feature exclusive of MiTail firmware 4.0.0 or greater or TailCoNTROL. | + | No-phone mode is a feature exclusive of MiTail firmware 4.0.0 or greater or TailControl. |
**AUTOMODE G< | **AUTOMODE G< | ||
Line 418: | Line 464: | ||
===== Tilt Mode ===== | ===== 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 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 '' |
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. | 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. | ||
- | == Copyright 2024 © The Mechanical Tail Company Limited contact@thetailcompany.com. All Rights Reserved. == | + | == Copyright 2024-2025 © The Mechanical Tail Company Limited contact@thetailcompany.com. All Rights Reserved. The Mechanical Tail Company Limited also claims trademark rights in the following: TailControl, |