The Joy sketch is intended to be uploaded to a Teensy 3.2 to perform the following tasks.
- Recieve information about the current state of the radio controller (axes, buttons, etc.) and relay that information as a virtual joystick device to be interfaced with using libraries such as SDL (roughly the bottom half of the diagram above).
- Send telemetery values to display on the TARANIS controller based on the information read over USB Serial (roughly the top half of the diagram).
Functions/variables dealing with the first task often have the sbus_
prefix, while ones implementing the second task often have the tele_
or sport_
prefix.
The global constants and fields are documented at the bottom of this page.
Calculates the proper checksum byte to be sent at the end of the reply packet.
Reads and throws out any bytes that haven't been read from the buffer from the half-duplexed telemetry line. Is called before switching between RX and TX mode on the half-duplexed line.
Puts Serial3 (Pin8) in RX mode despite that Pin8 is the TX pin of the Serial3 UART. This is done through changing individual bit registries on the Teensy. See the section in the README on half duplexing for more information.
Sets up the Serial3 UART for inverted serial at 57600 baud in half-duplex mode. See the section in the README on half duplexing for more information.
Puts the Serial3 UART in TX mode by changing the registries. See the section in the README on half duplexing for more information.
Sends a reply packet to update a value id with new data. This function is called by sport_telemetry()
.
Num | Name | Usage |
---|---|---|
0 | id | The value id being updated. See tele_ids for the valid value ids that may be used for the current setup |
1 | val | The value that will display on the TARANIS given the current id. Although the fuction takes a uint32_t and four value bytes are sent over the half-duplexed line as per the SPORT protocol, testing has only gotten a signed 16 bit integer to work, with the last two bytes that are sent on the half-duplexed line being zeros. |
- Create a sport_reply_packet type and fill it with the information from the arguments
- Set the half-duplexed line to TX mode.
- Generate the checksum byte based on the current reply paket
- Iterate over all the bytes in the packet and add the byte to the out buffer. If the byte needs to be escaped according to the SPORT protocol, add the proper bytes to the out buffer.
- Write the out buffer to the Serial3 UART
- Put the half-duplexed line (Serial3) into RX mode.
See the SPORT protocol section in the TeleJoy README for more information.
The two functions that deal with updating the telemetry values are usb_addSafeByte()
and sport_tryUsbInput()
.
The above image shows:
- How the intended telemetry data is filled into an array
- This data is converted a longer array of single bytes
- Those bytes are sent over USB serial
- tryUsbInput recieves those bytes and rebuilds the original desired telemetry values
The job of tryUsbInput()
is to sort out telemtry data from headers and escape bytes. It has the following flow:
usb_addSafeByte()
is called by tryUsbInput()
to process a byte which is guaranteed to be the actual data (blue X in the diagram above) that was intended to be sent over USB Serial.
If no escaping/headers was required for updating packets of telemetry data, then each individual byte of data could be passed to
usb_addSafeByte()
.
As per the USB Serial updating protcol (number 4 in the main README), the least significant byte is sent first, followed by the most significant byte.
usb_addSafeByte()
has three different operations based on the current state.
Name | Conditions | Actions |
---|---|---|
Bail | Current index (red #s above) is invalid | Do nothing with the byte |
Set LSB | Current index is valid and LSB not set | Save the lsb for later and remember that LSB has been set |
Set MSB | Current index valid and LSB is set | Construct a int16_t from the two bytes, increment index |
The following flowchart illustrates the information in the above table:
Unless is valid header is seen in tryUsbInput(), the currentIndex will be -1, which is invalid, and all the bytes will get ignored.
Most importantly, addSafeByte()
is the only method which directly updates the tele_data[]
array as shown in the joy master flowchart.
Sport telemetry reads and recieves the request packets over serial from the radio reciever. It checks to make sure that these packets are valid, and then, if valid, can decide to send a response packet which will update the telemetry value for a specific value id.
The table in the above diagram checks that the sensor id is 0x22 because that is the currently (when this documentation was written) single sensor id that the code tests for.
This function is called by the arduino function setup()
. It sets up the "sport" side of the code. Most notably, it calls sport_hdInit()
which sets up the Serial3 UART to be half-duplexed.
Calls tryUsbInput() and sport_telemetry() if applicable so they can be constantly testing their inputs.
It also turns off the onboard LED on the Teensy every 10 seconds.
In sport_telemetry()
, the led is turned back on, so if sport_telemetry()
is never called because no more requests are coming in on the Serial3 UART, then the onboard LED will stay off.
A past, bugged version of the joy sketch would not properly follow the expected SPORT protocol with the radio reciever, and the radio reciever would stop sending request packets- ending all telemetry data exchange.
If this happens, there will be an indication by the onbard LED not lighting.
Controller / SBUS Functions
The second half of the program (roughly the bottom half of the flowchart) deals with the current state of the radio controller and relaying those states to the Joystick
interface on the Teensy which allows the Teensy to act like a proper Joyhttps://github.com/osudrl/TeleJoy/tree/master/joy#initial-constants-lines-4-13stick device that can be interfaced with using libraries such as SDL.
As illustrated in the joy flowchart, the sbus_decode_packet()
function takes a buffer of 25 bytes of data that was recieved from the reciever's SBUS line and decodes it to fill a sbus_data_t
struct which contains the current states of the 16 analog axes. Is called by sport_loop
and sendJoyOutput()
is called by sport_loop()
after this function returns.
This function is left out of the joy flowchart but is responsible for sending the data from the newly updated sbus_data_t
struct to the TeensyJoystick
interface. sendJoyOutput()
is called right after sbus_decode_packet()
returns by sbus_loop()
and each analog axis in the sbus data struct is translated to an axis on the emulated Joystick
and then sent over USB.
Source | Left Range | Right Range |
---|---|---|
.analog[ ] from sbus_data_t | -820 to 0 | 0 to 820 |
Sent to USB Joystick (Joystick. ) |
~3950 to 32975* | 32975* to ~62000 |
Recieved in SDL SDL_JoystickGetAxis() |
-32768 to 0 | 0 to 32768 |
The approximate values above were determined from experimenting with the joy code and interfacing it with the jstest program. Their values are set in the constants at the top of joy.ino but can be tuned or changed as more testing is done and if any issues are found with the range or responsiveness.
Passing an axis value of 3950 to Joystick.slider()
or Joystick.X()
results in the leftmost value (-32768) observed in SDL, and passing a value of 62000 to the Joystick interface on the Teensy will result in the rightmost value (32768) read in SDL.
*When testing, the operating system was creating an artificial deadzone around the neutral position (0 in SDL).
When passing any value within 3800 values of 32975, the mean ((62000-3950) /2 =32975), jstest would show the axis at 0.
For all values from 29175 to 36775 that were passed to the Teensy Joystick
interface, the result in jstest would be 0.
To counteract this software-enforced deadzone, no values from 29175 to 36775 are sent to the Joystick
interface, and instead the left (negative) values are mapped from [-820 to 0] to [3950 to 29175] and the right (positive) values are mapped from [0 to 820] to [36775 to 62000].
The mapping and transformation of the analog axis data is illustrated in the below diagram and is able to be tweaked (or turned off) by modifying the DEADZONE_MITIGATION_CONSTANT
defined at the top of the joy sketch.
sbus_loop()
is called by the Arduino-mandated loop()
function.
Sets up the Serial1 UART so that it can RX data sent over the SBUS line from the radio reciever. The protocol and baudrate that is used by the SBUS protocol is unusual and specific so this line is needed to properly setup Serial1.
Initial Constants (lines 4-13)
Name | Value | Purpose |
---|---|---|
SPORT_ REQUEST_ HEADER | 0x7e | The header byte that validates a proper request packet for the SPORT protocol. |
SPORT_ ONLY_ SENSOR_ ID | 0x22 | The only sensor id that the telemetry (sport) code sends a reply to with this implementation of the SPORT protocol. Can be any of the sensorids that the reciever polls, but replying to all the sensors will cause the reciever to not respond. |
JOY_ MIN | 3950 | Approximately the value that corresponds to the most negative input that the operating system supports for analog joystick axes |
JOY_ MAX | 62000 | See above and the mapAnalog() function. |
IN_ MIN | -820 | The minimum analog joystick supplied by the input from the sbus line from the radio reciever. |
IN_ MAX | 819 | See above. |
DEADZONE_ MITIGATION_ CONSTANT | 3800 | The operating system interprets joysticks as having a pretty large deadzone around 0. Approximately, all values between -3800 to 3800 will be interpreted as exactly zero by the os. This value can be tuned based on experimentation. |
TELE_ ALLOWED_ IDLE_ TIME | 5000 | The amount of time that the joy program will wait before re-sending a telemetry value that hasn't changed to the reciever. See the sport_telemetry() function and the tele_msUpdated[ ] array for more information. TODO add links. |
Name | Purpose |
---|---|
tele_ids | Contains the fourteen different data ids that the TARANIS will display. For instance, with the current setup, ids 1-4 refer to the error codes that are displayed on page1, while the rest of the array is the ids of the 9 different channels displayed on the following telemetry page. |
tele_data | Contains the values that corrospond to ids contained in tele_ids at the same index |
tele_msUpdated | Holds the number of milliseconds that had elapsed when the value at that index was most recently updated. Used to ensure that a value doesn't go un-updated for longer than TELE_ALLOWED_IDLE_TIME . |
Written by Kevin Kellar for Oregon State University's Dynamic Robotics Laboratory. For issues, comments, or suggestions with this guide, contact the developer or open an issue.