[
  {
    "path": ".gitignore",
    "content": "# ignore the build directory\nbuild/\n# ignore vs code\n*.vscode\n"
  },
  {
    "path": ".gitlab-ci.yml",
    "content": "stages:\n  - lint\n\nLint:\n  stage: lint\n  tags:\n    - bfs\n  script: \n    - cpplint --verbose=0 src/sbus.cpp\n    - cpplint --verbose=0 src/sbus.h\n  "
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\r\n\r\n## v8.1.4\r\n- Fixed a bug where trying to write SBUS packets with Teensy 3.1/3.2 would hang the processor by de-referencing a null pointer\r\n\r\n## v8.1.3\r\n- Updated core to v3.1.3\r\n\r\n## v8.1.2\r\n- Updated core to v3.1.2\r\n\r\n## v8.1.1\r\n- Updated core to support MMOD\r\n\r\n## v8.1.0\r\n- Added option for fast SBUS (200000 baud)\r\n\r\n## v8.0.1\r\n- Enabling ESP32 to use non-inverted SBUS\r\n\r\n## v8.0.0\r\n- Fixed bug in SbusRx timing where the last packet waited for the start of the next packet before returning true\r\n- Added option to specify a non-inverted signal for cases where that is a hardware option\r\n- Removed std::array dependency to facilitate use on Arduino AVR\r\n- Updated ESP32 implementation for greater consistency to other processors\r\n- Implemented data struct to ease reading and setting SBUS values without needing std::array\r\n\r\n## v7.0.0\r\n- Using std::array for passing around SBUS data and commands\r\n\r\n## v6.0.3\r\n- Cleaned up folder structure after merge\r\n- Pulling in mcu-support repo for CMake builds\r\n- Fixed pointer constness for SbusRx\r\n\r\n## v6.0.2\r\n- Removed the tools folder to fix Arduino linting\r\n\r\n## v6.0.1\r\n- Fixing library.properties version number\r\n\r\n## v6.0.0\r\n- Merging CMake and Arduino SBUS libraries. Version 6 is the next available version number common between the two.\r\n\r\n## v2.1.2\r\n- Added Embedded Template Library support for AVR boards.\r\n\r\n## v2.1.1\r\n- Added CH17 and 18 support for read and write\r\n- Added capability to write lost_frame or failsafe\r\n\r\n## v2.0.1\r\n- Added back in support for SBUS2 footers\r\n\r\n## v2.0.0\r\n- Updated to match our [SBUS](https://github.com/bolderflight/sbus) library for flight software\r\n- Updated license to MIT\r\n\r\n## v1.0.1\r\n- Updated license to GPLV3.\r\n\r\n## v1.0.0\r\n- Updated to Arduino 1.5 format and setting a baseline release here.\r\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "# v3.14 required for FetchContent_MakeAvailable\ncmake_minimum_required(VERSION 3.14)\nif (DEFINED MCU)\n  include(FetchContent)\n  FetchContent_Declare(\n    mcu_support\n    GIT_REPOSITORY https://github.com/bolderflight/mcu-support.git\n    GIT_TAG v1.1.0\n  )\n  FetchContent_MakeAvailable(mcu_support)\n  # Setting up the toolchain\n  set(CMAKE_TOOLCHAIN_FILE \"${mcu_support_SOURCE_DIR}/cmake/cortex.cmake\")\n  # Project information\n  project(Sbus\n    VERSION 8.1.4\n    DESCRIPTION \"SBUS encoder and decoder\"\n    LANGUAGES C CXX\n  )\n  # Grab the processor and set up definitions and compile options\n  include(${mcu_support_SOURCE_DIR}/cmake/config_mcu.cmake)\n  configMcu(${MCU} ${mcu_support_SOURCE_DIR})\n  # Fetch core\n  FetchContent_Declare(\n    core\n    GIT_REPOSITORY https://github.com/bolderflight/core.git\n    GIT_TAG v3.1.3\n  )\n  FetchContent_MakeAvailable(core)\n  # Add the library target\n  add_library(sbus\n    src/sbus.cpp\n    src/sbus.h\n  )\n  # Link libraries\n  target_link_libraries(sbus\n    PUBLIC\n      core \n  )\n  # Setup include directories \n  target_include_directories(sbus PUBLIC \n    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>\n    $<INSTALL_INTERFACE:include>\n  )\n  # Example and test if this project is built separately\n  if (PROJECT_NAME STREQUAL CMAKE_PROJECT_NAME)\n    # Add the example target\n    add_executable(sbus_example examples/cmake/sbus_example.cc)\n    # Add the includes\n    target_include_directories(sbus_example PUBLIC \n      $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>\n      $<INSTALL_INTERFACE:include>\n    )\n    # Link libraries to the example target\n    target_link_libraries(sbus_example\n      PRIVATE \n        sbus\n    )\n    # Add hex and upload targets\n    include(${mcu_support_SOURCE_DIR}/cmake/flash_mcu.cmake)\n    FlashMcu(sbus_example ${MCU} ${mcu_support_SOURCE_DIR})\n  endif()\nendif()\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\nPlease see our [Contributing Guide](https://github.com/bolderflight/contributing) for tips on how to effectively make contributions to our project.\n"
  },
  {
    "path": "LICENSE.md",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2022 Bolder Flight Systems Inc\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n"
  },
  {
    "path": "README.md",
    "content": "[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n![Bolder Flight Systems Logo](img/logo-words_75.png) &nbsp; &nbsp; ![Arduino Logo](img/arduino_logo_75.png)\n\n# Sbus\nThis library communicates with SBUS receivers and servos and is compatible with Arduino and CMake build systems.\n   * [License](LICENSE.md)\n   * [Changelog](CHANGELOG.md)\n   * [Contributing guide](CONTRIBUTING.md)\n\n# Description\nSBUS is a bus protocol for receivers to send commands to servos. Unlike PWM, SBUS uses a bus architecture where a single serial line can be connected with up to 16 servos with each receiving a unique command.\n\nThe SBUS protocol uses an inverted serial logic with a baud rate of 100000, 8 data bits, even parity, and 2 stop bits. The SBUS packet is 25 bytes long consisting of:\n   * Byte[0]: SBUS header, 0x0F\n   * Byte[1 -22]: 16 servo channels, 11 bits each\n   * Byte[23]\n      * Bit 0: channel 17 (0x01)\n      * Bit 1: channel 18 (0x02)\n      * Bit 2: frame lost (0x04)\n      * Bit 3: failsafe activated (0x08)\n   * Byte[24]: SBUS footer\n\nNote that lost frame is indicated when a frame is lost between the transmitter and receiver. Failsafe activation typically requires that many frames are lost in a row and indicates that the receiver has moved into failsafe mode. Packets are sent approximately every 10 ms or 20 ms, depending on the system configuration.\n\nA variation on SBUS called \"Fast SBUS\" has started to be used. This uses a baudrate of 200000 and a quicker update rate.\n\n**Note on CH17 and CH18:** Channel 17 and channel 18 are digital on/off channels. These are not universally available on all SBUS receivers and servos.\n\nFrSky receivers will output a range of 172 - 1811 with channels set to a range of -100% to +100%. Using extended limits of -150% to +150% outputs a range of 0 to 2047, which is the maximum range acheivable with 11 bits of data.\n\nBecause SBUS is a digital bus format, it is an excellent means of receiving pilot commands from a transmitter and an SBUS capable receiver. If SBUS servos are used in the aircraft, SBUS is also an excellent means of sending actuator commands - servo commands can often be sent with lower latency and, by only using a single pin to command up to 16 servos, additional microcontroller pins are freed for other uses.\n\n# Inverted Serial\nSBUS uses an inverted serial protocol, which is not commonly supported in Arduino. This library is able to use inverted serial for the following microcontrollers:\n   * Teensy 3.x\n   * Teensy 4.x\n   * Teensy LC\n   * STM32L496xx\n   * STM32L476xx\n   * STM32L433xx\n   * STM32L432xx\n   * ESP32\n\nFor all other microcontrollers, you **must** use a serial inverter. If you've modified this library to work with other microcontrollers, please submit a pull request.\n\n# Installation\n\n## Arduino\nSimply clone or download and extract the zipped library into your Arduino/libraries folder. The library is added as:\n\n```C++\n#include \"sbus.h\"\n```\n\nAn example is located in *examples/arduino/sbus_example/sbus_example.ino*. This library is tested with Teensy 3.x, 4.x, and LC devices and should work with other Arduino devices.\n\n## CMake\nCMake is used to build this library, which is exported as a library target called *sbus*. The header is added as:\n\n```C++\n#include \"sbus.h\"\n```\n\nThe library can be also be compiled stand-alone using the CMake idiom of creating a *build* directory and then, from within that directory issuing:\n\n```\ncmake .. -DMCU=MK66FX1M0\nmake\n```\n\nThis will build the library and example executable called *sbus_example*. The example executable source file is located at *examples/cmake/sbus_example.cc*. Notice that the *cmake* command includes a define specifying the microcontroller the code is being compiled for. This is required to correctly configure the code, CPU frequency, and compile/linker options. The available MCUs are:\n   * MK64FX512\n   * MK66FX1M0\n   * MKL26Z64\n   * IMXRT1062_T40\n   * IMXRT1062_T41\n   * IMXRT1062_MMOD\n\nThese are known to work with the same packages used in Teensy products. Also switching packages is known to work well, as long as it's only a package change.\n\nThe *sbus_example* target creates an executable for communicating with sbus receivers and servos. This target also has a *_hex* for creating the hex file and an *_upload* for using the [Teensy CLI Uploader](https://www.pjrc.com/teensy/loader_cli.html) to flash the Teensy. Instructions for setting up your build environment can be found in our [build-tools repo](https://github.com/bolderflight/build-tools). \n\n# Namespace\nThis library is within the namespace *bfs*.\n\n# SbusData\nThis struct defines SBUS data that can be read and returned by the *SbusRx* object or set and sent by the *SbusTx* object.\n\n## Data Members\n\n**bool lost_frame** Whether a frame has been lost.\n\n**bool failsafe** Whether the receiver has entered failsafe mode or to command servos to enter failsafe mode.\n\n**bool ch17, ch18** State of channel 17 and channel 18.\n\n**static constexpr int8_t NUM_CH = 16** The number of SBUS channels.\n\n**int16_t ch[NUM_CH]** An array of SBUS channel data.\n\n# SbusRx\nThis class is used for receiving SBUS data from an SBUS capable receiver.\n\n**SbusRx(HardwareSerial &ast;bus)** Creates an *SbusRx* object. A pointer to the *Serial* object corresponding to the serial port used is passed. The RX pin of the serial port will receive SBUS packets.\n\n```C++\nbfs::SbusRx sbus(&Serial1);\n```\n\n**SbusRx(HardwareSerial &ast;bus, const bool inv)** Creates an *SbusRx* object. A pointer to the *Serial* object corresponding to the serial port used is passed along with a second parameter, *inv*, which sets whether inverted serial is used. If *inv* is true, the signal is the standard inverted SBUS, otherwise it is non-inverted SBUS.\n\n```C++\nbfs::SbusRx sbus(&Serial1, false);\n```\n\n**SbusRx(HardwareSerial &ast;bus, const bool inv, const bool fast)** Same as the constructor above, but enables selecting the fast SBUS baudrate (200000) if *fast* is true.\n\n**(ESP32 ONLY) SbusRx(HardwareSerial &ast;bus, const int8_t rxpin, const int8_t txpin, const bool inv)** Creates an *SbusRx* object. A pointer to the *Serial* object corresponding to the serial port used is passed along with the RX pin number (*rxpin*), TX pin number (*txpin*), and whether inverted serial is used (*inv*). If *inv* is true, the signal is the standard inverted SBUS, otherwise it is non-inverted SBUS.\n\n**(ESP32 ONLY) SbusRx(HardwareSerial &ast;bus, const int8_t rxpin, const int8_t txpin, const bool inv, const bool fast)** Same as the constructor above, but enables selecting the fast SBUS baudrate (200000) if *fast* is true.\n\n**void Begin()** Initializes SBUS communication.\n\n```C++\nsbus.Begin();\n```\n\n**bool Read()** Parses SBUS packets, returns true on successfully receiving an SBUS packet.\n\n```C++\nif (sbus.Read()) {\n  // Do something with the received data\n}\n```\n\n**SbusData data()** Returns the *SbusData* structure, populated with data from the last received packet.\n\n```C++\nif (sbus.Read()) {\n  bfs::SbusData data = sbus.data();\n}\n```\n\n# SbusTx\nThis class is used for transmitting SBUS data to SBUS capable servos.\n\n**SbusTx(HardwareSerial &ast;bus)** Creates an *SbusTx* object. A pointer to the *Serial* object corresponding to the serial port used is passed. The TX pin of the serial port will receive SBUS packets.\n\n```C++\nbfs::SbusTx sbus(&Serial1);\n```\n\n**SbusTx(HardwareSerial &ast;bus, const bool inv)** Creates an *SbusTx* object. A pointer to the *Serial* object corresponding to the serial port used is passed along with a second parameter, *inv*, which sets whether inverted serial is used. If *inv* is true, the signal is the standard inverted SBUS, otherwise it is non-inverted SBUS.\n\n```C++\nbfs::SbusTx sbus(&Serial1, false);\n```\n\n**(ESP32 ONLY) SbusTx(HardwareSerial &ast;bus, const int8_t rxpin, const int8_t txpin, const bool inv)** Creates an *SbusTx* object. A pointer to the *Serial* object corresponding to the serial port used is passed along with the RX pin number (*rxpin*), TX pin number (*txpin*), and whether inverted serial is used (*inv*). If *inv* is true, the signal is the standard inverted SBUS, otherwise it is non-inverted SBUS.\n\n**void Begin()** Initializes SBUS communication.\n\n```C++\nsbus.Begin();\n```\n\n**void Write()** Writes an SBUS packet. The packet is written immediately, you should regulate timing of sending packets to servos to maintain a frequency of approximately 100 Hz or 50 Hz, depending on the setup of the SBUS system.\n\n```C++\nsbus.Write();\n```\n\n**void data(const SbusData &data)** Sets the SBUS data, which will be transmitted on the next *Write* method.\n\n```C++\nbfs::SbusData data;\ndata.ch[0] = 900;\nsbus.data(data);\n```\n\n**SbusData data()** Returns the SBUS data buffered in the *SbusTx* object, which will be transmitted on the next *Write* method.\n\n```C++\nbfs::SbusData data = sbus.data();\n```\n"
  },
  {
    "path": "examples/arduino/sbus_example/sbus_example.ino",
    "content": "/*\n* Brian R Taylor\n* brian.taylor@bolderflight.com\n* \n* Copyright (c) 2022 Bolder Flight Systems Inc\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of this software and associated documentation files (the “Software”), to\n* deal in the Software without restriction, including without limitation the\n* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n* sell copies of the Software, and to permit persons to whom the Software is\n* furnished to do so, subject to the following conditions:\n*\n* The above copyright notice and this permission notice shall be included in\n* all copies or substantial portions of the Software.\n*\n* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n* IN THE SOFTWARE.\n*/\n\n#include \"sbus.h\"\n\n/* SBUS object, reading SBUS */\nbfs::SbusRx sbus_rx(&Serial2);\n/* SBUS object, writing SBUS */\nbfs::SbusTx sbus_tx(&Serial2);\n/* SBUS data */\nbfs::SbusData data;\n\nvoid setup() {\n  /* Serial to display data */\n  Serial.begin(115200);\n  while (!Serial) {}\n  /* Begin the SBUS communication */\n  sbus_rx.Begin();\n  sbus_tx.Begin();\n}\n\nvoid loop () {\n  if (sbus_rx.Read()) {\n    /* Grab the received data */\n    data = sbus_rx.data();\n    /* Display the received data */\n    for (int8_t i = 0; i < data.NUM_CH; i++) {\n      Serial.print(data.ch[i]);\n      Serial.print(\"\\t\");\n    }\n    /* Display lost frames and failsafe data */\n    Serial.print(data.lost_frame);\n    Serial.print(\"\\t\");\n    Serial.println(data.failsafe);\n    /* Set the SBUS TX data to the received data */\n    sbus_tx.data(data);\n    /* Write the data to the servos */\n    sbus_tx.Write();\n  }\n}\n"
  },
  {
    "path": "examples/cmake/sbus_example.cc",
    "content": "/*\n* Brian R Taylor\n* brian.taylor@bolderflight.com\n* \n* Copyright (c) 2022 Bolder Flight Systems Inc\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of this software and associated documentation files (the “Software”), to\n* deal in the Software without restriction, including without limitation the\n* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n* sell copies of the Software, and to permit persons to whom the Software is\n* furnished to do so, subject to the following conditions:\n*\n* The above copyright notice and this permission notice shall be included in\n* all copies or substantial portions of the Software.\n*\n* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n* IN THE SOFTWARE.\n*/\n\n#include \"sbus.h\"\n\n/* SBUS object, reading SBUS */\nbfs::SbusRx sbus_rx(&Serial2);\n/* SBUS object, writing SBUS */\nbfs::SbusTx sbus_tx(&Serial2);\n/* SBUS data */\nbfs::SbusData data;\n\nint main() {\n  /* Serial to display data */\n  Serial.begin(115200);\n  while (!Serial) {}\n  /* Begin the SBUS communication */\n  sbus_rx.Begin();\n  sbus_tx.Begin();\n  while (1) {\n    if (sbus_rx.Read()) {\n      /* Grab the received data */\n      data = sbus_rx.data();\n      /* Display the received data */\n      for (int8_t i = 0; i < data.NUM_CH; i++) {\n        Serial.print(data.ch[i]);\n        Serial.print(\"\\t\");\n      }\n      /* Display lost frames and failsafe data */\n      Serial.print(data.lost_frame);\n      Serial.print(\"\\t\");\n      Serial.println(data.failsafe);\n      /* Set the SBUS TX data to the received data */\n      sbus_tx.data(data);\n      /* Write the data to the servos */\n      sbus_tx.Write();\n    }\n  }\n}\n\n\n"
  },
  {
    "path": "keywords.txt",
    "content": "SbusRx\tKEYWORD1\nSbusTx\tKEYWORD1\nBegin\tKEYWORD2\nRead\tKEYWORD2\ndata\tKEYWORD2\nSbusData\tKEYWORD1\nlost_frame\tKEYWORD2\nfailsafe\tKEYWORD2\nch17\tKEYWORD2\nch18\tKEYWORD2\nNUM_CH\tKEYWORD2\nch\tKEYWORD2"
  },
  {
    "path": "library.properties",
    "content": "name=Bolder Flight Systems SBUS\nversion=8.1.4\nauthor=Brian Taylor <brian.taylor@bolderflight.com>\nmaintainer=Brian Taylor <brian.taylor@bolderflight.com>\nsentence=Library for communicating with SBUS receivers and servos.\nparagraph=This library communicates with SBUS receivers and servos, and works with Teensy 3.x, 4.x, and LC devices, the STM32L4, and ESP32 or any device using a hardware serial inverter.\ncategory=Device Control\nurl=https://github.com/bolderflight/sbus\narchitectures=*\nincludes=sbus.h\n"
  },
  {
    "path": "src/sbus.cpp",
    "content": "/*\n* Brian R Taylor\n* brian.taylor@bolderflight.com\n*\n* Copyright (c) 2022 Bolder Flight Systems Inc\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of this software and associated documentation files (the “Software”), to\n* deal in the Software without restriction, including without limitation the\n* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n* sell copies of the Software, and to permit persons to whom the Software is\n* furnished to do so, subject to the following conditions:\n*\n* The above copyright notice and this permission notice shall be included in\n* all copies or substantial portions of the Software.\n*\n* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n* IN THE SOFTWARE.\n*/\n\n#include \"sbus.h\"  // NOLINT\n#if defined(ARDUINO)\n#include <Arduino.h>\n#else\n#include <cstddef>\n#include <cstdint>\n#include \"core/core.h\"\n#endif\n\nnamespace bfs {\n\nvoid SbusRx::Begin() {\n  if (fast_) {\n    baud_ = 200000;\n  } else {\n    baud_ = 100000;\n  }\n  /* Start the bus */\n  /* Teensy 3.0 || Teensy 3.1/3.2 */\n  #if defined(__MK20DX128__) || defined(__MK20DX256__)\n  if (inv_) {\n    uart_->begin(baud_, SERIAL_8E1_RXINV_TXINV);\n  } else {\n    uart_->begin(baud_, SERIAL_8E1);\n  }\n  /*\n  * Teensy 3.5 || Teensy 3.6 ||\n  * Teensy LC  || Teensy 4.0/4.1 ||\n  * Teensy 4.0 Beta\n  */\n  #elif defined(__MK64FX512__) || defined(__MK66FX1M0__) || \\\n        defined(__MKL26Z64__)  || defined(__IMXRT1062__) || \\\n        defined(__IMXRT1052__)\n  if (inv_){\n    uart_->begin(baud_, SERIAL_8E2_RXINV_TXINV);\n  } else {\n    uart_->begin(baud_, SERIAL_8E2);\n  }\n  /* STM32L4 */\n  #elif defined(STM32L496xx) || defined(STM32L476xx) || \\\n        defined(STM32L433xx) || defined(STM32L432xx)\n  if (inv_) {\n    uart_->begin(baud_, SERIAL_8E2 | 0xC000ul);\n  } else {\n    uart_->begin(baud_, SERIAL_8E2);\n  }\n  /* ESP32 */\n  #elif defined(ESP32)\n  uart_->begin(baud_, SERIAL_8E2, rxpin_, txpin_, inv_);\n  /* Everything else, with a hardware inverter */\n  #else\n  uart_->begin(baud_, SERIAL_8E2);\n  #endif\n  /* flush the bus */\n  uart_->flush();\n}\n\nbool SbusRx::Read() {\n  /* Read through all available packets to get the newest */\n  new_data_ = false;\n  do {\n    if (Parse()) {\n      new_data_ = true;\n    }\n  } while (uart_->available());\n  return new_data_;\n}\n\nbool SbusRx::Parse() {\n  /* Parse messages */\n  while (uart_->available()) {\n    cur_byte_ = uart_->read();\n    if (state_ == 0) {\n      if ((cur_byte_ == HEADER_) && ((prev_byte_ == FOOTER_) ||\n         ((prev_byte_ & 0x0F) == FOOTER2_))) {\n        buf_[state_++] = cur_byte_;\n      } else {\n        state_ = 0;\n      }\n    } else if (state_ < PAYLOAD_LEN_ + HEADER_LEN_) {\n        buf_[state_++] = cur_byte_;\n    } else if (state_ < PAYLOAD_LEN_ + HEADER_LEN_ + FOOTER_LEN_) {\n      state_ = 0;\n      prev_byte_ = cur_byte_;\n      if ((cur_byte_ == FOOTER_) || ((cur_byte_ & 0x0F) == FOOTER2_)) {\n        /* Grab the channel data */\n        data_.ch[0]  = static_cast<int16_t>(buf_[1] |\n                                            ((buf_[2] << 8) & 0x07FF));\n        data_.ch[1]  = static_cast<int16_t>((buf_[2] >> 3) |\n                                            ((buf_[3] << 5) & 0x07FF));\n        data_.ch[2]  = static_cast<int16_t>((buf_[3] >> 6) |\n                                            (buf_[4] << 2) |\n                                            ((buf_[5] << 10) & 0x07FF));\n        data_.ch[3]  = static_cast<int16_t>((buf_[5] >> 1) |\n                                            ((buf_[6] << 7) & 0x07FF));\n        data_.ch[4]  = static_cast<int16_t>((buf_[6] >> 4) |\n                                            ((buf_[7] << 4) & 0x07FF));\n        data_.ch[5]  = static_cast<int16_t>((buf_[7] >> 7) |\n                                            (buf_[8] << 1) |\n                                            ((buf_[9] << 9) & 0x07FF));\n        data_.ch[6]  = static_cast<int16_t>((buf_[9] >> 2) |\n                                            ((buf_[10] << 6) & 0x07FF));\n        data_.ch[7]  = static_cast<int16_t>((buf_[10] >> 5) |\n                                            ((buf_[11] << 3) & 0x07FF));\n        data_.ch[8]  = static_cast<int16_t>(buf_[12] |\n                                            ((buf_[13] << 8) & 0x07FF));\n        data_.ch[9]  = static_cast<int16_t>((buf_[13] >> 3) |\n                                            ((buf_[14] << 5) & 0x07FF));\n        data_.ch[10] = static_cast<int16_t>((buf_[14] >> 6) |\n                                            (buf_[15] << 2) |\n                                            ((buf_[16] << 10) & 0x07FF));\n        data_.ch[11] = static_cast<int16_t>((buf_[16] >> 1) |\n                                            ((buf_[17] << 7) & 0x07FF));\n        data_.ch[12] = static_cast<int16_t>((buf_[17] >> 4) |\n                                            ((buf_[18] << 4) & 0x07FF));\n        data_.ch[13] = static_cast<int16_t>((buf_[18] >> 7) |\n                                            (buf_[19] << 1) |\n                                            ((buf_[20] << 9) & 0x07FF));\n        data_.ch[14] = static_cast<int16_t>((buf_[20] >> 2) |\n                                            ((buf_[21] << 6) & 0x07FF));\n        data_.ch[15] = static_cast<int16_t>((buf_[21] >> 5) |\n                                            ((buf_[22] << 3) & 0x07FF));\n        /* CH 17 */\n        data_.ch17 = buf_[23] & CH17_MASK_;\n        /* CH 18 */\n        data_.ch18 = buf_[23] & CH18_MASK_;\n        /* Grab the lost frame */\n        data_.lost_frame = buf_[23] & LOST_FRAME_MASK_;\n        /* Grab the failsafe */\n        data_.failsafe = buf_[23] & FAILSAFE_MASK_;\n        return true;\n      } else {\n        return false;\n      }\n    } else {\n      state_ = 0;\n    }\n    prev_byte_ = cur_byte_;\n  }\n  return false;\n}\n\n/* Needed for emulating two stop bytes on Teensy 3.0 and 3.1/3.2 */\n#if defined(__MK20DX128__) || defined(__MK20DX256__)\nnamespace {\n  IntervalTimer serial_timer;\n  HardwareSerial *sbus_bus;\n  uint8_t sbus_packet[25];\n  volatile int send_index = 0;\n  void SendByte() {\n    if (send_index < 25) {\n      sbus_bus->write(sbus_packet[send_index]);\n      send_index++;\n    } else {\n      serial_timer.end();\n      send_index = 0;\n    }\n  }\n}  // namespace\n#endif\n\nvoid SbusTx::Begin() {\n  if (fast_) {\n    baud_ = 200000;\n  } else {\n    baud_ = 100000;\n  }\n  /* Start the bus */\n  /* Teensy 3.0 || Teensy 3.1/3.2 */\n  #if defined(__MK20DX128__) || defined(__MK20DX256__)\n  sbus_bus = uart_;\n  if (inv_) {\n    uart_->begin(baud_, SERIAL_8E1_RXINV_TXINV);\n  } else {\n    uart_->begin(baud_, SERIAL_8E1);\n  }\n  /*\n  * Teensy 3.5 || Teensy 3.6 ||\n  * Teensy LC  || Teensy 4.0/4.1 ||\n  * Teensy 4.0 Beta\n  */\n  #elif defined(__MK64FX512__) || defined(__MK66FX1M0__) || \\\n        defined(__MKL26Z64__)  || defined(__IMXRT1062__) || \\\n        defined(__IMXRT1052__)\n  if (inv_){\n    uart_->begin(baud_, SERIAL_8E2_RXINV_TXINV);\n  } else {\n    uart_->begin(baud_, SERIAL_8E2);\n  }\n  /* STM32L4 */\n  #elif defined(STM32L496xx) || defined(STM32L476xx) || \\\n        defined(STM32L433xx) || defined(STM32L432xx)\n  if (inv_) {\n    uart_->begin(baud_, SERIAL_8E2 | 0xC000ul);\n  } else {\n    uart_->begin(baud_, SERIAL_8E2);\n  }\n  /* ESP32 */\n  #elif defined(ESP32)\n  uart_->begin(baud_, SERIAL_8E2, rxpin_, txpin_, inv_);\n  /* Everything else, with a hardware inverter */\n  #else\n  uart_->begin(baud_, SERIAL_8E2);\n  #endif\n}\n\nvoid SbusTx::Write() {\n  /* Assemble packet */\n  buf_[0] = HEADER_;\n  buf_[1] = static_cast<uint8_t>((data_.ch[0] & 0x07FF));\n  buf_[2] = static_cast<uint8_t>((data_.ch[0] & 0x07FF) >> 8 |\n            (data_.ch[1] & 0x07FF) << 3);\n  buf_[3] = static_cast<uint8_t>((data_.ch[1] & 0x07FF) >> 5 |\n            (data_.ch[2] & 0x07FF) << 6);\n  buf_[4] = static_cast<uint8_t>((data_.ch[2] & 0x07FF) >> 2);\n  buf_[5] = static_cast<uint8_t>((data_.ch[2] & 0x07FF) >> 10 |\n            (data_.ch[3] & 0x07FF) << 1);\n  buf_[6] = static_cast<uint8_t>((data_.ch[3] & 0x07FF) >> 7 |\n            (data_.ch[4] & 0x07FF) << 4);\n  buf_[7] = static_cast<uint8_t>((data_.ch[4] & 0x07FF) >> 4 |\n            (data_.ch[5] & 0x07FF) << 7);\n  buf_[8] = static_cast<uint8_t>((data_.ch[5] & 0x07FF) >> 1);\n  buf_[9] = static_cast<uint8_t>((data_.ch[5] & 0x07FF) >> 9  |\n            (data_.ch[6] & 0x07FF) << 2);\n  buf_[10] = static_cast<uint8_t>((data_.ch[6] & 0x07FF) >> 6  |\n             (data_.ch[7] & 0x07FF) << 5);\n  buf_[11] = static_cast<uint8_t>((data_.ch[7] & 0x07FF) >> 3);\n  buf_[12] = static_cast<uint8_t>((data_.ch[8] & 0x07FF));\n  buf_[13] = static_cast<uint8_t>((data_.ch[8] & 0x07FF) >> 8 |\n             (data_.ch[9]  & 0x07FF) << 3);\n  buf_[14] = static_cast<uint8_t>((data_.ch[9] & 0x07FF) >> 5 |\n             (data_.ch[10] & 0x07FF) << 6);\n  buf_[15] = static_cast<uint8_t>((data_.ch[10] & 0x07FF) >> 2);\n  buf_[16] = static_cast<uint8_t>((data_.ch[10] & 0x07FF) >> 10 |\n             (data_.ch[11] & 0x07FF) << 1);\n  buf_[17] = static_cast<uint8_t>((data_.ch[11] & 0x07FF) >> 7  |\n             (data_.ch[12] & 0x07FF) << 4);\n  buf_[18] = static_cast<uint8_t>((data_.ch[12] & 0x07FF) >> 4  |\n             (data_.ch[13] & 0x07FF) << 7);\n  buf_[19] = static_cast<uint8_t>((data_.ch[13] & 0x07FF) >> 1);\n  buf_[20] = static_cast<uint8_t>((data_.ch[13] & 0x07FF) >> 9  |\n             (data_.ch[14] & 0x07FF) << 2);\n  buf_[21] = static_cast<uint8_t>((data_.ch[14] & 0x07FF) >> 6  |\n             (data_.ch[15] & 0x07FF) << 5);\n  buf_[22] = static_cast<uint8_t>((data_.ch[15] & 0x07FF) >> 3);\n  buf_[23] = 0x00 | (data_.ch17 * CH17_MASK_) | (data_.ch18 * CH18_MASK_) |\n             (data_.failsafe * FAILSAFE_MASK_) |\n             (data_.lost_frame * LOST_FRAME_MASK_);\n  buf_[24] = FOOTER_;\n  /* Send packet to servos */\n  #if defined(__MK20DX128__) || defined(__MK20DX256__)\n  /*\n  * Use ISR to send byte at a time,\n  * 130 us between bytes to emulate 2 stop bits\n  */\n  __disable_irq();\n  memcpy(sbus_packet, buf_, sizeof(buf_));\n  __enable_irq();\n  serial_timer.priority(255);\n  serial_timer.begin(SendByte, 130);\n  #else\n  uart_->write(buf_, sizeof(buf_));\n  #endif\n}\n\n}  // namespace bfs\n"
  },
  {
    "path": "src/sbus.h",
    "content": "/*\n* Brian R Taylor\n* brian.taylor@bolderflight.com\n*\n* Copyright (c) 2022 Bolder Flight Systems Inc\n*\n* Permission is hereby granted, free of charge, to any person obtaining a copy\n* of this software and associated documentation files (the “Software”), to\n* deal in the Software without restriction, including without limitation the\n* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n* sell copies of the Software, and to permit persons to whom the Software is\n* furnished to do so, subject to the following conditions:\n*\n* The above copyright notice and this permission notice shall be included in\n* all copies or substantial portions of the Software.\n*\n* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n* IN THE SOFTWARE.\n*/\n\n#ifndef SRC_SBUS_H_\n#define SRC_SBUS_H_\n\n#if defined(ARDUINO)\n#include <Arduino.h>\n#else\n#include <cstddef>\n#include <cstdint>\n#include \"core/core.h\"\n#endif\n\nnamespace bfs {\n\nstruct SbusData {\n  bool lost_frame;\n  bool failsafe;\n  bool ch17, ch18;\n  static constexpr int8_t NUM_CH = 16;\n  int16_t ch[NUM_CH];\n};\n\nclass SbusRx {\n public:\n  #if defined(ESP32)\n  SbusRx(HardwareSerial *bus, const int8_t rxpin, const int8_t txpin,\n         const bool inv) : uart_(bus), inv_(inv), rxpin_(rxpin), txpin_(txpin)\n         {}\n  SbusRx(HardwareSerial *bus, const int8_t rxpin, const int8_t txpin,\n         const bool inv, const bool fast) : uart_(bus), inv_(inv), fast_(fast),\n                                            rxpin_(rxpin), txpin_(txpin) {}\n  #else\n  explicit SbusRx(HardwareSerial *bus) : uart_(bus) {}\n  SbusRx(HardwareSerial *bus, const bool inv) : uart_(bus), inv_(inv) {}\n  SbusRx(HardwareSerial *bus, const bool inv, const bool fast) : uart_(bus),\n                                                                 inv_(inv),\n                                                                 fast_(fast) {}\n  #endif\n  void Begin();\n  bool Read();\n  inline SbusData data() const {return data_;}\n\n private:\n  /* Communication */\n  HardwareSerial *uart_;\n  bool inv_ = true;\n  bool fast_ = false;\n  #if defined(ESP32)\n  int8_t rxpin_, txpin_;\n  #endif\n  int32_t baud_ = 100000;\n  /* Message len */\n  static constexpr int8_t PAYLOAD_LEN_ = 23;\n  static constexpr int8_t HEADER_LEN_ = 1;\n  static constexpr int8_t FOOTER_LEN_ = 1;\n  /* SBUS message defs */\n  static constexpr int8_t NUM_SBUS_CH_ = 16;\n  static constexpr uint8_t HEADER_ = 0x0F;\n  static constexpr uint8_t FOOTER_ = 0x00;\n  static constexpr uint8_t FOOTER2_ = 0x04;\n  static constexpr uint8_t CH17_MASK_ = 0x01;\n  static constexpr uint8_t CH18_MASK_ = 0x02;\n  static constexpr uint8_t LOST_FRAME_MASK_ = 0x04;\n  static constexpr uint8_t FAILSAFE_MASK_ = 0x08;\n  /* Parsing state tracking */\n  int8_t state_ = 0;\n  uint8_t prev_byte_ = FOOTER_;\n  uint8_t cur_byte_;\n  /* Buffer for storing messages */\n  uint8_t buf_[25];\n  /* Data */\n  bool new_data_;\n  SbusData data_;\n  bool Parse();\n};\n\nclass SbusTx {\n public:\n  #if defined(ESP32)\n  SbusTx(HardwareSerial *bus, const int8_t rxpin, const int8_t txpin,\n         const bool inv) : uart_(bus), inv_(inv), rxpin_(rxpin), txpin_(txpin)\n         {}\n  SbusTx(HardwareSerial *bus, const int8_t rxpin, const int8_t txpin,\n         const bool inv, const bool fast) : uart_(bus), inv_(inv), fast_(fast),\n                                            rxpin_(rxpin), txpin_(txpin) {}\n  #else\n  explicit SbusTx(HardwareSerial *bus) : uart_(bus) {}\n  SbusTx(HardwareSerial *bus, const bool inv) : uart_(bus), inv_(inv) {}\n  SbusTx(HardwareSerial *bus, const bool inv, const bool fast) : uart_(bus),\n                                                                 inv_(inv),\n                                                                 fast_(fast) {}\n  #endif\n  void Begin();\n  void Write();\n  inline void data(const SbusData &data) {data_ = data;}\n  inline SbusData data() const {return data_;}\n\n private:\n  /* Communication */\n  HardwareSerial *uart_;\n  bool inv_ = true;\n  bool fast_ = false;\n  #if defined(ESP32)\n  int8_t rxpin_, txpin_;\n  #endif\n  int32_t baud_ = 100000;\n  /* Message len */\n  static constexpr int8_t BUF_LEN_ = 25;\n  /* SBUS message defs */\n  static constexpr int8_t NUM_SBUS_CH_ = 16;\n  static constexpr uint8_t HEADER_ = 0x0F;\n  static constexpr uint8_t FOOTER_ = 0x00;\n  static constexpr uint8_t FOOTER2_ = 0x04;\n  static constexpr uint8_t CH17_MASK_ = 0x01;\n  static constexpr uint8_t CH18_MASK_ = 0x02;\n  static constexpr uint8_t LOST_FRAME_MASK_ = 0x04;\n  static constexpr uint8_t FAILSAFE_MASK_ = 0x08;\n  /* Data */\n  uint8_t buf_[BUF_LEN_];\n  SbusData data_;\n};\n\n}  // namespace bfs\n\n#endif  // SRC_SBUS_H_\n"
  }
]