├── .clang-format ├── .devcontainer └── devcontainer.json ├── .gitattributes ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ ├── config.yml │ ├── devboard-request.yml │ └── feature-request.yml └── workflows │ ├── codeql.yml │ └── quality_control.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── LICENSE.md ├── README.md ├── examples ├── flight_modes │ └── flight_modes.ino ├── link_stats │ └── link_stats.ino ├── platformio │ └── main.cpp ├── rc_channels │ └── rc_channels.ino └── telemetry │ └── telemetry.ino ├── keywords.txt ├── library.json ├── library.properties └── src ├── CFA_Config.hpp ├── CRSFforArduino.cpp ├── CRSFforArduino.hpp ├── SerialReceiver ├── CRC │ ├── CRC.cpp │ └── CRC.hpp ├── CRSF │ ├── CRSF.cpp │ ├── CRSF.hpp │ └── CRSFProtocol.hpp ├── SerialBuffer │ ├── SerialBuffer.cpp │ └── SerialBuffer.hpp ├── SerialReceiver.cpp ├── SerialReceiver.hpp └── Telemetry │ ├── Telemetry.cpp │ └── Telemetry.hpp ├── build ├── scripts │ ├── build.py │ └── platformio.ini └── targets │ ├── common.ini │ ├── quality_control.ini │ ├── unified_esp32.ini │ ├── unified_rp2040.ini │ ├── unified_samd21.ini │ ├── unified_samd51.ini │ ├── unified_stm32.ini │ ├── unified_teensy3x.ini │ └── unified_teensy4x.ini └── hal └── CompatibilityTable ├── CompatibilityTable.cpp └── CompatibilityTable.hpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: GNU 4 | AccessModifierOffset: -2 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveMacros: true 7 | AlignConsecutiveAssignments: false 8 | AlignConsecutiveBitFields: true 9 | AlignConsecutiveDeclarations: false 10 | AlignEscapedNewlines: Right 11 | AlignOperands: Align 12 | AlignTrailingComments: true 13 | AllowAllArgumentsOnNextLine: true 14 | AllowAllConstructorInitializersOnNextLine: true 15 | AllowAllParametersOfDeclarationOnNextLine: true 16 | AllowShortEnumsOnASingleLine: false 17 | AllowShortBlocksOnASingleLine: Never 18 | AllowShortCaseLabelsOnASingleLine: false 19 | AllowShortFunctionsOnASingleLine: false 20 | AllowShortLambdasOnASingleLine: Empty 21 | AllowShortIfStatementsOnASingleLine: Never 22 | AllowShortLoopsOnASingleLine: false 23 | AlwaysBreakAfterDefinitionReturnType: None 24 | AlwaysBreakAfterReturnType: None 25 | AlwaysBreakBeforeMultilineStrings: false 26 | AlwaysBreakTemplateDeclarations: MultiLine 27 | BinPackArguments: true 28 | BinPackParameters: true 29 | BraceWrapping: 30 | AfterCaseLabel: true 31 | AfterClass: true 32 | AfterControlStatement: Always 33 | AfterEnum: true 34 | AfterFunction: true 35 | AfterNamespace: true 36 | AfterObjCDeclaration: false 37 | AfterStruct: true 38 | AfterUnion: true 39 | AfterExternBlock: true 40 | BeforeCatch: true 41 | BeforeElse: true 42 | BeforeLambdaBody: false 43 | BeforeWhile: false 44 | IndentBraces: false 45 | SplitEmptyFunction: false 46 | SplitEmptyRecord: false 47 | SplitEmptyNamespace: false 48 | BreakBeforeBinaryOperators: None 49 | BreakBeforeBraces: Custom 50 | BreakBeforeInheritanceComma: false 51 | BreakInheritanceList: AfterColon 52 | BreakBeforeTernaryOperators: true 53 | # BreakConstructorInitializersBeforeComma: false 54 | BreakConstructorInitializers: AfterColon 55 | BreakAfterJavaFieldAnnotations: false 56 | BreakStringLiterals: true 57 | ColumnLimit: 0 58 | CommentPragmas: '^ IWYU pragma:' 59 | CompactNamespaces: false 60 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 61 | ConstructorInitializerIndentWidth: 4 62 | ContinuationIndentWidth: 4 63 | Cpp11BracedListStyle: true 64 | DeriveLineEnding: true 65 | DerivePointerAlignment: false 66 | DisableFormat: false 67 | ExperimentalAutoDetectBinPacking: false 68 | FixNamespaceComments: true 69 | ForEachMacros: 70 | - foreach 71 | - Q_FOREACH 72 | - BOOST_FOREACH 73 | IncludeBlocks: Preserve 74 | IncludeCategories: 75 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 76 | Priority: 2 77 | SortPriority: 0 78 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 79 | Priority: 3 80 | SortPriority: 0 81 | - Regex: '.*' 82 | Priority: 1 83 | SortPriority: 0 84 | IncludeIsMainRegex: '(Test)?$' 85 | IncludeIsMainSourceRegex: '' 86 | IndentCaseLabels: true 87 | IndentCaseBlocks: true 88 | IndentGotoLabels: true 89 | IndentPPDirectives: None 90 | IndentExternBlock: AfterExternBlock 91 | IndentWidth: 4 92 | IndentWrappedFunctionNames: false 93 | InsertTrailingCommas: None 94 | JavaScriptQuotes: Leave 95 | JavaScriptWrapImports: true 96 | KeepEmptyLinesAtTheStartOfBlocks: true 97 | MacroBlockBegin: '' 98 | MacroBlockEnd: '' 99 | MaxEmptyLinesToKeep: 1 100 | NamespaceIndentation: All 101 | ObjCBinPackProtocolList: Auto 102 | ObjCBlockIndentWidth: 2 103 | ObjCBreakBeforeNestedBlockParam: true 104 | ObjCSpaceAfterProperty: false 105 | ObjCSpaceBeforeProtocolList: true 106 | PenaltyBreakAssignment: 2 107 | PenaltyBreakBeforeFirstCallParameter: 19 108 | PenaltyBreakComment: 300 109 | PenaltyBreakFirstLessLess: 120 110 | PenaltyBreakString: 1000 111 | PenaltyBreakTemplateDeclaration: 10 112 | PenaltyExcessCharacter: 1000000 113 | PenaltyReturnTypeOnItsOwnLine: 60 114 | PointerAlignment: Right 115 | ReflowComments: false 116 | SortIncludes: true 117 | SortUsingDeclarations: true 118 | SpaceAfterCStyleCast: false 119 | SpaceAfterLogicalNot: false 120 | SpaceAfterTemplateKeyword: true 121 | SpaceBeforeAssignmentOperators: true 122 | SpaceBeforeCpp11BracedList: false 123 | SpaceBeforeCtorInitializerColon: true 124 | SpaceBeforeInheritanceColon: true 125 | SpaceBeforeParens: ControlStatements 126 | SpaceBeforeRangeBasedForLoopColon: true 127 | SpaceInEmptyBlock: false 128 | SpaceInEmptyParentheses: false 129 | SpacesBeforeTrailingComments: 1 130 | SpacesInAngles: false 131 | SpacesInConditionalStatement: false 132 | SpacesInContainerLiterals: false 133 | SpacesInCStyleCastParentheses: false 134 | SpacesInParentheses: false 135 | SpacesInSquareBrackets: false 136 | SpaceBeforeSquareBrackets: false 137 | BitFieldColonSpacing: Both 138 | Standard: Latest 139 | StatementMacros: 140 | - Q_UNUSED 141 | - QT_REQUIRE_VERSION 142 | TabWidth: 8 143 | UseCRLF: false 144 | UseTab: Never 145 | WhitespaceSensitiveMacros: 146 | - STRINGIZE 147 | - PP_STRINGIZE 148 | - BOOST_PP_STRINGIZE 149 | ... 150 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CRSFforArduino development container", 3 | "image": "mcr.microsoft.com/devcontainers/python:3.11", 4 | "postCreateCommand": "pip install platformio", 5 | "postStartCommand": "sudo groupadd -g 986 uucp || true && sudo usermod -aG uucp vscode", 6 | "customizations": { 7 | "vscode": { 8 | "extensions": [ 9 | "donjayamanne.githistory", 10 | "eamodio.gitlens", 11 | "github.vscode-github-actions", 12 | "GitHub.vscode-pull-request-github", 13 | "ms-vscode.cpptools", 14 | "ms-vscode.cpptools-extension-pack", 15 | "ms-vscode.cpptools-themes", 16 | "platformio.platformio-ide", 17 | "streetsidesoftware.code-spell-checker", 18 | "streetsidesoftware.code-spell-checker-australian-english", 19 | "vivaxy.vscode-conventional-commits", 20 | "xaver.clang-format" 21 | ] 22 | } 23 | }, 24 | "runArgs": [ 25 | "--device=/dev/ttyACM0", 26 | "--group-add=986" 27 | ], 28 | "mounts": [ 29 | "source=/dev/bus/usb,target=/dev/bus/usb,type=bind" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.c text eol=lf 2 | *.cpp text eol=lf 3 | *.h text eol=lf 4 | *.hpp text eol=lf 5 | *.ini text eol=lf 6 | *.ino text eol=lf 7 | *.json text eol=lf 8 | *.md text eol=lf 9 | *.properties text eol=lf 10 | *.txt text eol=lf 11 | *.yml text eol=lf 12 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to CRSF for Arduino 2 | 3 | Thank you for helping to make CRSF for Arduino better. 4 | Before you can go Gung Ho on coding, there are a few house rules I need you to familiarise yourself with. 5 | 6 | ## Do's and don'ts of CRSF for Arduino 7 | 8 | ### **Do...** 9 | 10 | 1. Read the README in its entirety _before_ submitting an issue, as your "bug" may very well be a case of PICNIC. 11 | 2. Respond in a timely and respectful manner. 12 | 3. If something isn't working, please provide evidence - EG Screenshots of compiler errors. 13 | 4. Provide concise details. 14 | 5. For general feedback, comments and questions, use the [Discussions Tab](https://github.com/ZZ-Cat/CRSFforArduino/discussions). 15 | 6. Format your code. CRSF for Arduino uses clang-format as a formatter. So, use that if you actually want to contribute to CRSF for Arduino's development. 16 | 17 | ### **Don't...** 18 | 19 | 1. Use CRSF for Arduino on incompatible hardware and write me an issue about "strange behaviour". 20 | 2. Ghost me for more than 14 consecutive days. 21 | 3. Write things like "This isn't working" and refuse to provide any information &/or context beyond that. 22 | 4. Write walls of text. Do this and watch how quickly I mark your issue as "TL;DR" and close it without any response from me. 23 | 5. Beg for updates. Please don't do this. If you do, I will intentionally take longer to release said update to spite you. 24 | 6. Assume that the project is "dead" after any significant amount of time has passed. 25 | 26 | ## A few notable points 27 | 28 | - CRSF for Arduino requires _all_ commits to be properly signed and verfied. 29 | This means that you _must_ have commit signing with either an SSH or GPG key set-up. 30 | Pull Requests with unverified commits _will not_ be accepted. 31 | - Not every Pull Request can or will be accepted. 32 | Simply because you submitted it, that doesn't automatically guarantee that your Pull Request will be merged. 33 | Generally speaking, I will let you know as to why your Pull Request will not be merged. Nine-times-out-of-ten, it's because of something that's easily fixable within reason. 34 | - I reserve the right to decline any Pull Request(s) if I deem it necessary to do so. 35 | - All Pull Requests to the Main-Trunk _must_ be reviewed before they can be merged. 36 | If I suggest to you to make changes to your Pull Request, don't fight me on it. I already have a reason why those changes need to be made and I would have told you what that reason is. If you are having difficulty with something, let me know. 37 | - All checks on your Pull Request _must_ pass before it can be merged. 38 | These checks ensure correct code formatting, and the codebase compiles successfully on all supported development boards. 39 | 40 | ## Ghosting 41 | 42 | ### What is it? 43 | 44 | This describes a situation where I have not received any interaction from you over a signifcant amount of time. 45 | I define "a significant amount of time" to be three weeks or more. 46 | For example, if you open an issue, and I ask for clarification on said issue and you have not replied to that message over a significant amount of time. This is ghosting. 47 | Another example is I tag you in a Pull Request because I require your assistance, yet there has not been any dialog from you over a significant amount of time. That is also ghosting. 48 | 49 | ### Why is ghosting bad? 50 | 51 | Short answer: It's unprofessional. 52 | 53 | Consider this scenario: 54 | You submit an Issue, requesting compatibility for your development board. 55 | I don't have the board-in-question available to me on-hand to test on my own end, so I elect to enlist your assistance. 56 | I go ahead and open a Pull Request, tagging you in it (and asking you to test the Pull Request). 57 | Only... 58 | One week passes. Okay, they're probably busy with life stuff and maybe they will respond over the weekend. 59 | Weekend comes... still no response. Okay, I will give them another week. 60 | Second week comes... still no response. Maybe if I give them another week? Hopefully they might respond?. 61 | Third week goes by... still no response, and CRSF for Arduino's development has stalled. 62 | 63 | ### How I combat it 64 | 65 | For a start, I have a grace period of up to 14 days past the last interaction you have had with me. 66 | After the 14 days is up, I decide (at my discretion) to close your Issue or Pull Request. In the context of a Pull Request, depending on the state of the code (EG Accuracy, completeness and functionality), at my discretion, I may merge the Pull Request "as-is", and any potential bugs that it could introduce (that I may have missed), you are welcome to open an Issue about it later on. 67 | 68 | This grace period exists to mitigate stalled development, and keep the development of CRSF for Arduino moving along. 69 | People that genuinely want to help out with the project, they typically respond within 24~72 hours, with some outlying exceptions responding up to 7~12 days apart. 70 | This grace period strikes a balance between my need to keep the project moving, and giving someone time to do what they need to do in their own private life as well as contributing to CRSF for Arduino. 71 | 72 | ## For more information 73 | 74 | - [About forks](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/about-forks) 75 | - [Creating a Pull Request from your fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) 76 | - [Code of Conduct](/CODE_OF_CONDUCT.md) 77 | - [Submit an Issue](https://github.com/ZZ-Cat/CRSFforArduino/issues/new/choose) 78 | - [License](/LICENSE.md) 79 | - [README](/README.md) 80 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | name: Submit a bug report 2 | description: Create a bug report and help me improve CRSF for Arduino 3 | labels: ["🐞 Bug 🐞", "Pending 📌"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thank you for taking the time to report a bug in CRSF for Arduino. 9 | Please fill out the form below to help me improve CRSF for Arduino. 10 | - type: checkboxes 11 | attributes: 12 | label: Is there an existing issue for this bug? 13 | description: Please check if there is already an issue for this bug. 14 | options: 15 | - label: I have searched the issues and there is no existing issue for this bug. 16 | required: true 17 | - type: dropdown 18 | id: development-environment 19 | attributes: 20 | label: What development environment are you using? 21 | description: Are you using the Arduino IDE or PlatformIO? 22 | options: 23 | - Arduino IDE 24 | - PlatformIO 25 | validations: 26 | required: true 27 | - type: dropdown 28 | id: board 29 | attributes: 30 | label: What board are you using? 31 | description: | 32 | Please select the board you are using. 33 | Please note that only the boards listed below are supported. 34 | options: 35 | - Adafruit Feather ESP32 36 | - Adafruit Feather ESP32-S2 37 | - Adafruit Feather ESP32-S3 (2 MB PSRAM) 38 | - Adafruit Feather ESP32-S3 (No PSRAM) 39 | - Adafruit Feather F405 40 | - Adafruit Feather M0 (Any variants EG Bluefruit LE, LoRa, etc) 41 | - Adafruit Feather M0 Express 42 | - Adafruit Feather M4 Express 43 | - Adafruit Feather M4 CAN Express 44 | - Adafruit Grand Central M4 45 | - Adafruit ItsyBitsy ESP32 46 | - Adafruit ItsyBitsy M0 Express 47 | - Adafruit ItsyBitsy M4 Express 48 | - Adafruit Metro ESP32-S2 49 | - Adafruit Metro M0 Express 50 | - Adafruit Metro M4 Express 51 | - Adafruit Metro M4 AirLift Lite 52 | - Adafruit QtPy ESP32 Pico 53 | - Adafruit QtPy ESP32-C3 54 | - Adafruit QtPy ESP32-S2 55 | - Adafruit QtPy ESP32-S3 56 | - Adafruit QtPy M0 57 | - Adafruit Trinket M0 58 | - Arduino MKR 1000 59 | - Arduino MKR FOX 1200 60 | - Arduino MKR GSM 1400 61 | - Arduino MKR NB 1500 62 | - Arduino MKR VIDOR 4000 63 | - Arduino MKR WAN 1300 64 | - Arduino MKR WAN 1310 65 | - Arduino MKR WiFi 1010 66 | - Arduino MKR ZERO 67 | - Arduino Nano 33 IoT 68 | - Arduino Nano RP2040 Connect 69 | - Arduino Nicla Vision 70 | - Arduino Opta 71 | - Arduino Portenta H7 72 | - Arduino Zero 73 | - Black Pill F103C8 74 | - Black Pill F103C8 (128 KB Flash) 75 | - Black Pill F401CC 76 | - Black Pill F411CE 77 | - Blue Pill F103C6 78 | - Blue Pill F103C8 79 | - Blue Pill F103C8 (128 KB Flash) 80 | - Espressif ESP32-C3 DevKitC-02 81 | - Espressif ESP32-C3 DevKitM-1 82 | - Espressif ESP32-S3 DevKitC-1-N8 83 | - Raspberry Pi Pico RP2040 84 | - Seeed Studio Xiao ESP32-C3 85 | - Seeed Studio Xiao ESP32-S3 86 | - Seeed Studio XIAO SAMD21 87 | - SparkFun ESP32 RedBoard IoT 88 | - SparkFun ESP32 Thing 89 | - SparkFun ESP32 Thing Plus 90 | - SparkFun ESP32-S2 Thing Plus 91 | - SparkFun MicroMod F405 92 | - ST Black F407VE 93 | - ST Black F407VG 94 | - ST Black F407ZE 95 | - ST Black F407ZG 96 | - ST Blue F407VE Mini 97 | - ST Discovery F413ZH 98 | - ST Discovery F746NG 99 | - ST Nucleo F401RE 100 | - ST Nucleo F411RE 101 | - ST Nucleo F429ZI 102 | - ST Nucleo F446RE 103 | - ST Nucleo F722ZE 104 | - ST Nucleo F746ZG 105 | - ST Nucleo F756ZG 106 | - ST Nucleo F767ZI 107 | - ST Nucleo H723ZG 108 | - ST Nucleo H743ZI 109 | - STM32F103C6 110 | - STM32F103C8 111 | - STM32F103CB 112 | - STM32F103R6 113 | - STM32F103R8 114 | - STM32F103RB 115 | - STM32F103RC 116 | - STM32F103RD 117 | - STM32F103RE 118 | - STM32F103RF 119 | - STM32F103RG 120 | - STM32F103T6 121 | - STM32F103T8 122 | - STM32F103TB 123 | - STM32F103V8 124 | - STM32F103VB 125 | - STM32F103VC 126 | - STM32F103VD 127 | - STM32F103VE 128 | - STM32F103VF 129 | - STM32F103VG 130 | - STM32F103ZC 131 | - STM32F103ZD 132 | - STM32F103ZE 133 | - STM32F103ZF 134 | - STM32F103ZG 135 | - STM32F401CB 136 | - STM32F401CC 137 | - STM32F401CD 138 | - STM32F401CE 139 | - STM32F401RB 140 | - STM32F401RC 141 | - STM32F401RD 142 | - STM32F401RE 143 | - STM32F405OE 144 | - STM32F405OG 145 | - STM32F405RG 146 | - STM32F405VG 147 | - STM32F405ZG 148 | - STM32F407VE 149 | - STM32F407VG 150 | - STM32F410C8 151 | - STM32F410CB 152 | - STM32F410R8 153 | - STM32F410RB 154 | - STM32F411CE 155 | - STM32F411RC 156 | - STM32F411RE 157 | - STM32F412CE 158 | - STM32F412CG 159 | - STM32F412RE 160 | - STM32F412RG 161 | - STM32f413CG 162 | - STM32F413CH 163 | - STM32F413RG 164 | - STM32F413RH 165 | - STM32F415RG 166 | - STM32F417VE 167 | - STM32F417VG 168 | - STM32F423CH 169 | - STM32F423RH 170 | - STM32F446RC 171 | - STM32F446RE 172 | - STM32F722IC 173 | - STM32F722IE 174 | - STM32F722RC 175 | - STM32F722RE 176 | - STM32F722VC 177 | - STM32F722VE 178 | - STM32F722ZC 179 | - STM32F722ZE 180 | - STM32H745BG 181 | - STM32H745BI 182 | - STM32H745IG 183 | - STM32H745II 184 | - STM32H745ZG 185 | - STM32H745ZI 186 | - STM32H750BT 187 | - Teensy 3.0 188 | - Teensy 3.1/3.2 189 | - Teensy 3.5 190 | - Teensy 3.6 191 | - Teensy 4.0 192 | - Teensy 4.1 193 | validations: 194 | required: true 195 | - type: dropdown 196 | id: bug-type 197 | attributes: 198 | label: What part of CRSF for Arduino is this bug related to? 199 | description: Please select the part of CRSF for Arduino that this bug is related to. 200 | options: 201 | - Build/Compile/Upload 202 | - Compatibility Table 203 | - RC Channels 204 | - Other (please specify) 205 | validations: 206 | required: true 207 | - type: textarea 208 | attributes: 209 | label: Current behaviour 210 | description: Provide a clear and concise description of the bug. 211 | placeholder: | 212 | A clear and concise description of what the bug is. 213 | EG: When I try to compile the code I get this error... 214 | validations: 215 | required: true 216 | - type: textarea 217 | attributes: 218 | label: Expected behaviour 219 | description: Provide a clear and concise description of what you expected to happen. 220 | placeholder: | 221 | A clear and concise description of what you expected to happen. 222 | EG: The code should compile without any errors. 223 | validations: 224 | required: true 225 | - type: textarea 226 | attributes: 227 | label: Steps to reproduce 228 | description: | 229 | Provide a clear and concise description of how to reproduce the bug. 230 | Please include any code that is required to reproduce the bug. 231 | placeholder: | 232 | A clear and concise description of how to reproduce the bug. 233 | EG: To reproduce the bug, compile the code with the following settings... 234 | validations: 235 | required: true 236 | - type: textarea 237 | attributes: 238 | label: Additional information 239 | description: | 240 | Provide any additional information that may be relevant to the bug. 241 | EG: I am using the following version of the Arduino IDE... 242 | 243 | Tip: You can paste code, logs, or config files into the text box below. 244 | Screenshots can also be pasted into the text box below. 245 | validations: 246 | required: false 247 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/devboard-request.yml: -------------------------------------------------------------------------------- 1 | name: Devboard Compatibility Request 2 | description: Tell me which development board you would like me to add to CRSF for Arduino. 3 | labels: ["✨️ Enhancement ✨️", "Pending 📌"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Request your development board to be added to CRSF for Arduino's Compatibility Table. 9 | - type: checkboxes 10 | attributes: 11 | label: Minimum requirements 12 | description: | 13 | Your devboard _must_ meet these requirements in order for it to be compatible with CRSF for Arduino. 14 | Please check your development board's documentation and the data sheet of its host microcontroller for more information. 15 | options: 16 | - label: Core clock speed is 48 MHz or faster? 17 | required: true 18 | - label: Is the CPU an ARM Cortex M0+ or later? 19 | required: true 20 | - label: Does the host microcontroller have at least 32 KB of flash memory? 21 | required: true 22 | - label: Is the host microcontroller's UART peripheral able to support 420,000 (or higher) baud rate(s)? 23 | required: true 24 | - label: Is your development board currently supported in the Arduino IDE? 25 | required: true 26 | - type: textarea 27 | attributes: 28 | label: What development board would you like me to add? 29 | description: | 30 | Provide a clear and concise description of what your devboard is, what its host microcontroller is, 31 | and provide links to the development board's documentation and the data sheet of its host microcontroller. 32 | placeholder: My development board is... 33 | validations: 34 | required: true 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | name: Submit a feature request 2 | description: Suggest an idea for CRSF for Arduino to help make it better. 3 | labels: ["✨️ Enhancement ✨️", "Pending 📌"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thank you for suggesting an idea for CRSF for Arduino! 9 | 10 | If your idea is still in its early stages or you're not sure if it will benefit my project, 11 | you can open a discussion in the [Ideas](https://github.com/ZZ-Cat/CRSFforArduino/discussions/categories/ideas) category instead. 12 | - type: checkboxes 13 | attributes: 14 | label: Is there already an issue for this? 15 | description: Please check if there is already an issue for this feature request. 16 | options: 17 | - label: I have searched the issues and there is no existing issue for this feature request. 18 | required: true 19 | - type: textarea 20 | attributes: 21 | label: Is your feature request related to a problem? 22 | description: Please provide a clear and concise description of what the problem is. 23 | placeholder: I'm always frustrated when [...] 24 | validations: 25 | required: true 26 | - type: textarea 27 | attributes: 28 | label: Describe the solution you'd like 29 | description: Please provide a clear and concise description of what you want to happen. 30 | placeholder: I'd like to be able to [...] 31 | validations: 32 | required: true 33 | - type: textarea 34 | attributes: 35 | label: Describe alternatives you've considered 36 | description: Please provide a clear and concise description of any alternative solutions or features you've considered. 37 | placeholder: | 38 | I have considered [...] 39 | But it will not work because [...] 40 | validations: 41 | required: true 42 | - type: textarea 43 | attributes: 44 | label: Additional context 45 | description: Please provide any other context or screenshots about the feature request here. 46 | placeholder: | 47 | Add any other context or screenshots about the feature request here. 48 | EG: I am using [...] 49 | EG: I am using the following version of CRSF for Arduino [...] 50 | validations: 51 | required: false 52 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "Main-Trunk" ] 17 | pull_request: 18 | branches: [ "Main-Trunk" ] 19 | schedule: 20 | - cron: '31 20 * * 6' 21 | 22 | jobs: 23 | analyze: 24 | name: Scan for vulnerabilities 25 | # Runner size impacts CodeQL analysis time. To learn more, please see: 26 | # - https://gh.io/recommended-hardware-resources-for-running-codeql 27 | # - https://gh.io/supported-runners-and-hardware-resources 28 | # - https://gh.io/using-larger-runners 29 | # Consider using larger runners for possible analysis time improvements. 30 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 31 | timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} 32 | permissions: 33 | # required for all workflows 34 | security-events: write 35 | 36 | # only required for workflows in private repositories 37 | actions: read 38 | contents: read 39 | 40 | strategy: 41 | fail-fast: false 42 | matrix: 43 | language: [ 'c-cpp', 'python' ] 44 | # CodeQL supports [ 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' ] 45 | # Use only 'java-kotlin' to analyze code written in Java, Kotlin or both 46 | # Use only 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both 47 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 48 | 49 | steps: 50 | - name: Checkout repository 51 | uses: actions/checkout@v4 52 | 53 | # Initializes the CodeQL tools for scanning. 54 | - name: Initialize CodeQL 55 | uses: github/codeql-action/init@v3 56 | with: 57 | languages: ${{ matrix.language }} 58 | # If you wish to specify custom queries, you can do so here or in a config file. 59 | # By default, queries listed here will override any specified in a config file. 60 | # Prefix the list here with "+" to use these queries and those in the config file. 61 | 62 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 63 | # queries: security-extended,security-and-quality 64 | 65 | 66 | # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). 67 | # If this step fails, then you should remove it and run the build manually (see below) 68 | # - name: Autobuild 69 | # uses: github/codeql-action/autobuild@v3 70 | 71 | # ℹ️ Command-line programs to run using the OS shell. 72 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 73 | 74 | # If the Autobuild fails above, remove it and uncomment the following three lines. 75 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 76 | 77 | # - run: | 78 | # echo "Run, Build Application using script" 79 | # ./location_of_script_within_repo/buildscript.sh 80 | 81 | # Setup PlatformIO Build Environment 82 | - uses: actions/cache@v4 83 | with: 84 | path: | 85 | ~/.cache/pip 86 | ~/.platformio/.cache 87 | key: ${{ runner.os }}-pio 88 | - uses: actions/setup-python@v5 89 | with: 90 | python-version: '3.9' 91 | - name: Install PlatformIO Core 92 | run: pip install --upgrade platformio 93 | 94 | # Build the Defect Detector with PlatformIO 95 | - name: Build defect detector 96 | run: | 97 | cd src/build/scripts 98 | python build.py 99 | 100 | # Perform CodeQL Analysis 101 | - name: Perform CodeQL Analysis 102 | uses: github/codeql-action/analyze@v3 103 | with: 104 | category: "/language:${{matrix.language}}" 105 | -------------------------------------------------------------------------------- /.github/workflows/quality_control.yml: -------------------------------------------------------------------------------- 1 | name: Quality Control 2 | 3 | on: 4 | push: 5 | branches: [ "Main-Trunk" ] 6 | pull_request: 7 | branches: [ "Main-Trunk" ] 8 | 9 | jobs: 10 | # Code formatting with clang-format 11 | code_formatting: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Set up Python 16 | uses: actions/setup-python@v5 17 | with: 18 | python-version: '3.x' 19 | - name: Install Adafruit's Arduino CI 20 | uses: actions/checkout@v4.1.1 21 | with: 22 | repository: adafruit/ci-arduino 23 | path: ci 24 | - name: Install ci/actions_install.sh 25 | run: bash ci/actions_install.sh 26 | - name: Check for correct formatting with clang-format 27 | run: python3 ci/run-clang-format.py -e "ci/*" -e "bin/*" -r . 28 | 29 | # New quality control script using Python and PlatformIO. 30 | defect_detector: 31 | runs-on: ubuntu-latest 32 | steps: 33 | - uses: actions/checkout@v4.1.1 34 | - uses: actions/cache@v4 35 | with: 36 | path: | 37 | ~/.cache/pip 38 | ~/.platformio/.cache 39 | key: ${{runner.os}}-pio 40 | - uses: actions/setup-python@v5 41 | with: 42 | python-version: '3.9' 43 | - name: Install PlatformIO 44 | run: pip install --upgrade platformio 45 | - name: Run Quality Control script 46 | run: | 47 | cd src/build/scripts 48 | python build.py --build-on-compatible-devices 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Artefacts for compiling the code 2 | .pio/ 3 | 4 | # Artefacts for Visual Studio Code 5 | .vscode/ 6 | 7 | # PlatformIO specific files 8 | test/README 9 | examples/platformio/cfa_code_test.cpp 10 | examples/platformio/hac_driver_test.cpp 11 | examples/platformio/hw_multi_mutex_test.cpp 12 | examples/platformio/hw_mutex_test.cpp 13 | 14 | # Template files for CRSF for Arduino 15 | templates/ 16 | 17 | # Arduino IDE artefacts 18 | .development 19 | 20 | # Developer's notes 21 | notes/ 22 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, caste, color, religion, or sexual 10 | identity and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the overall 26 | community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or advances of 31 | any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email address, 35 | without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | I, Cassandra "ZZ Cat" Robinson, am the sole community leader of this project. 42 | I am responsible for clarifying and enforcing our standards of acceptable behavior 43 | and will take appropriate and fair corrective action in response to any behavior 44 | that is deemed inappropriate, threatening, offensive, or harmful. 45 | 46 | I, Cassandra "ZZ Cat" Robinson, as a community leader have the right and 47 | responsibility to remove, edit, or reject comments, commits, code, wiki edits, 48 | issues, and other contributions that are not aligned to this Code of Conduct, and 49 | will communicate reasons for moderation decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to Cassandra "ZZ Cat" Robinson on either her Discord: ZZ Cat#0174 or 63 | her email at nicad.heli.flier@gmail.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series of 86 | actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or permanent 93 | ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within the 113 | community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.1, available at 119 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 120 | 121 | Community Impact Guidelines were inspired by 122 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 123 | 124 | For answers to common questions about this code of conduct, see the FAQ at 125 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 126 | [https://www.contributor-covenant.org/translations][translations]. 127 | 128 | [homepage]: https://www.contributor-covenant.org 129 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 130 | [Mozilla CoC]: https://github.com/mozilla/diversity 131 | [FAQ]: https://www.contributor-covenant.org/faq 132 | [translations]: https://www.contributor-covenant.org/translations 133 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CRSF for Arduino 2 | 3 | [![GitHub release (latest by date)](https://img.shields.io/github/v/release/ZZ-Cat/CRSFforArduino)](https://github.com/ZZ-Cat/CRSFforArduino/releases/latest) 4 | [![GitHub license](https://img.shields.io/github/license/ZZ-Cat/CRSFforArduino)](https://github.com/ZZ-Cat/CRSFforArduino/blob/Main-Trunk/LICENSE.md) 5 | ![Quality Control](https://github.com/ZZ-Cat/CRSFforArduino/actions/workflows/quality_control.yml/badge.svg) 6 | [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-%23FE5196?logo=conventionalcommits&logoColor=white)](https://conventionalcommits.org) 7 | 8 | ## Written and developed by 9 | 10 | Cassandra "ZZ Cat" Robinson 11 | 12 | > [!NOTE] 13 | > CRSF for Arduino is now distributed under the [GNU Affero General Public License v3.](https://github.com/ZZ-Cat/CRSFforArduino/blob/Main-Trunk/LICENSE.md) 14 | > Please take the time to familiarise yourself with the terms of this license _before_ you use this library in your projects. 15 | 16 | ## Description 17 | 18 | CRSF for Arduino brings the Crossfire Protocol to the Arduino ecosystem. 19 | This library enables you to connect either a TBS Crossfire or ExpressLRS receiver to your development board, 20 | giving you access to telemetry and up to 16 11-bit proportional RC channels over a tried-and-true serial protocol. 21 | 22 | The Crossfire Protocol (better known as CRSF) is used by both Team BlackSheep (in their Crossfire and Tracer receivers) and 23 | ExpressLRS. The latter of the two are well-known in the FPV drone community for their ultra low latency and long range control 24 | link. 25 | By pairing CRSF for Arduino with an ExpressLRS transmitter and receiver, you have a control link between your RC handset and your development project that is robust in tough RF environments. 26 | 27 | ## Documentation 28 | 29 | CRSF for Arduino's documentation is now live. 30 | Feel free to peruse the [Wiki](https://github.com/ZZ-Cat/CRSFforArduino/wiki) at your leisure. 31 | For new users, it is _strongly_ recommended that you read the documentation in its entirety. 32 | 33 | ## Features 34 | 35 | CRSF for Arduino comes packaged with these features: 36 | 37 | - 16 11-bit proportional RC channels. 38 | - Diverse modern hardware support from the following architectures: 39 | - ESP32, RP2040, SAMD21, SAMD5x, SAME5x, STM32, and Teensy 4.x. 40 | - Flight Modes, both custom and standard. 41 | - Standard Flight Modes are based on the Flight Modes from Betaflight 4.3. 42 | - Custom Flight Modes use text-based strings that you can assign custom names to for your bespoke purposes. 43 | - Both Standard and Custom Flight Modes can be sent as Telemetry back to your controller. 44 | - Event-driven API. 45 | - Fully configurable. 46 | - CRSF for Arduino can be tailored to suit the needs of your individual projects. 47 | - Telemetry. 48 | - Attitude. 49 | - Barometric Altitude. 50 | - Battery. 51 | - Flight Modes. 52 | - GPS. 53 | 54 | ## Software license 55 | 56 | CRSF for Arduino is distributed under a new license: The [GNU Affero GPL v3](https://github.com/ZZ-Cat/CRSFforArduino/blob/Main-Trunk/LICENSE.md). 57 | For regular users, this may be a non-issue for you, as you are already REQUIRED by existing licensing terms to make CRSF for Arduino's source code available (you're already doing this by linking back to its GitHub Repository). 58 | With the upcoming Serial Transmitter Interface, this increases CRSF for Arduino's potential usage in the context of network servers. With the former GPL v3, you could use CRSF for Arduino as a dependency in your network host and never make the source code available to clients. Updating CRSF for Arduino's license to the Affero GPL v3 closes that loop hole, because you are REQUIRED to make the source code of CRSF for Arduino available in _all_ of its distributions. 59 | 60 | Not a lot has changed across licenses. The same basic four freedoms are upheld: Use, copy, modify, and re-distribute. 61 | The only noteworthy change here you are REQUIRED to make the source code available to clients, if you choose to use CRSF for Arduino as a part of your server-side project. To do this, simply link back to this repository via its URL in addition to the terms outlined in the license. 62 | 63 | ## Attributions 64 | 65 | I give credit where credit is due. Because CRSF for Arduino isn't entirely my own idea, but built on the shoulders of giants. Here is a list of credits to those what helped to make this possible: 66 | 67 | - Inspiration for this library 68 | - [ExpressLRS](https://github.com/ExpressLRS) 69 | - [Development Team](https://github.com/orgs/ExpressLRS/people) 70 | - [License](https://github.com/ExpressLRS/ExpressLRS/blob/master/LICENSE) 71 | - [Source Code](https://github.com/ExpressLRS/ExpressLRS) 72 | - [Website](https://www.expresslrs.org/3.0/) 73 | - [Team BlackSheep FPV](https://github.com/tbs-fpv) - The folks behind the CRSF protocol that both ExpressLRS and CRSF for Arduino uses. 74 | - References for CRSF for Arduino 75 | - [BetaFlight](https://github.com/betaflight) 76 | - [Development Team](https://github.com/orgs/betaflight/people) 77 | - [License](https://github.com/betaflight/betaflight/blob/master/LICENSE) 78 | - [Source Code](https://github.com/betaflight/betaflight) 79 | - [Website](https://betaflight.com/) 80 | - [CRSF-WG](https://github.com/crsf-wg/crsf) 81 | - This is the official repository for The CRSF Protocol, which CRSF for Arduino is based on. 82 | - [RotorFlight](https://github.com/rotorflight) 83 | - [Development Team](https://github.com/rotorflight#credits) 84 | - [License](https://github.com/rotorflight/rotorflight-firmware/blob/master/LICENSE) 85 | - [Source Code](https://github.com/rotorflight/rotorflight-firmware) 86 | - Third Party libraries 87 | - [Adafruit Industries](https://github.com/adafruit) 88 | - [Adafruit_ZeroDMA](https://github.com/adafruit/Adafruit_ZeroDMA) 89 | - [Author](https://github.com/PaintYourDragon) 90 | - [License](https://github.com/adafruit/Adafruit_ZeroDMA/blob/master/LICENSE) 91 | - [Website](https://www.adafruit.com/) 92 | 93 | ## Contributing 94 | 95 | If you would like to contribute to the development to CRSF for Arduino, here is what you can do: 96 | 97 | 1. Read my [code of conduct](https://github.com/ZZ-Cat/CRSFforArduino/blob/Main-Trunk/CODE_OF_CONDUCT.md) and [contribution guidelines](https://github.com/ZZ-Cat/CRSFforArduino/blob/Main-Trunk/.github/CONTRIBUTING.md); 98 | 2. Fork CRSF for Arduino; 99 | 3. Make your contribution to the codebase; 100 | 4. Submit your changes in your fork as a Pull Request back to CRSF for Arduino. 101 | 102 | Your contributions are very welcome, and if it benefits the project and community, it will be merged into the Main-Trunk. 103 | -------------------------------------------------------------------------------- /examples/flight_modes/flight_modes.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file flight_modes.ino 3 | * @author Cassandra "ZZ Cat" Robinson (nicad.heli.flier@gmail.com) 4 | * @brief Example of how to read flight modes from a receiver. 5 | * 6 | * @copyright Copyright (c) 2024, Cassandra "ZZ Cat" Robinson. All rights reserved. 7 | * 8 | * @section License GNU Affero General Public License v3.0 9 | * This example is a part of the CRSF for Arduino library. 10 | * CRSF for Arduino is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * CRSF for Arduino is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with CRSF for Arduino. If not, see . 22 | * 23 | */ 24 | 25 | #include "CRSFforArduino.hpp" 26 | 27 | /* Tested with the following equipment: 28 | - Controller: RadioMaster TX16S Max Edition Mk1 29 | - Firmware: EdgeTX 2.10.0 Nightly 30 | - Lua Script: iNav OpenTX Telemetry Widget 2.2.3 31 | - Transmitter Module: RadioMaster Ranger 32 | - Firmware: ExpressLRS 3.3.1 33 | - Receiver: RadioMaster RP3 Diversity 34 | - Firmware: ExpressLRS 3.3.1 35 | - Development Board: Adafruit Metro M4 Express 36 | - Board Package: Adafruit SAMD Boards 1.7.5 37 | - Framework: Arduino 1.8.13 38 | - Library: CRSF for Arduino 0.5.0 39 | */ 40 | 41 | #define FLIGHT_MODE_ARM_CHANNEL 5 // Set FLIGHT_MODE_ARM_CHANNEL to the channel that you want to use to simulate arming your drone. 42 | #define FLIGHT_MODE_ARM_MIN 1000 43 | #define FLIGHT_MODE_ARM_MAX 1800 44 | 45 | #define FLIGHT_MODE_ACRO_CHANNEL 8 // Set FLIGHT_MODE_ACRO_CHANNEL to the channel that you want to use to simulate acro mode. 46 | #define FLIGHT_MODE_ACRO_MIN 900 47 | #define FLIGHT_MODE_ACRO_MAX 1300 48 | 49 | #define FLIGHT_MODE_ANGLE_CHANNEL 8 // Set FLIGHT_MODE_ANGLE_CHANNEL to the channel that you want to use to simulate angle mode. 50 | #define FLIGHT_MODE_ANGLE_MIN 1300 51 | #define FLIGHT_MODE_ANGLE_MAX 1700 52 | 53 | #define FLIGHT_MODE_HORIZON_CHANNEL 8 // Set FLIGHT_MODE_HORIZON_CHANNEL to the channel that you want to use to simulate horizon mode. 54 | #define FLIGHT_MODE_HORIZON_MIN 1700 55 | #define FLIGHT_MODE_HORIZON_MAX 2100 56 | 57 | CRSFforArduino crsf = CRSFforArduino(&Serial1); 58 | 59 | void onFlightModeUpdate(serialReceiverLayer::flightModeId_t); 60 | 61 | void setup() 62 | { 63 | // Initialise the Serial Port and wait for it to open. 64 | Serial.begin(115200); 65 | // while (!Serial) 66 | // { 67 | // ; 68 | // } 69 | 70 | Serial.println("Flight Modes Example"); 71 | 72 | // Initialise CRSF for Arduino. 73 | if (!crsf.begin()) 74 | { 75 | Serial.println("CRSF initialisation failed!"); 76 | while (1) 77 | { 78 | ; 79 | } 80 | } 81 | 82 | // Set flight modes. 83 | if (!crsf.setFlightMode(serialReceiverLayer::FLIGHT_MODE_DISARMED, FLIGHT_MODE_ARM_CHANNEL, FLIGHT_MODE_ARM_MIN, FLIGHT_MODE_ARM_MAX)) 84 | { 85 | Serial.println("Failed to set \"DISARMED\" flight mode!"); 86 | while (1) 87 | { 88 | ; 89 | } 90 | } 91 | 92 | if (!crsf.setFlightMode(serialReceiverLayer::FLIGHT_MODE_ACRO, FLIGHT_MODE_ACRO_CHANNEL, FLIGHT_MODE_ACRO_MIN, FLIGHT_MODE_ACRO_MAX)) 93 | { 94 | Serial.println("Failed to set \"ACRO\" flight mode!"); 95 | while (1) 96 | { 97 | ; 98 | } 99 | } 100 | 101 | if (!crsf.setFlightMode(serialReceiverLayer::FLIGHT_MODE_ANGLE, FLIGHT_MODE_ANGLE_CHANNEL, FLIGHT_MODE_ANGLE_MIN, FLIGHT_MODE_ANGLE_MAX)) 102 | { 103 | Serial.println("Failed to set \"ANGLE\" flight mode!"); 104 | while (1) 105 | { 106 | ; 107 | } 108 | } 109 | 110 | if (!crsf.setFlightMode(serialReceiverLayer::FLIGHT_MODE_HORIZON, FLIGHT_MODE_HORIZON_CHANNEL, FLIGHT_MODE_HORIZON_MIN, FLIGHT_MODE_HORIZON_MAX)) 111 | { 112 | Serial.println("Failed to set \"HORIZON\" flight mode!"); 113 | while (1) 114 | { 115 | ; 116 | } 117 | } 118 | 119 | // Set flight mode callback. 120 | crsf.setFlightModeCallback(onFlightModeUpdate); 121 | 122 | Serial.print("\tArm channel: "); 123 | Serial.println(FLIGHT_MODE_ARM_CHANNEL); 124 | Serial.print("\tFlight Modes Channel: "); 125 | Serial.println(FLIGHT_MODE_ACRO_CHANNEL); 126 | } 127 | 128 | void loop() 129 | { 130 | // Update CRSF for Arduino. 131 | crsf.update(); 132 | } 133 | 134 | void onFlightModeUpdate(serialReceiverLayer::flightModeId_t flightMode) 135 | { 136 | /* Here is where you would put your flight mode implementation. 137 | For this example, we will just print the flight mode to the serial port, 138 | and send it to the controller as telemetry. */ 139 | static serialReceiverLayer::flightModeId_t lastFlightMode = serialReceiverLayer::FLIGHT_MODE_DISARMED; 140 | 141 | if (flightMode != lastFlightMode) 142 | { 143 | Serial.print("Flight Mode: "); 144 | switch (flightMode) 145 | { 146 | case serialReceiverLayer::FLIGHT_MODE_DISARMED: 147 | Serial.println("Disarmed"); 148 | break; 149 | case serialReceiverLayer::FLIGHT_MODE_ACRO: 150 | Serial.println("Acro"); 151 | break; 152 | case serialReceiverLayer::FLIGHT_MODE_WAIT: 153 | Serial.println("Wait for GPS Lock"); 154 | break; 155 | case serialReceiverLayer::FLIGHT_MODE_FAILSAFE: 156 | Serial.println("Failsafe"); 157 | break; 158 | case serialReceiverLayer::FLIGHT_MODE_GPS_RESCUE: 159 | Serial.println("GPS Rescue"); 160 | break; 161 | case serialReceiverLayer::FLIGHT_MODE_PASSTHROUGH: 162 | Serial.println("Passthrough"); 163 | break; 164 | case serialReceiverLayer::FLIGHT_MODE_ANGLE: 165 | Serial.println("Angle"); 166 | break; 167 | case serialReceiverLayer::FLIGHT_MODE_HORIZON: 168 | Serial.println("Horizon"); 169 | break; 170 | case serialReceiverLayer::FLIGHT_MODE_AIRMODE: 171 | Serial.println("Airmode"); 172 | break; 173 | default: 174 | Serial.println("Unknown"); 175 | break; 176 | } 177 | lastFlightMode = flightMode; 178 | 179 | crsf.telemetryWriteFlightMode(flightMode); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /examples/link_stats/link_stats.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file link_stats.ino 3 | * @author Cassandra "ZZ Cat" Robinson (nicad.heli.flier@gmail.com) 4 | * @brief Example of how to read link statistics from a receiver. 5 | * 6 | * @copyright Copyright (c) 2024, Cassandra "ZZ Cat" Robinson. All rights reserved. 7 | * 8 | * @section License GNU Affero General Public License v3.0 9 | * This example is a part of the CRSF for Arduino library. 10 | * CRSF for Arduino is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * CRSF for Arduino is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with CRSF for Arduino. If not, see . 22 | * 23 | */ 24 | 25 | #include "CRSFforArduino.hpp" 26 | 27 | /* Tested with the following equipment: 28 | - Controller: RadioMaster TX16S Max Edition Mk1 29 | - Firmware: EdgeTX 2.10.0 Nightly 30 | - Lua Script: iNav OpenTX Telemetry Widget 2.2.3 31 | - Transmitter Module: RadioMaster Ranger 32 | - Firmware: ExpressLRS 3.3.1 33 | - Receiver: RadioMaster RP3 Diversity 34 | - Firmware: ExpressLRS 3.3.1 35 | - Development Board: Adafruit Metro M4 Express 36 | - Board Package: Adafruit SAMD Boards 1.7.5 37 | - Framework: Arduino 1.8.13 38 | - Library: CRSF for Arduino 1.0.0 39 | */ 40 | 41 | CRSFforArduino *crsf = nullptr; 42 | 43 | void onLinkStatisticsUpdate(serialReceiverLayer::link_statistics_t); 44 | 45 | void setup() 46 | { 47 | Serial.begin(115200); 48 | while (!Serial) 49 | { 50 | delay(10); 51 | } 52 | 53 | Serial.println("Link Statistics Example."); 54 | 55 | crsf = new CRSFforArduino(); 56 | 57 | if (!crsf->begin()) 58 | { 59 | Serial.println("CRSF for Arduino failed to initialise."); 60 | 61 | delete crsf; 62 | crsf = nullptr; 63 | 64 | while (1) 65 | { 66 | delay(10); 67 | } 68 | } 69 | 70 | // Set link statistics callback. 71 | crsf->setLinkStatisticsCallback(onLinkStatisticsUpdate); 72 | 73 | Serial.println("Ready."); 74 | delay(1000); 75 | } 76 | 77 | void loop() 78 | { 79 | crsf->update(); 80 | } 81 | 82 | void onLinkStatisticsUpdate(serialReceiverLayer::link_statistics_t linkStatistics) 83 | { 84 | static unsigned long lastPrint = 0; 85 | if (millis() - lastPrint >= 200) 86 | { 87 | lastPrint = millis(); 88 | Serial.print("Link Statistics: "); 89 | Serial.print("RSSI: "); 90 | Serial.print(linkStatistics.rssi); 91 | Serial.print(", Link Quality: "); 92 | Serial.print(linkStatistics.lqi); 93 | Serial.print(", Signal-to-Noise Ratio: "); 94 | Serial.print(linkStatistics.snr); 95 | Serial.print(", Transmitter Power: "); 96 | Serial.println(linkStatistics.tx_power); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /examples/platformio/main.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file main.cpp 3 | * @author Cassandra "ZZ Cat" Robinson (nicad.heli.flier@gmail.com) 4 | * @brief This is the main development file for CRSF for Arduino. 5 | * 6 | * @copyright Copyright (c) 2024, Cassandra "ZZ Cat" Robinson. All rights reserved. 7 | * 8 | * @section License GNU Affero General Public License v3.0 9 | * This source file is a part of the CRSF for Arduino library. 10 | * CRSF for Arduino is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * CRSF for Arduino is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with CRSF for Arduino. If not, see . 22 | * 23 | */ 24 | 25 | #include "Arduino.h" 26 | #include "CRSFforArduino.hpp" 27 | 28 | CRSFforArduino *crsf = nullptr; 29 | 30 | void onReceiveRcChannels(serialReceiverLayer::rcChannels_t *rcChannels); 31 | 32 | void setup() 33 | { 34 | Serial.begin(115200); 35 | while (!Serial) 36 | { 37 | delay(10); 38 | } 39 | 40 | crsf = new CRSFforArduino(); 41 | 42 | if (!crsf->begin()) 43 | { 44 | Serial.println("CRSF for Arduino failed to initialise."); 45 | 46 | delete crsf; 47 | crsf = nullptr; 48 | 49 | while (1) 50 | { 51 | delay(10); 52 | } 53 | } 54 | 55 | crsf->setRcChannelsCallback(onReceiveRcChannels); 56 | } 57 | 58 | void loop() 59 | { 60 | crsf->update(); 61 | } 62 | 63 | void onReceiveRcChannels(serialReceiverLayer::rcChannels_t *rcChannels) 64 | { 65 | static unsigned long lastPrint = millis(); 66 | if (millis() - lastPrint >= 100) 67 | { 68 | lastPrint = millis(); 69 | 70 | static bool initialised = false; 71 | static bool lastFailSafe = false; 72 | if (rcChannels->failsafe != lastFailSafe || !initialised) 73 | { 74 | initialised = true; 75 | lastFailSafe = rcChannels->failsafe; 76 | Serial.print("FailSafe: "); 77 | Serial.println(lastFailSafe ? "Active" : "Inactive"); 78 | } 79 | 80 | if (rcChannels->failsafe == false) 81 | { 82 | Serial.print("RC Channels rcToUs(rcChannels->value[0])); 84 | Serial.print(", E: "); 85 | Serial.print(crsf->rcToUs(rcChannels->value[1])); 86 | Serial.print(", T: "); 87 | Serial.print(crsf->rcToUs(rcChannels->value[2])); 88 | Serial.print(", R: "); 89 | Serial.print(crsf->rcToUs(rcChannels->value[3])); 90 | Serial.print(", Aux1: "); 91 | Serial.print(crsf->rcToUs(rcChannels->value[4])); 92 | Serial.print(", Aux2: "); 93 | Serial.print(crsf->rcToUs(rcChannels->value[5])); 94 | Serial.print(", Aux3: "); 95 | Serial.print(crsf->rcToUs(rcChannels->value[6])); 96 | Serial.print(", Aux4: "); 97 | Serial.print(crsf->rcToUs(rcChannels->value[7])); 98 | Serial.println(">"); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /examples/rc_channels/rc_channels.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file rc_channels.ino 3 | * @author Cassandra "ZZ Cat" Robinson (nicad.heli.flier@gmail.com) 4 | * @brief Example of how to read rc channels from a receiver. 5 | * 6 | * @copyright Copyright (c) 2024, Cassandra "ZZ Cat" Robinson. All rights reserved. 7 | * 8 | * @section License GNU Affero General Public License v3.0 9 | * This example is a part of the CRSF for Arduino library. 10 | * CRSF for Arduino is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * CRSF for Arduino is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with CRSF for Arduino. If not, see . 22 | * 23 | */ 24 | 25 | #include "CRSFforArduino.hpp" 26 | #define USE_SERIAL_PLOTTER 0 27 | 28 | CRSFforArduino *crsf = nullptr; 29 | 30 | int rcChannelCount = crsfProtocol::RC_CHANNEL_COUNT; 31 | const char *rcChannelNames[] = { 32 | "A", 33 | "E", 34 | "T", 35 | "R", 36 | "Aux1", 37 | "Aux2", 38 | "Aux3", 39 | "Aux4", 40 | 41 | "Aux5", 42 | "Aux6", 43 | "Aux7", 44 | "Aux8", 45 | "Aux9", 46 | "Aux10", 47 | "Aux11", 48 | "Aux12"}; 49 | 50 | void onReceiveRcChannels(serialReceiverLayer::rcChannels_t *rcChannels); 51 | 52 | void setup() 53 | { 54 | // Initialise the serial port & wait for the port to open. 55 | Serial.begin(115200); 56 | while (!Serial) 57 | { 58 | ; 59 | } 60 | 61 | // Initialise CRSF for Arduino. 62 | crsf = new CRSFforArduino(); 63 | if (!crsf->begin()) 64 | { 65 | crsf->end(); 66 | 67 | delete crsf; 68 | crsf = nullptr; 69 | 70 | Serial.println("CRSF for Arduino initialisation failed!"); 71 | while (1) 72 | { 73 | delay(10); 74 | } 75 | } 76 | 77 | rcChannelCount = rcChannelCount > crsfProtocol::RC_CHANNEL_COUNT ? crsfProtocol::RC_CHANNEL_COUNT : rcChannelCount; 78 | 79 | crsf->setRcChannelsCallback(onReceiveRcChannels); 80 | 81 | // Show the user that the sketch is ready. 82 | Serial.println("RC Channels Example"); 83 | delay(1000); 84 | Serial.println("Ready"); 85 | delay(1000); 86 | } 87 | 88 | void loop() 89 | { 90 | crsf->update(); 91 | } 92 | 93 | void onReceiveRcChannels(serialReceiverLayer::rcChannels_t *rcChannels) 94 | { 95 | if (rcChannels->failsafe == false) 96 | { 97 | /* Print RC channels every 100 ms. */ 98 | unsigned long thisTime = millis(); 99 | static unsigned long lastTime = millis(); 100 | 101 | /* Compensate for millis() overflow. */ 102 | if (thisTime < lastTime) 103 | { 104 | lastTime = thisTime; 105 | } 106 | 107 | if (thisTime - lastTime >= 100) 108 | { 109 | lastTime = thisTime; 110 | #if USE_SERIAL_PLOTTER > 0 111 | for (int i = 1; i <= rcChannelCount; i++) 112 | { 113 | Serial.print(i); 114 | Serial.print(":"); 115 | Serial.print(crsf->rcToUs(crsf->getChannel(i))); 116 | Serial.print("\t"); 117 | } 118 | Serial.println(); 119 | #else 120 | Serial.print("RC Channels <"); 121 | for (int i = 1; i <= rcChannelCount; i++) 122 | { 123 | Serial.print(rcChannelNames[i - 1]); 124 | Serial.print(": "); 125 | Serial.print(crsf->rcToUs(crsf->getChannel(i))); 126 | 127 | if (i < rcChannelCount) 128 | { 129 | Serial.print(", "); 130 | } 131 | } 132 | Serial.println(">"); 133 | #endif 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /examples/telemetry/telemetry.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file flight_modes.ino 3 | * @author Cassandra "ZZ Cat" Robinson (nicad.heli.flier@gmail.com) 4 | * @brief Example of how to send telemetry back to your RC handset using CRSF for Arduino. 5 | * 6 | * @copyright Copyright (c) 2024, Cassandra "ZZ Cat" Robinson. All rights reserved. 7 | * 8 | * @section License GNU Affero General Public License v3.0 9 | * This example is a part of the CRSF for Arduino library. 10 | * CRSF for Arduino is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * CRSF for Arduino is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with CRSF for Arduino. If not, see . 22 | * 23 | */ 24 | 25 | #include "CRSFforArduino.hpp" 26 | 27 | /* Configuration Options. */ 28 | #define VIEW_RC_CHANNELS 0 // Set VIEW_RC_CHANNELS to 1 to view the RC channel data in the serial monitor. 29 | #define GENERATE_RANDOM_BATTERY_DATA 0 // Set GENERATE_RANDOM_BATTERY_DATA to 1 to generate random battery sensor telemetry data. 30 | #define GENERATE_RANDOM_GPS_DATA 0 // Set GENERATE_RANDOM_GPS_DATA to 1 to generate random GPS telemetry data. 31 | 32 | uint32_t timeNow = 0; 33 | 34 | /* Initialise the attitude telemetry with default values. */ 35 | int16_t roll = -200; // Roll is in decided degrees (eg -200 = -20.0 degrees). 36 | int16_t pitch = 150; // Pitch is in decided degrees (eg 150 = 15.0 degrees). 37 | uint16_t yaw = 2758; // Yaw is in decided degrees (eg 2758 = 275.8 degrees). 38 | 39 | /* Initialise the barometric altitude telemetry with default values. */ 40 | uint16_t baroAltitude = 10; // Barometric altitude is in decimetres (eg 10 = 1.0 metres). 41 | int16_t verticalSpeed = 50; // Vertical speed is in centimetres per second (eg 50 = 0.5 metres per second). 42 | 43 | /* Initialise the battery sensor telemetry with default values. */ 44 | float batteryVoltage = 385.0F; // Battery voltage is in millivolts (mV * 100). 45 | float batteryCurrent = 150.0F; // Battery current is in milliamps (mA * 10). 46 | uint32_t batteryFuel = 700; // Battery fuel is in milliamp hours (mAh). 47 | uint8_t batteryPercent = 50; // Battery percentage remaining is in percent (0 - 100). 48 | 49 | /* Initialise the GPS telemetry data with default values. */ 50 | float latitude = -41.18219482686493F; // Latitude is in decimal degrees. 51 | float longitude = 174.9497131419602F; // Longitude is in decimal degrees. 52 | float altitude = 100.0F; // Altitude is in centimetres. 53 | float speed = 500.0F; // Speed is in cm/s 54 | float groundCourse = 275.8F; // Ground Course is in degrees. 55 | uint8_t satellites = 7; // 7 satellites are in view (implies a 3D fix). 56 | 57 | CRSFforArduino crsf = CRSFforArduino(&Serial1); 58 | 59 | #if VIEW_RC_CHANNELS > 0 60 | const int channelCount = 8; 61 | const char *channelNames[crsfProtocol::RC_CHANNEL_COUNT] = { 62 | "A", "E", "T", "R", "Aux1", "Aux2", "Aux3", "Aux4", "Aux5", "Aux6", "Aux7", "Aux8", "Aux9", "Aux10", "Aux11", "Aux12"}; 63 | 64 | static_assert(channelCount <= crsfProtocol::RC_CHANNEL_COUNT, "The number of RC channels must be less than or equal to the maximum number of RC channels supported by CRSF."); 65 | #endif 66 | 67 | void setup() 68 | { 69 | #if VIEW_RC_CHANNELS > 0 || defined(CRSF_DEBUG) 70 | Serial.begin(115200); 71 | while (!Serial) 72 | { 73 | ; 74 | } 75 | 76 | Serial.println("GPS Telemetry Example"); 77 | #endif 78 | 79 | /* Initialise CRSF for Arduino */ 80 | if (!crsf.begin()) 81 | { 82 | #if VIEW_RC_CHANNELS > 0 || defined(CRSF_DEBUG) 83 | Serial.println("CRSF for Arduino initialization failed!"); 84 | #endif 85 | while (1) 86 | { 87 | ; 88 | } 89 | } 90 | 91 | #if VIEW_RC_CHANNELS > 0 || defined(CRSF_DEBUG) 92 | /* Show the user that the sketch is ready. */ 93 | Serial.println("Ready"); 94 | delay(1000); 95 | #endif 96 | } 97 | 98 | void loop() 99 | { 100 | 101 | // Use timeNow to store the current time in milliseconds. 102 | timeNow = millis(); 103 | 104 | /* Attitude Telemetry 105 | 106 | Normally, you would read the raw attitude data from your IMU and convert it to roll, pitch and yaw values. 107 | For the purposes of this example, we will just update the following with random values: 108 | - Roll 109 | - Pitch 110 | - Yaw 111 | 112 | These values are updated at a rate of 100 Hz. 113 | */ 114 | 115 | /* Update the attitude telemetry at a rate of 100 Hz. */ 116 | static unsigned long lastAttitudeUpdate = 0; 117 | if (timeNow - lastAttitudeUpdate >= 10) 118 | { 119 | lastAttitudeUpdate = timeNow; 120 | 121 | /* Update the attitude telemetry with the new values. */ 122 | crsf.telemetryWriteAttitude(roll, pitch, yaw); 123 | } 124 | 125 | /* Barometric Altitude Telemetry 126 | 127 | Normally, you would read the barometric altitude and vertical speed from a barometric pressure sensor 128 | connected to your Arduino board. 129 | 130 | For the purposes of this example, we will just update the following with random values: 131 | - Barometric Altitude 132 | - Vertical Speed 133 | 134 | These values are updated at a rate of 10 Hz. 135 | */ 136 | 137 | /* Update the barometric altitude telemetry at a rate of 10 Hz. */ 138 | static unsigned long lastBaroAltitudeUpdate = 0; 139 | if (timeNow - lastBaroAltitudeUpdate >= 100) 140 | { 141 | lastBaroAltitudeUpdate = timeNow; 142 | 143 | /* Update the barometric altitude telemetry with the new values. */ 144 | crsf.telemetryWriteBaroAltitude(baroAltitude, verticalSpeed); 145 | } 146 | 147 | /* Battery Telemetry 148 | 149 | Normally, you read the battery voltage and current from two analog pins on your Arduino board or from a 150 | battery sensor connected to your Arduino board. 151 | For the purposes of this example, we will just update the following with random values: 152 | - Battery Voltage 153 | - Battery Current 154 | 155 | These values are updated at a rate of 10 Hz. 156 | Battery fuel and percentage remaining are calculated from the battery voltage and current values, with 157 | a simulated battery capacity of 1000 mAh. */ 158 | 159 | /* Update the battery sensor telemetry at a rate of 10 Hz. */ 160 | static unsigned long lastBatteryUpdate = 0; 161 | if (timeNow - lastBatteryUpdate >= 100) 162 | { 163 | lastBatteryUpdate = timeNow; 164 | 165 | #if GENERATE_RANDOM_BATTERY_DATA > 0 166 | // Generate random values for the battery sensor telemetry. 167 | batteryVoltage = random(300, 420); 168 | batteryCurrent = random(0, 1000); 169 | #endif 170 | 171 | // Calculate the battery fuel and percentage remaining. 172 | // batteryFuel = (uint32_t)(batteryFuel + (batteryCurrent / 36000)); 173 | // batteryPercent = (uint8_t)(batteryFuel / 10); 174 | 175 | // Update the battery sensor telemetry with the new values. 176 | crsf.telemetryWriteBattery(batteryVoltage, batteryCurrent, batteryFuel, batteryPercent); 177 | } 178 | 179 | /* GPS Telemetry 180 | 181 | Normally, you would update the GPS telemetry data with the latest values from your GPS module. 182 | For the purposes of this example, we will just update the following with random values: 183 | - Latitude 184 | - Longitude 185 | - Altitude 186 | - Speed 187 | - Ground Course 188 | - Satellites 189 | 190 | These values are updated at a rate of 1 Hz. 191 | Normally, you would update these values at a rate of 5 Hz or higher. 192 | */ 193 | 194 | /* Update the GPS telemetry data at a rate of 1 Hz. */ 195 | static unsigned long lastGpsUpdate = 0; 196 | if (timeNow - lastGpsUpdate >= 1000) 197 | { 198 | lastGpsUpdate = timeNow; 199 | 200 | #if GENERATE_RANDOM_GPS_DATA > 0 201 | // Generate random values for the GPS telemetry data. 202 | latitude = random(-90, 90); 203 | longitude = random(-180, 180); 204 | altitude = random(0, 500000); 205 | speed = random(0, 6625); 206 | groundCourse = random(0, 359); 207 | satellites = random(0, 16); 208 | #endif 209 | 210 | // Update the GPS telemetry data with the new values. 211 | crsf.telemetryWriteGPS(latitude, longitude, altitude, speed, groundCourse, satellites); 212 | } 213 | 214 | /* Call crsf.update() in the main loop to process CRSF packets. */ 215 | crsf.update(); 216 | 217 | /* You can process RC channel data here. */ 218 | #if VIEW_RC_CHANNELS > 0 219 | static unsigned long lastPrint = 0; 220 | if (timeNow - lastPrint >= 100) 221 | { 222 | lastPrint = timeNow; 223 | Serial.print("RC Channels <"); 224 | for (uint8_t i = 0; i < channelCount; i++) 225 | { 226 | Serial.print(channelNames[i]); 227 | Serial.print(": "); 228 | Serial.print(crsf.rcToUs(crsf.getChannel(i + 1))); 229 | if (i < channelCount - 1) 230 | { 231 | Serial.print(", "); 232 | } 233 | } 234 | Serial.println(">"); 235 | } 236 | #endif 237 | } 238 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | # Syntax colouring map for CRSF for Arduino. 2 | 3 | # Namespaces (KEYWORD1) 4 | crsfProtocol KEYWORD1 5 | hal KEYWORD1 6 | genericStreamBuffer KEYWORD1 7 | serialReceiver KEYWORD1 8 | sketchLayer KEYWORD1 9 | 10 | # Datatypes (KEYWORD1) 11 | CRSF_DEBUG_SERIAL_PORT KEYWORD1 12 | crsf KEYWORD1 13 | CRSFforArduino KEYWORD1 14 | 15 | # Functions (KEYWORD2) 16 | begin KEYWORD2 17 | end KEYWORD2 18 | getChannel KEYWORD2 19 | rcToUs KEYWORD2 20 | readRcChannel KEYWORD2 21 | telemetryWriteAttitude KEYWORD2 22 | telemetryWriteBaroAltitude KEYWORD2 23 | telemetryWriteBattery KEYWORD2 24 | telemetryWriteGPS KEYWORD2 25 | setRcChannelsCallback KEYWORD2 26 | setLinkStatisticsCallback KEYWORD2 27 | setFlightModeCallback KEYWORD2 28 | setFlightMode KEYWORD2 29 | update KEYWORD2 30 | 31 | # Structures (KEYWORD3) 32 | 33 | # Constants (LITERAL1) 34 | BAUD_RATE LITERAL1 35 | CRSFFORARDUINO_VERSION LITERAL1 36 | CRSFFORARDUINO_VERSION_DATE LITERAL1 37 | CRSFFORARDUINO_VERSION_MAJOR LITERAL1 38 | CRSFFORARDUINO_VERSION_MINOR LITERAL1 39 | CRSFFORARDUINO_VERSION_PATCH LITERAL1 40 | CRSF_RC_ENABLED LITERAL1 41 | CRSF_RC_MAX_CHANNELS LITERAL1 42 | CRSF_RC_MAX_CHANNELS LITERAL1 43 | CRSF_RC_CHANNEL_MAX LITERAL1 44 | CRSF_RC_CHANNEL_CENTER LITERAL1 45 | CRSF_RC_INITIALISE_CHANNELS LITERAL1 46 | CRSF_RC_INITIALISE_THROTTLECHANNEL LITERAL1 47 | CRSF_TELEMETRY_ENABLED LITERAL1 48 | CRSF_TELEMETRY_ATTITUDE_ENABLED LITERAL1 49 | CRSF_TELEMETRY_BAROALTITUDE_ENABLED LITERAL1 50 | CRSF_TELEMETRY_BATTERY_ENABLED LITERAL1 51 | CRSF_TELEMETRY_GPS_ENABLED LITERAL1 52 | CRSF_DEBUG_ENABLED LITERAL1 53 | RC_CHANNEL_ROLL LITERAL1 54 | RC_CHANNEL_PITCH LITERAL1 55 | RC_CHANNEL_THROTTLE LITERAL1 56 | RC_CHANNEL_YAW LITERAL1 57 | RC_CHANNEL_AUX1 LITERAL1 58 | RC_CHANNEL_AUX2 LITERAL1 59 | RC_CHANNEL_AUX3 LITERAL1 60 | RC_CHANNEL_AUX4 LITERAL1 61 | RC_CHANNEL_AUX5 LITERAL1 62 | RC_CHANNEL_AUX6 LITERAL1 63 | RC_CHANNEL_AUX7 LITERAL1 64 | RC_CHANNEL_AUX8 LITERAL1 65 | RC_CHANNEL_AUX9 LITERAL1 66 | RC_CHANNEL_AUX10 LITERAL1 67 | RC_CHANNEL_AUX11 LITERAL1 68 | RC_CHANNEL_AUX12 LITERAL1 69 | RC_CHANNEL_COUNT LITERAL1 70 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/platformio/platformio-core/develop/platformio/assets/schema/library.json", 3 | "name": "CRSFforArduino", 4 | "version": "2025.9.2", 5 | "description": "An Arduino Library for communicating with ExpressLRS and TBS Crossfire receivers.", 6 | "keywords": "arduino, remote-control, arduino-library, protocols, rc, radio-control, crsf, expresslrs", 7 | "repository": 8 | { 9 | "type": "git", 10 | "url": "https://github.com/ZZ-Cat/CRSFforArduino.git" 11 | }, 12 | "authors": 13 | [ 14 | { 15 | "name": "Cassandra Robinson", 16 | "email": "nicad.heli.flier@gmail.com", 17 | "url": "https://github.com/ZZ-Cat", 18 | "maintainer": true 19 | } 20 | ], 21 | "license": "GNU AGPLv3", 22 | "homepage": "https://github.com/ZZ-Cat/CRSFforArduino", 23 | "dependencies": { 24 | }, 25 | "frameworks": "Arduino", 26 | "platforms": [ 27 | "atmelsam", 28 | "espressif32", 29 | "raspberrypi", 30 | "ststm32", 31 | "teensy" 32 | ], 33 | "examples": [ 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=CRSFforArduino 2 | version=2025.9.2 3 | author=Cassandra Robinson 4 | maintainer=Cassandra Robinson 5 | sentence=CRSF for Arduino brings the Crossfire Protocol to the Arduino ecosystem. 6 | category=Communication 7 | url=https://github.com/ZZ-Cat/CRSFforArduino 8 | dot_a_linkage=false 9 | includes=CRSFforArduino.hpp 10 | -------------------------------------------------------------------------------- /src/CFA_Config.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file CFA_Config.hpp 3 | * @author Cassandra "ZZ Cat" Robinson (nicad.heli.flier@gmail.com) 4 | * @brief This is the configuration file for CRSF for Arduino. 5 | * 6 | * @copyright Copyright (c) 2024, Cassandra "ZZ Cat" Robinson. All rights reserved. 7 | * 8 | * @section License GNU Affero General Public License v3.0 9 | * This source file is a part of the CRSF for Arduino library. 10 | * CRSF for Arduino is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * CRSF for Arduino is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with CRSF for Arduino. If not, see . 22 | * 23 | */ 24 | 25 | #pragma once 26 | 27 | #include "Arduino.h" 28 | 29 | /* The following defines are used to configure CRSF for Arduino. 30 | You can change these values to suit your needs. */ 31 | 32 | namespace crsfForArduinoConfig 33 | { 34 | /* CRSFforArduino version 35 | Versioning is based on a rolling release model 36 | and is backwards-compatible with Semantic Versioning 2.0.0. 37 | See https://semver.org/ for more information. */ 38 | #define CRSFFORARDUINO_VERSION "2025.9.2" 39 | #define CRSFFORARDUINO_VERSION_DATE "2025-09-02" 40 | #define CRSFFORARDUINO_VERSION_MAJOR 2025 41 | #define CRSFFORARDUINO_VERSION_MINOR 9 42 | #define CRSFFORARDUINO_VERSION_PATCH 2 43 | 44 | // This is set to 1 if the version is a pre-release version. 45 | #define CRSFFORARDUINO_VERSION_IS_PRERELEASE 0 46 | 47 | // These are the pre-release version details which are only used if CRSFFORARDUINO_VERSION_IS_PRERELEASE is set to 1. 48 | // NOTE: Pre-release versions are not recommended for production use. 49 | // #if CRSFFORARDUINO_VERSION_IS_PRERELEASE == 1 50 | // #define CRSFFORARDUINO_VERSION_PRE "2025.8.30" 51 | // #define CRSFFORARDUINO_VERSION_BUILD_DATE "2025-08-30" 52 | // #define CRSFFORARDUINO_VERSION_BUILD_MAJOR 2025 53 | // #define CRSFFORARDUINO_VERSION_BUILD_MINOR 8 54 | // #define CRSFFORARDUINO_VERSION_BUILD_PATCH 30 55 | // #endif 56 | 57 | /* Failsafe Options 58 | - CRSF_FAILSAFE_LQI_THRESHOLD: The minimum LQI value for the receiver to be considered connected. 59 | - CRSF_FAILSAFE_RSSI_THRESHOLD: The minimum RSSI value for the receiver to be considered connected. 60 | - NB: It is considered good practice to set this value to the same as the RSSI Sensitivity Limit in your Lua script. 61 | */ 62 | #define CRSF_FAILSAFE_LQI_THRESHOLD 80 63 | #define CRSF_FAILSAFE_RSSI_THRESHOLD 105 64 | 65 | /* RC Options 66 | - RC_ENABLED: Enables or disables the RC API. 67 | - RC_MAX_CHANNELS: The maximum number of RC channels to be received. 68 | - RC_CHANNEL_MIN: The minimum value of an RC channel. 69 | - RC_CHANNEL_MAX: The maximum value of an RC channel. 70 | - RC_CHANNEL_CENTER: The center value of an RC channel. 71 | - RC_INITIALISE_CHANNELS: Whether or not to initialise the RC channels to their center values. 72 | - RC_INITIALISE_ARMCHANNEL: When enabled, the arm channel is set to its minimum value. 73 | - NB: This refers to the Aux1 channel and is intended for use with ExpressLRS receivers. 74 | - RC_INITIALISE_THROTTLECHANNEL: When enabled, the throttle channel is set to its minimum value. */ 75 | #define CRSF_RC_ENABLED 1 76 | #define CRSF_RC_MAX_CHANNELS 16 77 | #define CRSF_RC_CHANNEL_MIN 172 78 | #define CRSF_RC_CHANNEL_MAX 1811 79 | #define CRSF_RC_CHANNEL_CENTER 992 80 | #define CRSF_RC_INITIALISE_CHANNELS 1 81 | #define CRSF_RC_INITIALISE_ARMCHANNEL 1 82 | #define CRSF_RC_INITIALISE_THROTTLECHANNEL 1 83 | 84 | /* Flight Modes 85 | Enables or disables the Flight Mode API. 86 | When enabled, you are given an event-driven API that allows you to easily implement flight modes 87 | and assign them to a switch on your controller. 88 | Pro Tip: You can combine the Flight Mode API with the Telemetry API to send flight mode 89 | information back to your controller. */ 90 | #define CRSF_FLIGHTMODES_ENABLED 0 91 | 92 | /* Custom Flight Modes 93 | Enables or disables the Custom Flight Modes. 94 | When enabled, this allows you to implement flight modes with custom names 95 | and assign them to a switch on your controller. */ 96 | #define CRSF_CUSTOM_FLIGHT_MODES_ENABLED 0 97 | 98 | /* Telemetry Options 99 | - TELEMETRY_ENABLED: Enables or disables the Telemetry API. 100 | - TELEMETRY_ATTITUDE_ENABLED: Enables or disables attitude telemetry output. 101 | - TELEMETRY_BAROALTITUDE_ENABLED: Enables or disables barometric altitude telemetry output. 102 | - TELEMETRY_BATTERY_ENABLED: Enables or disables battery telemetry output. 103 | - TELEMETRY_FLIGHTMODE_ENABLED: Enables or disables flight mode telemetry output. 104 | - TELEMETRY_GPS_ENABLED: Enables or disables GPS telemetry output. 105 | - TELEMETRY_SIMULATE_ARBITRARY_VALUES: When enabled, arbitrary values are sent for telemetry. */ 106 | #define CRSF_TELEMETRY_ENABLED 1 107 | 108 | #define CRSF_TELEMETRY_ATTITUDE_ENABLED 1 109 | #define CRSF_TELEMETRY_BAROALTITUDE_ENABLED 1 110 | #define CRSF_TELEMETRY_BATTERY_ENABLED 1 111 | 112 | #ifndef CRSF_TELEMETRY_FLIGHTMODE_ENABLED 113 | #define CRSF_TELEMETRY_FLIGHTMODE_ENABLED 0 114 | #endif 115 | 116 | #define CRSF_TELEMETRY_GPS_ENABLED 1 117 | 118 | #define CRSF_LINK_STATISTICS_ENABLED 1 119 | 120 | /* Debug Options 121 | - DEBUG_ENABLED: Enables or disables debug output over the selected serial port. 122 | - CRSF_DEBUG_SERIAL_PORT: The serial port to use for debug output. Usually the native USB port. 123 | - CRSF_DEBUG_ENABLE_COMPATIBILITY_TABLE_OUTPUT: Enables or disables debug output from the compatibility table. 124 | - CRSF_DEBUG_ENABLE_CONFIGURATION_DUMP: When enabled, this will print the configuration of CFA to the Serial Monitor. 125 | - CRSF_DEBUG_ENABLE_VERSION_OUTPUT: When enabled, this will print the version of CFA to the Serial Monitor. */ 126 | #define CRSF_DEBUG_ENABLED 0 127 | #define CRSF_DEBUG_SERIAL_PORT Serial 128 | #define CRSF_DEBUG_ENABLE_COMPATIBILITY_TABLE_OUTPUT 0 129 | #define CRSF_DEBUG_ENABLE_CONFIGURATION_DUMP 0 130 | #define CRSF_DEBUG_ENABLE_VERSION_OUTPUT 1 131 | 132 | /* All warnings and asserts below this point are to ensure that the configuration is valid. */ 133 | 134 | /* Compiler warning if both RC and Telemetry are disabled. */ 135 | #if CRSF_RC_ENABLED == 0 && CRSF_TELEMETRY_ENABLED == 0 136 | #warning "Both CRSF_RC_ENABLED and CRSF_TELEMETRY_ENABLED are disabled. CRSF for Arduino will not do anything." 137 | #endif 138 | 139 | /* Static assert if Flight Modes are enabled, but RC is disabled. */ 140 | #if CRSF_FLIGHTMODES_ENABLED == 1 && CRSF_RC_ENABLED == 0 141 | static_assert(false, "CRSF_FLIGHTMODES_ENABLED is enabled, but CRSF_RC_ENABLED is disabled. Flight Modes require RC to be enabled."); 142 | #endif 143 | 144 | /* Static assert if all telemetry options are disabled. 145 | Better to use CRSF_TELEMETRY_ENABLED instead. */ 146 | #if CRSF_TELEMETRY_ATTITUDE_ENABLED == 0 && CRSF_TELEMETRY_BAROALTITUDE_ENABLED == 0 && CRSF_TELEMETRY_BATTERY_ENABLED == 0 && CRSF_TELEMETRY_FLIGHTMODE_ENABLED == 0 && CRSF_TELEMETRY_GPS_ENABLED == 0 147 | static_assert(false, "All telemetry options are disabled. Set CRSF_TELEMETRY_ENABLED to 0 to disable telemetry instead."); 148 | #endif 149 | 150 | }; // namespace crsfForArduinoConfig 151 | -------------------------------------------------------------------------------- /src/CRSFforArduino.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file CRSFforArduino.cpp 3 | * @author Cassandra "ZZ Cat" Robinson (nicad.heli.flier@gmail.com) 4 | * @brief This is the Sketch Layer, which is a simplified API for CRSF for Arduino. 5 | * It is intended to be used by the user in their sketches. 6 | * 7 | * @copyright Copyright (c) 2024, Cassandra "ZZ Cat" Robinson. All rights reserved. 8 | * 9 | * @section License GNU Affero General Public License v3.0 10 | * This source file is a part of the CRSF for Arduino library. 11 | * CRSF for Arduino is free software: you can redistribute it and/or modify 12 | * it under the terms of the GNU Affero General Public License as published by 13 | * the Free Software Foundation, either version 3 of the License, or 14 | * (at your option) any later version. 15 | * 16 | * CRSF for Arduino is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU Affero General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU Affero General Public License 22 | * along with CRSF for Arduino. If not, see . 23 | * 24 | */ 25 | 26 | #include "CRSFforArduino.hpp" 27 | #include "Arduino.h" 28 | 29 | namespace sketchLayer 30 | { 31 | /** 32 | * @brief Construct a new CRSFforArduino object. 33 | * 34 | */ 35 | CRSFforArduino::CRSFforArduino() : SerialReceiver() 36 | { 37 | } 38 | 39 | /** 40 | * @brief Construct a new CRSFforArduino object with the specified serial port. 41 | * 42 | * @param serialPort 43 | */ 44 | CRSFforArduino::CRSFforArduino(HardwareSerial *serialPort) : SerialReceiver(serialPort) 45 | { 46 | } 47 | 48 | /** 49 | * @brief Construct a new CRSFforArduino object with the specified RX and TX pins. 50 | * 51 | * @param rxPin 52 | * @param txPin 53 | */ 54 | CRSFforArduino::CRSFforArduino(HardwareSerial *serialPort, int rxPin, int txPin) : SerialReceiver(serialPort, rxPin, txPin) 55 | { 56 | } 57 | 58 | /** 59 | * @brief Destroy the CRSFforArduino object. 60 | * 61 | */ 62 | CRSFforArduino::~CRSFforArduino() 63 | { 64 | } 65 | 66 | /** 67 | * @brief Initialises CRSF for Arduino. 68 | * 69 | * @return true if CRSF for Arduino was initialised successfully. 70 | */ 71 | bool CRSFforArduino::begin(const uint32_t baud_rate) 72 | { 73 | #if CRSF_DEBUG_ENABLED > 0 && CRSF_DEBUG_ENABLE_VERSION_OUTPUT > 0 74 | CRSF_DEBUG_SERIAL_PORT.println("[CRSF for Arduino | INFO]: "); 75 | CRSF_DEBUG_SERIAL_PORT.print("- Version: "); 76 | CRSF_DEBUG_SERIAL_PORT.print(CRSFFORARDUINO_VERSION); 77 | #if CRSFFORARDUINO_VERSION_IS_PRERELEASE > 0 78 | CRSF_DEBUG_SERIAL_PORT.print("-"); 79 | CRSF_DEBUG_SERIAL_PORT.println(CRSFFORARDUINO_VERSION_PRE); 80 | #endif 81 | CRSF_DEBUG_SERIAL_PORT.print("- Version Date: "); 82 | CRSF_DEBUG_SERIAL_PORT.println(CRSFFORARDUINO_VERSION_DATE); 83 | #if CRSFFORARDUINO_VERSION_IS_PRERELEASE > 0 84 | CRSF_DEBUG_SERIAL_PORT.print("- Build Date: "); 85 | CRSF_DEBUG_SERIAL_PORT.println(CRSFFORARDUINO_VERSION_BUILD_DATE); 86 | #endif 87 | #endif 88 | 89 | #if CRSF_RC_ENABLED > 0 || CRSF_TELEMETRY_ENABLED > 0 90 | return this->SerialReceiver::begin(baud_rate); 91 | #else 92 | // Return false if RC is disabled 93 | return false; 94 | #endif 95 | } 96 | 97 | /** 98 | * @brief Ends CRSF for Arduino. 99 | * 100 | */ 101 | void CRSFforArduino::end() 102 | { 103 | #if CRSF_RC_ENABLED > 0 || CRSF_TELEMETRY_ENABLED > 0 104 | this->SerialReceiver::end(); 105 | #endif 106 | } 107 | 108 | /** 109 | * @brief This processes RC and Telemetry frames. 110 | * It should be called as often as possible. 111 | * 112 | */ 113 | void CRSFforArduino::update() 114 | { 115 | #if CRSF_RC_ENABLED > 0 || CRSF_TELEMETRY_ENABLED > 0 116 | this->SerialReceiver::processFrames(); 117 | #endif 118 | 119 | #if CRSF_RC_ENABLED > 0 && CRSF_FLIGHTMODES_ENABLED > 0 120 | this->SerialReceiver::handleFlightMode(); 121 | #endif 122 | } 123 | 124 | /** 125 | * @brief Reads the specified RC channel. 126 | * @param channel The channel to read. 127 | * @param raw If true, returns the raw RC value. If false, returns the scaled RC value in microseconds. 128 | * 129 | * @return The RC value. 130 | */ 131 | [[deprecated("Use RC channel callback instead")]] uint16_t CRSFforArduino::readRcChannel(uint8_t channel, bool raw) 132 | { 133 | #if CRSF_RC_ENABLED > 0 134 | return this->SerialReceiver::readRcChannel(channel - 1, raw); 135 | #else 136 | // Prevent compiler warnings 137 | (void)channel; 138 | (void)raw; 139 | 140 | // Return 0 if RC is disabled 141 | return 0; 142 | #endif 143 | } 144 | 145 | /** 146 | * @brief Alias for readRcChannel(channel, true). 147 | * 148 | * @param channel The channel to read. 149 | * @return The RC value. 150 | */ 151 | [[deprecated("Use RC channel callback instead")]] uint16_t CRSFforArduino::getChannel(uint8_t channel) 152 | { 153 | #if CRSF_RC_ENABLED > 0 154 | return this->SerialReceiver::getChannel(channel - 1); 155 | #else 156 | // Prevent compiler warnings 157 | (void)channel; 158 | 159 | // Return 0 if RC is disabled 160 | return 0; 161 | #endif 162 | } 163 | 164 | /** 165 | * @brief Converts a raw RC value to microseconds. 166 | * 167 | * @param rc The raw RC value to convert. 168 | * @return The converted RC value in microseconds. 169 | */ 170 | uint16_t CRSFforArduino::rcToUs(uint16_t rc) 171 | { 172 | #if CRSF_RC_ENABLED > 0 173 | return this->SerialReceiver::rcToUs(rc); 174 | #else 175 | // Prevent compiler warnings 176 | (void)rc; 177 | 178 | // Return 0 if RC is disabled 179 | return 0; 180 | #endif 181 | } 182 | 183 | void CRSFforArduino::setRcChannelsCallback(void (*callback)(serialReceiverLayer::rcChannels_t *rcChannels)) 184 | { 185 | #if CRSF_RC_ENABLED > 0 186 | this->SerialReceiver::setRcChannelsCallback(callback); 187 | #else 188 | // Prevent compiler warnings 189 | (void)callback; 190 | #endif 191 | } 192 | 193 | void CRSFforArduino::setRawDataCallback(void (*callback)(int8_t byteReceived)) 194 | { 195 | this->SerialReceiver::setRawDataCallback(callback); 196 | } 197 | 198 | void CRSFforArduino::setLinkUpCallback(void (*callback)()) 199 | { 200 | this->SerialReceiver::setLinkUpCallback(callback); 201 | } 202 | 203 | void CRSFforArduino::setLinkDownCallback(void (*callback)()) 204 | { 205 | this->SerialReceiver::setLinkDownCallback(callback); 206 | } 207 | 208 | void CRSFforArduino::setLinkStatisticsCallback(void (*callback)(serialReceiverLayer::link_statistics_t linkStatistics)) 209 | { 210 | #if CRSF_LINK_STATISTICS_ENABLED > 0 211 | this->SerialReceiver::setLinkStatisticsCallback(callback); 212 | #else 213 | // Prevent compiler warnings 214 | (void)callback; 215 | #endif 216 | } 217 | 218 | /** 219 | * @brief Assigns a Flight Mode to the specified channel. 220 | * 221 | * @param flightModeId The ID of the Flight Mode to assign. 222 | * @param flightModeName The name of the Flight Mode to assign. 223 | * @param channel The channel to assign the Flight Mode to. 224 | * @param min The minimum RC value for the Flight Mode to be active. 225 | * @param max The maximum RC value for the Flight Mode to be active. 226 | * @return true if the Flight Mode was assigned successfully. 227 | */ 228 | bool CRSFforArduino::setFlightMode(serialReceiverLayer::flightModeId_t flightModeId, const char *flightModeName, uint8_t channel, uint16_t min, uint16_t max) 229 | { 230 | #if CRSF_RC_ENABLED > 0 && CRSF_FLIGHTMODES_ENABLED > 0 231 | return this->SerialReceiver::setFlightMode(flightModeId, flightModeName, channel - 1, this->SerialReceiver::usToRc(min), this->SerialReceiver::usToRc(max)); 232 | #else 233 | // Prevent compiler warnings 234 | (void)flightModeId; 235 | (void)flightModeName; 236 | (void)channel; 237 | (void)min; 238 | (void)max; 239 | 240 | // Return false if RC is disabled 241 | return false; 242 | #endif 243 | } 244 | 245 | /** 246 | * @brief Assigns a Flight Mode to the specified channel. 247 | * 248 | * @param flightMode The Flight Mode to assign. 249 | * @param channel The channel to assign the Flight Mode to. 250 | * @param min The minimum RC value for the Flight Mode to be active. 251 | * @param max The maximum RC value for the Flight Mode to be active. 252 | * @return true if the Flight Mode was assigned successfully. 253 | */ 254 | [[deprecated("This function must pass in the name of the Flight Mode as well as its ID.")]] bool CRSFforArduino::setFlightMode(serialReceiverLayer::flightModeId_t flightMode, uint8_t channel, uint16_t min, uint16_t max) 255 | { 256 | #if CRSF_RC_ENABLED > 0 && CRSF_FLIGHTMODES_ENABLED > 0 257 | return this->SerialReceiver::setFlightMode(flightMode, channel - 1, this->SerialReceiver::usToRc(min), this->SerialReceiver::usToRc(max)); 258 | #else 259 | // Prevent compiler warnings 260 | (void)flightMode; 261 | (void)channel; 262 | (void)min; 263 | (void)max; 264 | 265 | // Return false if RC is disabled 266 | return false; 267 | #endif 268 | } 269 | 270 | /** 271 | * @brief Registers a callback function to be called when a Flight Mode is activated. 272 | * This is called when the RC value for the Flight Mode channel is between the min and max values. 273 | * @param callback The callback function to register. 274 | */ 275 | void CRSFforArduino::setFlightModeCallback(void (*callback)(serialReceiverLayer::flightModeId_t flightMode)) 276 | { 277 | #if CRSF_RC_ENABLED > 0 && CRSF_FLIGHTMODES_ENABLED > 0 278 | this->SerialReceiver::setFlightModeCallback(callback); 279 | #else 280 | // Prevent compiler warnings 281 | (void)callback; 282 | #endif 283 | } 284 | 285 | /** 286 | * @brief Sends a CRSF Telemetry Frame with the current attitude data. 287 | * 288 | * @param roll In decidegrees (eg 15 degrees = 150). 289 | * @param pitch In decidegrees (eg 20 degrees = 200). 290 | * @param yaw In decidegrees (eg 30 degrees = 300). 291 | */ 292 | void CRSFforArduino::telemetryWriteAttitude(int16_t roll, int16_t pitch, int16_t yaw) 293 | { 294 | #if CRSF_TELEMETRY_ENABLED > 0 && CRSF_TELEMETRY_ATTITUDE_ENABLED > 0 295 | this->SerialReceiver::telemetryWriteAttitude(roll, pitch, yaw); 296 | #else 297 | // Prevent compiler warnings 298 | (void)roll; 299 | (void)pitch; 300 | (void)yaw; 301 | #endif 302 | } 303 | 304 | /** 305 | * @brief Sends a CRSF Telemetry Frame with the current barometric altitude data. 306 | * 307 | * @param altitude In decimeters (eg 1m = 10) 308 | * @param vario In centimetres per second (eg 1m/s = 100) 309 | */ 310 | void CRSFforArduino::telemetryWriteBaroAltitude(uint16_t altitude, int16_t vario) 311 | { 312 | #if CRSF_TELEMETRY_ENABLED > 0 && CRSF_TELEMETRY_BAROALTITUDE_ENABLED > 0 313 | this->SerialReceiver::telemetryWriteBaroAltitude(altitude, vario); 314 | #else 315 | // Prevent compiler warnings 316 | (void)altitude; 317 | (void)vario; 318 | #endif 319 | } 320 | 321 | /** 322 | * @brief Sends a CRSF Telemetry Frame with the current battery data. 323 | * 324 | * @param voltage In millivolts * 100 (eg 3.8V = 380.0F). 325 | * @param current In milliamps * 10 (eg 1.5A = 150.0F). 326 | * @param fuel In milliampere hours (eg 100 mAh = 100). 327 | * @param percent In percent (eg 50% = 50). 328 | */ 329 | void CRSFforArduino::telemetryWriteBattery(float voltage, float current, uint32_t fuel, uint8_t percent) 330 | { 331 | #if CRSF_TELEMETRY_ENABLED > 0 && CRSF_TELEMETRY_BATTERY_ENABLED > 0 332 | this->SerialReceiver::telemetryWriteBattery(voltage, current, fuel, percent); 333 | #else 334 | // Prevent compiler warnings 335 | (void)voltage; 336 | (void)current; 337 | (void)fuel; 338 | (void)percent; 339 | #endif 340 | } 341 | 342 | /** 343 | * @brief Sends a CRSF Telemetry Frame with the current Flight Mode. 344 | * 345 | * @param flightMode The Flight Mode to send. 346 | */ 347 | void CRSFforArduino::telemetryWriteFlightMode(serialReceiverLayer::flightModeId_t flightMode, bool disarmed) 348 | { 349 | #if CRSF_TELEMETRY_ENABLED > 0 && CRSF_TELEMETRY_FLIGHTMODE_ENABLED > 0 350 | this->SerialReceiver::telemetryWriteFlightMode(flightMode, disarmed); 351 | #else 352 | // Prevent compiler warnings 353 | (void)flightMode; 354 | (void)disarmed; 355 | #endif 356 | } 357 | 358 | /** 359 | * @brief Sends a CRSF Telemetry Frame with a custom Flight Mode string. 360 | * 361 | * @param flightMode The Flight Mode string to send. 362 | */ 363 | [[deprecated("This is nos handled automatically with telemetryWriteFlightMode()")]] void CRSFforArduino::telemetryWriteCustomFlightMode(const char *flightMode, bool armed) 364 | { 365 | #if CRSF_TELEMETRY_ENABLED > 0 && CRSF_TELEMETRY_FLIGHTMODE_ENABLED > 0 366 | this->SerialReceiver::telemetryWriteCustomFlightMode(flightMode, armed); 367 | #else 368 | // Prevent compiler warnings 369 | (void)flightMode; 370 | #endif 371 | } 372 | 373 | /** 374 | * @brief Sends a CRSF Telemetry Frame with the current GPS data. 375 | * 376 | * @param latitude In decimal degrees. 377 | * @param longitude In decimal degrees. 378 | * @param altitude In centimetres. 379 | * @param speed in centimeters per second. 380 | * @param groundCourse In degrees. 381 | * @param satellites In view. 382 | */ 383 | void CRSFforArduino::telemetryWriteGPS(float latitude, float longitude, float altitude, float speed, float groundCourse, uint8_t satellites) 384 | { 385 | #if CRSF_TELEMETRY_ENABLED > 0 && CRSF_TELEMETRY_GPS_ENABLED > 0 386 | this->SerialReceiver::telemetryWriteGPS(latitude, longitude, altitude, speed, groundCourse, satellites); 387 | #else 388 | // Prevent compiler warnings 389 | (void)latitude; 390 | (void)longitude; 391 | (void)altitude; 392 | (void)speed; 393 | (void)groundCourse; 394 | (void)satellites; 395 | #endif 396 | } 397 | } // namespace sketchLayer 398 | -------------------------------------------------------------------------------- /src/CRSFforArduino.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file CRSFforArduino.cpp 3 | * @author Cassandra "ZZ Cat" Robinson (nicad.heli.flier@gmail.com) 4 | * @brief This is the Sketch Layer, which is a simplified API for CRSF for Arduino. 5 | * It is intended to be used by the user in their sketches. 6 | * 7 | * @copyright Copyright (c) 2024, Cassandra "ZZ Cat" Robinson. All rights reserved. 8 | * 9 | * @section License GNU Affero General Public License v3.0 10 | * This source file is a part of the CRSF for Arduino library. 11 | * CRSF for Arduino is free software: you can redistribute it and/or modify 12 | * it under the terms of the GNU Affero General Public License as published by 13 | * the Free Software Foundation, either version 3 of the License, or 14 | * (at your option) any later version. 15 | * 16 | * CRSF for Arduino is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU Affero General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU Affero General Public License 22 | * along with CRSF for Arduino. If not, see . 23 | * 24 | */ 25 | 26 | #pragma once 27 | 28 | #include "Arduino.h" 29 | #include "SerialReceiver/SerialReceiver.hpp" 30 | 31 | namespace sketchLayer 32 | { 33 | class CRSFforArduino : private serialReceiverLayer::SerialReceiver 34 | { 35 | public: 36 | CRSFforArduino(); 37 | explicit CRSFforArduino(HardwareSerial *serialPort); 38 | CRSFforArduino(HardwareSerial *serialPort, int rxPin, int txPin); 39 | ~CRSFforArduino(); 40 | bool begin(const uint32_t baud_rate = crsfProtocol::BAUD_RATE); 41 | void end(); 42 | void update(); 43 | 44 | // RC channel functions. 45 | uint16_t getChannel(uint8_t channel); 46 | uint16_t rcToUs(uint16_t rc); 47 | uint16_t readRcChannel(uint8_t channel, bool raw = false); 48 | void setRcChannelsCallback(void (*callback)(serialReceiverLayer::rcChannels_t *rcChannels)); 49 | 50 | // Link statistics functions. 51 | void setLinkStatisticsCallback(void (*callback)(serialReceiverLayer::link_statistics_t linkStatistics)); 52 | 53 | // Flight mode functions. 54 | bool setFlightMode(serialReceiverLayer::flightModeId_t flightModeId, const char *flightModeName, uint8_t channel, uint16_t min, uint16_t max); 55 | bool setFlightMode(serialReceiverLayer::flightModeId_t flightMode, uint8_t channel, uint16_t min, uint16_t max); 56 | void setFlightModeCallback(void (*callback)(serialReceiverLayer::flightModeId_t flightMode)); 57 | void setRawDataCallback(void (*callback)(int8_t byteReceived)); 58 | 59 | void setLinkUpCallback(void (*callback)()); 60 | void setLinkDownCallback(void (*callback)()); 61 | 62 | // Telemetry functions. 63 | void telemetryWriteAttitude(int16_t roll, int16_t pitch, int16_t yaw); 64 | void telemetryWriteBaroAltitude(uint16_t altitude, int16_t vario); 65 | void telemetryWriteBattery(float voltage, float current, uint32_t fuel, uint8_t percent); 66 | void telemetryWriteFlightMode(serialReceiverLayer::flightModeId_t flightMode, bool disarmed = false); 67 | void telemetryWriteCustomFlightMode(const char *flightMode, bool armed = false); 68 | void telemetryWriteGPS(float latitude, float longitude, float altitude, float speed, float groundCourse, uint8_t satellites); 69 | 70 | private: 71 | }; 72 | } // namespace sketchLayer 73 | 74 | using namespace sketchLayer; 75 | -------------------------------------------------------------------------------- /src/SerialReceiver/CRC/CRC.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file CRC.cpp 3 | * @author Cassandra "ZZ Cat" Robinson (nicad.heli.flier@gmail.com) 4 | * @brief A generic CRC8 implementation for the CRSF for Arduino library. 5 | * 6 | * @copyright Copyright (c) 2024, Cassandra "ZZ Cat" Robinson. All rights reserved. 7 | * 8 | * @section License GNU Affero General Public License v3.0 9 | * This source file is a part of the CRSF for Arduino library. 10 | * CRSF for Arduino is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * CRSF for Arduino is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with CRSF for Arduino. If not, see . 22 | * 23 | */ 24 | 25 | #include "CRC.hpp" 26 | #include "stdlib.h" 27 | 28 | namespace genericCrc 29 | { 30 | GenericCRC::GenericCRC() 31 | { 32 | #if (CRC_OPTIMISATION_LEVEL == CRC_OPTIMISATION_SPEED) 33 | crc_8_dvb_s2_table = new uint8_t[256]; 34 | 35 | for (uint16_t i = 0; i < 256; i++) 36 | { 37 | uint8_t crc = i; 38 | for (uint8_t j = 0; j < 8; j++) 39 | { 40 | if (crc & 0x80) 41 | { 42 | crc = (crc << 1) ^ 0xd5; 43 | } 44 | else 45 | { 46 | crc <<= 1; 47 | } 48 | } 49 | 50 | crc_8_dvb_s2_table[i] = crc & 0xff; 51 | } 52 | #endif 53 | } 54 | 55 | /* GenericCRC copy constructor. */ 56 | GenericCRC::GenericCRC(const GenericCRC &other) 57 | { 58 | #if (CRC_OPTIMISATION_LEVEL == CRC_OPTIMISATION_SPEED) 59 | crc_8_dvb_s2_table = new uint8_t[256]; 60 | for (uint16_t i = 0; i < 256; i++) 61 | { 62 | crc_8_dvb_s2_table[i] = other.crc_8_dvb_s2_table[i]; 63 | } 64 | #endif 65 | } 66 | 67 | /* GenericCRC operator= */ 68 | GenericCRC &GenericCRC::operator=(const GenericCRC &other) 69 | { 70 | if (this != &other) 71 | { 72 | #if (CRC_OPTIMISATION_LEVEL == CRC_OPTIMISATION_SPEED) 73 | crc_8_dvb_s2_table = other.crc_8_dvb_s2_table; 74 | #endif 75 | } 76 | return *this; 77 | } 78 | 79 | GenericCRC::~GenericCRC() 80 | { 81 | #if (CRC_OPTIMISATION_LEVEL == CRC_OPTIMISATION_SPEED) 82 | delete[] crc_8_dvb_s2_table; 83 | crc_8_dvb_s2_table = nullptr; 84 | #endif 85 | } 86 | 87 | #if (CRC_OPTIMISATION_LEVEL == CRC_OPTIMISATION_SIZE) 88 | uint8_t GenericCRC::crc_8_dvb_s2(uint8_t crc, uint8_t data) 89 | { 90 | crc ^= data; 91 | for (uint8_t i = 0; i < 8; i++) 92 | { 93 | if (crc & 0x80) 94 | { 95 | crc = (crc << 1) ^ 0xd5; 96 | } 97 | else 98 | { 99 | crc <<= 1; 100 | } 101 | } 102 | return crc; 103 | } 104 | #endif 105 | 106 | uint8_t GenericCRC::calculate(uint8_t start, const uint8_t *data, uint8_t length) 107 | { 108 | #if (CRC_OPTIMISATION_LEVEL == CRC_OPTIMISATION_SPEED) 109 | uint8_t crc = crc_8_dvb_s2_table[0 ^ start]; 110 | 111 | for (uint8_t i = 0; i < length; i++) 112 | { 113 | crc = crc_8_dvb_s2_table[crc ^ data[i]]; 114 | } 115 | 116 | return crc; 117 | #elif (CRC_OPTIMISATION_LEVEL == CRC_OPTIMISATION_SIZE) 118 | uint8_t crc = crc_8_dvb_s2(0, start); 119 | 120 | for (uint8_t i = 0; i < length; i++) 121 | { 122 | crc = crc_8_dvb_s2(crc, data[i]); 123 | } 124 | 125 | return crc; 126 | #elif (CRC_OPTIMISATION_LEVEL == CRC_OPTIMISATION_HARDWARE) 127 | #error "CRC_OPTIMISATION_LEVEL is set to CRC_OPTIMISATION_HARDWARE, but no hardware implementation is available." 128 | #endif 129 | } 130 | 131 | uint8_t GenericCRC::calculate(uint8_t offset, uint8_t start, const uint8_t *data, uint8_t length) 132 | { 133 | (void)start; 134 | #if (CRC_OPTIMISATION_LEVEL == CRC_OPTIMISATION_SPEED) 135 | uint8_t crc = crc_8_dvb_s2_table[0 ^ data[offset]]; 136 | 137 | for (uint8_t i = offset + 1; i < length; i++) 138 | { 139 | crc = crc_8_dvb_s2_table[crc ^ data[i]]; 140 | } 141 | 142 | return crc; 143 | #elif (CRC_OPTIMISATION_LEVEL == CRC_OPTIMISATION_SIZE) 144 | uint8_t crc = crc_8_dvb_s2(0, data[offset]); 145 | 146 | for (uint8_t i = offset + 1; i < length; i++) 147 | { 148 | crc = crc_8_dvb_s2(crc, data[i]); 149 | } 150 | 151 | return crc; 152 | #elif (CRC_OPTIMISATION_LEVEL == CRC_OPTIMISATION_HARDWARE) 153 | #error "CRC_OPTIMISATION_LEVEL is set to CRC_OPTIMISATION_HARDWARE, but no hardware implementation is available." 154 | #endif 155 | } 156 | } // namespace genericCrc 157 | -------------------------------------------------------------------------------- /src/SerialReceiver/CRC/CRC.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file GenericCRC.hpp 3 | * @author Cassandra "ZZ Cat" Robinson (nicad.heli.flier@gmail.com) 4 | * @brief A generic CRC8 implementation for the CRSF for Arduino library. 5 | * 6 | * @copyright Copyright (c) 2024, Cassandra "ZZ Cat" Robinson. All rights reserved. 7 | * 8 | * @section License GNU Affero General Public License v3.0 9 | * This source file is a part of the CRSF for Arduino library. 10 | * CRSF for Arduino is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * CRSF for Arduino is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with CRSF for Arduino. If not, see . 22 | * 23 | */ 24 | 25 | #pragma once 26 | 27 | #include "stdint.h" 28 | 29 | namespace genericCrc 30 | { 31 | #define CRC_OPTIMISATION_SPEED 0 32 | #define CRC_OPTIMISATION_SIZE 1 33 | #define CRC_OPTIMISATION_HARDWARE 2 34 | 35 | class GenericCRC 36 | { 37 | public: 38 | GenericCRC(); 39 | /* GenericCRC copy constructor. */ 40 | GenericCRC(const GenericCRC &other); 41 | /* GenericCRC operator= */ 42 | GenericCRC &operator=(const GenericCRC &other); 43 | virtual ~GenericCRC(); 44 | 45 | uint8_t calculate(uint8_t start, const uint8_t *data, uint8_t length); 46 | uint8_t calculate(uint8_t offset, uint8_t start, const uint8_t *data, uint8_t length); 47 | 48 | private: 49 | #if (CRC_OPTIMISATION_LEVEL == CRC_OPTIMISATION_SPEED) 50 | uint8_t *crc_8_dvb_s2_table; 51 | #elif (CRC_OPTIMISATION_LEVEL == CRC_OPTIMISATION_SIZE) 52 | uint8_t crc_8_dvb_s2(uint8_t crc, uint8_t data); 53 | #endif 54 | }; 55 | } // namespace genericCrc 56 | -------------------------------------------------------------------------------- /src/SerialReceiver/CRSF/CRSF.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file CRSF.cpp 3 | * @author Cassandra "ZZ Cat" Robinson (nicad.heli.flier@gmail.com) 4 | * @brief This decodes CRSF frames from a serial port. 5 | * 6 | * @copyright Copyright (c) 2024, Cassandra "ZZ Cat" Robinson. All rights reserved. 7 | * 8 | * @section License GNU Affero General Public License v3.0 9 | * This source file is a part of the CRSF for Arduino library. 10 | * CRSF for Arduino is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * CRSF for Arduino is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with CRSF for Arduino. If not, see . 22 | * 23 | */ 24 | 25 | #include "CRSF.hpp" 26 | #include "Arduino.h" 27 | 28 | using namespace crsfProtocol; 29 | using namespace genericCrc; 30 | 31 | namespace serialReceiverLayer 32 | { 33 | CRSF::CRSF() 34 | { 35 | rcFrameReceived = false; 36 | frameCount = 0; 37 | timePerFrame = 0; 38 | 39 | crc8 = new GenericCRC(); 40 | } 41 | 42 | CRSF::CRSF(const CRSF &crsf) 43 | { 44 | rcFrameReceived = crsf.rcFrameReceived; 45 | frameCount = crsf.frameCount; 46 | timePerFrame = crsf.timePerFrame; 47 | 48 | memcpy(rxFrame.raw, crsf.rxFrame.raw, CRSF_FRAME_SIZE_MAX); 49 | memcpy(rcChannelsFrame.raw, crsf.rcChannelsFrame.raw, CRSF_FRAME_SIZE_MAX); 50 | 51 | crc8 = new GenericCRC(*crsf.crc8); 52 | } 53 | 54 | CRSF &CRSF::operator=(const CRSF &crsf) 55 | { 56 | if (this != &crsf) 57 | { 58 | rcFrameReceived = crsf.rcFrameReceived; 59 | frameCount = crsf.frameCount; 60 | timePerFrame = crsf.timePerFrame; 61 | 62 | memcpy(rxFrame.raw, crsf.rxFrame.raw, CRSF_FRAME_SIZE_MAX); 63 | memcpy(rcChannelsFrame.raw, crsf.rcChannelsFrame.raw, CRSF_FRAME_SIZE_MAX); 64 | 65 | *crc8 = *crsf.crc8; 66 | } 67 | 68 | return *this; 69 | } 70 | 71 | CRSF::~CRSF() 72 | { 73 | delete crc8; 74 | crc8 = nullptr; 75 | } 76 | 77 | void CRSF::begin() 78 | { 79 | rcFrameReceived = false; 80 | frameCount = 0; 81 | timePerFrame = 0; 82 | 83 | memset(rxFrame.raw, 0, CRSF_FRAME_SIZE_MAX); 84 | memset(rcChannelsFrame.raw, 0, CRSF_FRAME_SIZE_MAX); 85 | } 86 | 87 | void CRSF::end() 88 | { 89 | memset(rcChannelsFrame.raw, 0, CRSF_FRAME_SIZE_MAX); 90 | memset(rxFrame.raw, 0, CRSF_FRAME_SIZE_MAX); 91 | 92 | timePerFrame = 0; 93 | frameCount = 0; 94 | rcFrameReceived = false; 95 | } 96 | 97 | void CRSF::setFrameTime(uint32_t baudRate, uint8_t packetCount) 98 | { 99 | /* Calculate the time per frame based on the baud rate and packet count. */ 100 | timePerFrame = ((1000000 * packetCount) / (baudRate / (CRSF_FRAME_SIZE_MAX - 1))); 101 | } 102 | 103 | bool CRSF::receiveFrames(uint8_t rxByte) 104 | { 105 | static uint8_t framePosition = 0; 106 | static uint32_t frameStartTime = 0; 107 | const uint32_t currentTime = micros(); 108 | 109 | /* Reset the frame position if the frame time has expired. */ 110 | if (currentTime - frameStartTime > timePerFrame) 111 | { 112 | framePosition = 0; 113 | 114 | if (currentTime < frameStartTime) 115 | { 116 | frameStartTime = currentTime; 117 | } 118 | } 119 | 120 | if (framePosition == 0) 121 | { 122 | frameStartTime = currentTime; 123 | } 124 | 125 | /* Assume the full frame length is 5 bytes until the frame length byte is received. */ 126 | const int fullFrameLength = framePosition < 3 ? 5 : min(rxFrame.frame.frameLength + CRSF_FRAME_LENGTH_ADDRESS + CRSF_FRAME_LENGTH_FRAMELENGTH, (int)CRSF_FRAME_SIZE_MAX); 127 | 128 | if (framePosition < fullFrameLength) 129 | { 130 | /* Store the received byte in the frame buffer. */ 131 | rxFrame.raw[framePosition] = rxByte; 132 | framePosition++; 133 | 134 | if (framePosition >= fullFrameLength) 135 | { 136 | /* Frame is complete, calculate the CRC and check if it is valid. */ 137 | const uint8_t crc = calculateFrameCRC(); 138 | 139 | if (crc == rxFrame.raw[fullFrameLength - 1]) 140 | { 141 | switch (rxFrame.frame.type) 142 | { 143 | case crsfProtocol::CRSF_FRAMETYPE_RC_CHANNELS_PACKED: 144 | if (rxFrame.frame.deviceAddress == CRSF_ADDRESS_FLIGHT_CONTROLLER) 145 | { 146 | memcpy(&rcChannelsFrame, &rxFrame, CRSF_FRAME_SIZE_MAX); 147 | rcFrameReceived = true; 148 | } 149 | break; 150 | 151 | #if CRSF_LINK_STATISTICS_ENABLED > 0 152 | case CRSF_FRAMETYPE_LINK_STATISTICS: 153 | if ((rxFrame.frame.deviceAddress == CRSF_ADDRESS_FLIGHT_CONTROLLER) && (rxFrame.frame.frameLength == CRSF_FRAME_ORIGIN_DEST_SIZE + CRSF_FRAME_LINK_STATISTICS_PAYLOAD_SIZE)) 154 | { 155 | crsf_payload_link_statistics_t linkStatisticsPayload; 156 | memcpy(&linkStatisticsPayload, rxFrame.frame.payload, sizeof(crsf_payload_link_statistics_t)); 157 | 158 | linkStatistics.rssi = (linkStatisticsPayload.active_antenna ? linkStatisticsPayload.uplink_rssi_2 : linkStatisticsPayload.uplink_rssi_1); 159 | linkStatistics.lqi = linkStatisticsPayload.uplink_link_quality; 160 | linkStatistics.snr = linkStatisticsPayload.uplink_snr; 161 | linkStatistics.tx_power = (linkStatisticsPayload.uplink_tx_power < 9) ? tx_power_table[linkStatisticsPayload.uplink_tx_power] : 0; 162 | } 163 | break; 164 | #endif 165 | } 166 | } 167 | 168 | /* Clear the frame buffer and reset the frame position. */ 169 | memset(rxFrame.raw, 0, CRSF_FRAME_SIZE_MAX); 170 | framePosition = 0; 171 | return true; 172 | } 173 | } 174 | 175 | return false; 176 | } 177 | 178 | void CRSF::getFailSafe(bool *failSafe) 179 | { 180 | /* Set the failsafe flag based on the link statistics thresholds. */ 181 | if (linkStatistics.lqi <= CRSF_FAILSAFE_LQI_THRESHOLD || linkStatistics.rssi >= CRSF_FAILSAFE_RSSI_THRESHOLD) 182 | { 183 | *failSafe = true; 184 | } 185 | else 186 | { 187 | *failSafe = false; 188 | } 189 | } 190 | 191 | void CRSF::getRcChannels(uint16_t *rcChannels) 192 | { 193 | /* Decode RC frames if one has been received. */ 194 | if (rcFrameReceived) 195 | { 196 | rcFrameReceived = false; 197 | if (rcChannelsFrame.frame.type == CRSF_FRAMETYPE_RC_CHANNELS_PACKED) 198 | { 199 | rcChannelsPacked_t rcChannelsPacked; 200 | memcpy(&rcChannelsPacked, rcChannelsFrame.frame.payload, sizeof(rcChannelsPacked_t)); 201 | 202 | rcChannels[RC_CHANNEL_ROLL] = rcChannelsPacked.channel0; 203 | rcChannels[RC_CHANNEL_PITCH] = rcChannelsPacked.channel1; 204 | rcChannels[RC_CHANNEL_THROTTLE] = rcChannelsPacked.channel2; 205 | rcChannels[RC_CHANNEL_YAW] = rcChannelsPacked.channel3; 206 | rcChannels[RC_CHANNEL_AUX1] = rcChannelsPacked.channel4; 207 | rcChannels[RC_CHANNEL_AUX2] = rcChannelsPacked.channel5; 208 | rcChannels[RC_CHANNEL_AUX3] = rcChannelsPacked.channel6; 209 | rcChannels[RC_CHANNEL_AUX4] = rcChannelsPacked.channel7; 210 | rcChannels[RC_CHANNEL_AUX5] = rcChannelsPacked.channel8; 211 | rcChannels[RC_CHANNEL_AUX6] = rcChannelsPacked.channel9; 212 | rcChannels[RC_CHANNEL_AUX7] = rcChannelsPacked.channel10; 213 | rcChannels[RC_CHANNEL_AUX8] = rcChannelsPacked.channel11; 214 | rcChannels[RC_CHANNEL_AUX9] = rcChannelsPacked.channel12; 215 | rcChannels[RC_CHANNEL_AUX10] = rcChannelsPacked.channel13; 216 | rcChannels[RC_CHANNEL_AUX11] = rcChannelsPacked.channel14; 217 | rcChannels[RC_CHANNEL_AUX12] = rcChannelsPacked.channel15; 218 | } 219 | } 220 | } 221 | 222 | void CRSF::getLinkStatistics(link_statistics_t *linkStats) 223 | { 224 | #if CRSF_LINK_STATISTICS_ENABLED > 0 225 | memcpy(linkStats, &linkStatistics, sizeof(link_statistics_t)); 226 | #else 227 | (void)linkStats; 228 | #endif 229 | } 230 | 231 | uint8_t CRSF::calculateFrameCRC() 232 | { 233 | return crc8->calculate(rxFrame.frame.type, rxFrame.frame.payload, rxFrame.frame.frameLength - CRSF_FRAME_LENGTH_TYPE_CRC); 234 | } 235 | } // namespace serialReceiverLayer 236 | -------------------------------------------------------------------------------- /src/SerialReceiver/CRSF/CRSF.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file CRSF.hpp 3 | * @author Cassandra "ZZ Cat" Robinson (nicad.heli.flier@gmail.com) 4 | * @brief This decodes CRSF frames from a serial port. 5 | * 6 | * @copyright Copyright (c) 2024, Cassandra "ZZ Cat" Robinson. All rights reserved. 7 | * 8 | * @section License GNU Affero General Public License v3.0 9 | * This source file is a part of the CRSF for Arduino library. 10 | * CRSF for Arduino is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * CRSF for Arduino is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with CRSF for Arduino. If not, see . 22 | * 23 | */ 24 | 25 | #pragma once 26 | 27 | #include "../CRC/CRC.hpp" 28 | #include "CRSFProtocol.hpp" 29 | 30 | namespace serialReceiverLayer 31 | { 32 | typedef struct link_statistics_s 33 | { 34 | int16_t rssi = 0; 35 | int16_t lqi = 0; 36 | int16_t snr = 0; 37 | int16_t tx_power = 0; 38 | } link_statistics_t; 39 | 40 | const uint16_t tx_power_table[9] = { 41 | 0, // 0 mW 42 | 10, // 10 mW 43 | 25, // 25 mW 44 | 100, // 100 mW 45 | 500, // 500 mW 46 | 1000, // 1 W 47 | 2000, // 2 W 48 | 250, // 250 mW 49 | 50 // 50 mW 50 | }; 51 | 52 | class CRSF 53 | { 54 | public: 55 | CRSF(); 56 | CRSF(const CRSF &crsf); 57 | CRSF &operator=(const CRSF &crsf); 58 | virtual ~CRSF(); 59 | void begin(); 60 | void end(); 61 | void setFrameTime(uint32_t baudRate, uint8_t packetCount = 10); 62 | bool receiveFrames(uint8_t rxByte); 63 | void getFailSafe(bool *failSafe); 64 | void getRcChannels(uint16_t *rcChannels); 65 | void getLinkStatistics(link_statistics_t *linkStats); 66 | 67 | private: 68 | bool rcFrameReceived; 69 | uint16_t frameCount; 70 | uint32_t timePerFrame; 71 | crsfProtocol::frame_t rxFrame; 72 | crsfProtocol::frame_t rcChannelsFrame; 73 | link_statistics_t linkStatistics; 74 | genericCrc::GenericCRC *crc8 = nullptr; 75 | uint8_t calculateFrameCRC(); 76 | }; 77 | } // namespace serialReceiverLayer 78 | -------------------------------------------------------------------------------- /src/SerialReceiver/CRSF/CRSFProtocol.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file CRSFProtocol.hpp 3 | * @author Cassandra "ZZ Cat" Robinson (nicad.heli.flier@gmail.com) 4 | * @brief This file contains enums and structs for the CRSF protocol. 5 | * 6 | * @copyright Copyright (c) 2024, Cassandra "ZZ Cat" Robinson. All rights reserved. 7 | * 8 | * @section License GNU Affero General Public License v3.0 9 | * This source file is a part of the CRSF for Arduino library. 10 | * CRSF for Arduino is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * CRSF for Arduino is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with CRSF for Arduino. If not, see . 22 | * 23 | */ 24 | 25 | #include "stdint.h" 26 | 27 | #pragma once 28 | 29 | #ifndef ENV_DEFECT_DETECTOR 30 | #include "../../CFA_Config.hpp" 31 | #endif 32 | 33 | namespace crsfProtocol 34 | { 35 | enum rc_channels_e 36 | { 37 | RC_CHANNEL_ROLL = 0, 38 | RC_CHANNEL_PITCH, 39 | RC_CHANNEL_THROTTLE, 40 | RC_CHANNEL_YAW, 41 | RC_CHANNEL_AUX1, 42 | RC_CHANNEL_AUX2, 43 | RC_CHANNEL_AUX3, 44 | RC_CHANNEL_AUX4, 45 | RC_CHANNEL_AUX5, 46 | RC_CHANNEL_AUX6, 47 | RC_CHANNEL_AUX7, 48 | RC_CHANNEL_AUX8, 49 | RC_CHANNEL_AUX9, 50 | RC_CHANNEL_AUX10, 51 | RC_CHANNEL_AUX11, 52 | RC_CHANNEL_AUX12, 53 | RC_CHANNEL_COUNT 54 | }; 55 | 56 | enum syncByte_e 57 | { 58 | CRSF_SYNC_BYTE = 0xC8, 59 | }; 60 | 61 | enum frameSize_e 62 | { 63 | CRSF_FRAME_SIZE_MAX = 64, 64 | CRSF_PAYLOAD_SIZE_MAX = CRSF_FRAME_SIZE_MAX - 6 65 | }; 66 | 67 | enum payloadSize_e 68 | { 69 | CRSF_FRAME_GPS_PAYLOAD_SIZE = 15, 70 | CRSF_FRAME_VARIO_PAYLOAD_SIZE = 2, 71 | CRSF_FRAME_BARO_ALTITUDE_PAYLOAD_SIZE = 4, // TBS is 2, ExpressLRS is 4 (combines vario) 72 | CRSF_FRAME_BATTERY_SENSOR_PAYLOAD_SIZE = 8, 73 | CRSF_FRAME_DEVICE_INFO_PAYLOAD_SIZE = 48, 74 | CRSF_FRAME_FLIGHT_MODE_PAYLOAD_SIZE = 16, 75 | CRSF_FRAME_HEARTBEAT_PAYLOAD_SIZE = 2, 76 | CRSF_FRAME_LINK_STATISTICS_PAYLOAD_SIZE = 10, 77 | CRSF_FRAME_LINK_STATISTICS_TX_PAYLOAD_SIZE = 6, 78 | CRSF_FRAME_RC_CHANNELS_PAYLOAD_SIZE = 22, 79 | CRSF_FRAME_ATTITUDE_PAYLOAD_SIZE = 6 80 | }; 81 | 82 | enum frameLength_e 83 | { 84 | CRSF_FRAME_LENGTH_ADDRESS = 1, // Length of the address field in bytes. 85 | CRSF_FRAME_LENGTH_FRAMELENGTH = 1, // Length of the frame length field in bytes. 86 | CRSF_FRAME_LENGTH_TYPE = 1, // Length of the type field in bytes. 87 | CRSF_FRAME_LENGTH_CRC = 1, // Length of the CRC field in bytes. 88 | CRSF_FRAME_LENGTH_TYPE_CRC = CRSF_FRAME_LENGTH_TYPE + CRSF_FRAME_LENGTH_CRC, // Length of the type and CRC fields in bytes. 89 | CRSF_FRAME_LENGTH_EXT_TYPE_CRC = 4, // Length of the extended Dest/Origin, type, and CRC fields in bytes. 90 | CRSF_FRAME_LENGTH_NON_PAYLOAD = 4 // Combined length of all fields except the payload in bytes. 91 | }; 92 | 93 | typedef enum frameType_e 94 | { 95 | CRSF_FRAMETYPE_GPS = 0x02, 96 | CRSF_FRAMETYPE_VARIO = 0x07, 97 | CRSF_FRAMETYPE_BATTERY_SENSOR = 0x08, 98 | CRSF_FRAMETYPE_BARO_ALTITUDE = 0x09, 99 | CRSF_FRAMETYPE_HEARTBEAT = 0x0B, 100 | CRSF_FRAMETYPE_LINK_STATISTICS = 0x14, 101 | CRSF_FRAMETYPE_RC_CHANNELS_PACKED = 0x16, 102 | CRSF_FRAMETYPE_SUBSET_RC_CHANNELS_PACKED = 0x17, 103 | CRSF_FRAMETYPE_LINK_STATISTICS_RX = 0x1C, 104 | CRSF_FRAMETYPE_LINK_STATISTICS_TX = 0x1D, 105 | CRSF_FRAMETYPE_ATTITUDE = 0x1E, 106 | CRSF_FRAMETYPE_FLIGHT_MODE = 0x21, 107 | 108 | CRSF_FRAMETYPE_DEVICE_PING = 0x28, 109 | CRSF_FRAMETYPE_DEVICE_INFO = 0x29, 110 | CRSF_FRAMETYPE_PARAMETER_SETTINGS_ENTRY = 0x2B, 111 | CRSF_FRAMETYPE_PARAMETER_READ = 0x2C, 112 | CRSF_FRAMETYPE_PARAMETER_WRITE = 0x2D, 113 | CRSF_FRAMETYPE_COMMAND = 0x32, 114 | 115 | CRSF_FRAMETYPE_MSP_REQ = 0x7A, 116 | CRSF_FRAMETYPE_MSP_RESP = 0x7B, 117 | CRSF_FRAMETYPE_MSP_WRITE = 0x7C, 118 | CRSF_FRAMETYPE_DISPLAYPORT_CMD = 0x7D, 119 | } frameType_t; 120 | 121 | #if CRSF_TELEMETRY_ENABLED == 1 || CRSF_LINK_STATISTICS_ENABLED == 1 122 | #define CRSF_FRAME_ORIGIN_DEST_SIZE 2 123 | #endif 124 | 125 | typedef enum address_e 126 | { 127 | CRSF_ADDRESS_BROADCAST = 0x00, 128 | CRSF_ADDRESS_USB = 0x10, 129 | CRSF_ADDRESS_TBS_CORE_PNP_PRO = 0x80, 130 | CRSF_ADDRESS_RESERVED1 = 0x8A, 131 | CRSF_ADDRESS_CURRENT_SENSOR = 0xC0, 132 | CRSF_ADDRESS_GPS = 0xC2, 133 | CRSF_ADDRESS_TBS_BLACKBOX = 0xC4, 134 | CRSF_ADDRESS_FLIGHT_CONTROLLER = 0xC8, 135 | CRSF_ADDRESS_RESERVED2 = 0xCA, 136 | CRSF_ADDRESS_RACE_TAG = 0xCC, 137 | CRSF_ADDRESS_RADIO_TRANSMITTER = 0xEA, 138 | CRSF_ADDRESS_CRSF_RECEIVER = 0xEC, 139 | CRSF_ADDRESS_CRSF_TRANSMITTER = 0xEE 140 | } address_t; 141 | 142 | // Schedule array to send telemetry frames. 143 | typedef enum 144 | { 145 | CRSF_TELEMETRY_FRAME_START_INDEX = 0, 146 | CRSF_TELEMETRY_FRAME_ATTITUDE_INDEX, 147 | CRSF_TELEMETRY_FRAME_BARO_ALTITUDE_INDEX, 148 | CRSF_TELEMETRY_FRAME_BATTERY_SENSOR_INDEX, 149 | CRSF_TELEMETRY_FRAME_FLIGHT_MODE_INDEX, 150 | CRSF_TELEMETRY_FRAME_GPS_INDEX, 151 | // CRSF_TELEMETRY_FRAME_HEARTBEAT_INDEX, 152 | // CRSF_TELEMETRY_FRAME_VARIO_INDEX, 153 | CRSF_TELEMETRY_FRAME_SCHEDULE_MAX 154 | } telemetryFrame_t; 155 | 156 | // RC Channels Packed. 22 bytes (11 bits per channel, 16 channels) total. 157 | struct rcChannelsPacked_s 158 | { 159 | uint16_t channel0 : 11; 160 | uint16_t channel1 : 11; 161 | uint16_t channel2 : 11; 162 | uint16_t channel3 : 11; 163 | uint16_t channel4 : 11; 164 | uint16_t channel5 : 11; 165 | uint16_t channel6 : 11; 166 | uint16_t channel7 : 11; 167 | uint16_t channel8 : 11; 168 | uint16_t channel9 : 11; 169 | uint16_t channel10 : 11; 170 | uint16_t channel11 : 11; 171 | uint16_t channel12 : 11; 172 | uint16_t channel13 : 11; 173 | uint16_t channel14 : 11; 174 | uint16_t channel15 : 11; 175 | } __attribute__((packed)); 176 | 177 | typedef struct rcChannelsPacked_s rcChannelsPacked_t; 178 | 179 | typedef struct frameDefinition_s 180 | { 181 | uint8_t deviceAddress; // Frame address. 182 | uint8_t frameLength; // Frame length. Includes payload and CRC. 183 | uint8_t type; // Frame type. 184 | uint8_t payload[CRSF_PAYLOAD_SIZE_MAX + CRSF_FRAME_LENGTH_CRC]; // Frame payload. 185 | } frameDefinition_t; 186 | 187 | typedef union frame_u 188 | { 189 | uint8_t raw[CRSF_FRAME_SIZE_MAX]; 190 | frameDefinition_t frame; 191 | } frame_t; 192 | 193 | #if CRSF_LINK_STATISTICS_ENABLED > 0 194 | // Link Statistics frame. 195 | // Uplink is the connection from the transmitter to the receiver. Downlink is in the opposite direction. 196 | typedef struct crsf_payload_link_statistics_s 197 | { 198 | uint8_t uplink_rssi_1; // Uplink RSSI Antenna 1 (dBm * -1) 199 | uint8_t uplink_rssi_2; // Uplink RSSI Antenna 2 (dBm * -1) 200 | uint8_t uplink_link_quality; // Uplink Link Quality/Packet Success Rate (%) 201 | int8_t uplink_snr; // Uplink Signal-to-Noise Ratio (dB) 202 | uint8_t active_antenna; // Active Antenna (0 = Antenna 1, 1 = Antenna 2) 203 | uint8_t rf_mode; // RF Mode (4 fps = 0, 50 fps = 1, 150 fps = 2, 250 fps = 3, 500 fps = 4, 1000 fps = 5) 204 | uint8_t uplink_tx_power; // Uplink TX Power (0 mW = 0, 10 mW = 1, 25 mW = 2, 100 mW = 3, 250 mW = 4, 500 mW = 5, 1000 mW = 6, 2000 mW = 7) 205 | uint8_t downlink_rssi; // Downlink RSSI (dBm * -1) 206 | uint8_t downlink_link_quality; // Downlink Link Quality/Packet Success Rate (%) 207 | int8_t downlink_snr; // Downlink Signal-to-Noise Ratio (dB) 208 | } crsf_payload_link_statistics_t; 209 | #endif 210 | 211 | // Attitude Data to pass to the telemetry frame. 212 | typedef struct attitudeData_s 213 | { 214 | int16_t roll; // Roll angle in radians. 215 | int16_t pitch; // Pitch angle in radians. 216 | int16_t yaw; // Yaw angle in radians. 217 | } attitudeData_t; 218 | 219 | // Barometric Altitude and Variometer Data to pass to the telemetry frame. 220 | typedef struct baroAltitudeData_s 221 | { 222 | uint16_t altitude; // Altitude in decimeters + 10000 or metres if high bit is set. 223 | int16_t vario; // Variometer in centimeters per second. 224 | } baroAltitudeData_t; 225 | 226 | // Battery Sensor Data to pass to the telemetry frame. 227 | typedef struct batterySensorData_s 228 | { 229 | uint16_t voltage; // Average battery cell voltage. 230 | uint16_t current; // Amperage. 231 | uint32_t capacity; // mAh drawn. 232 | uint8_t percent; // Battery % remaining. 233 | } batterySensorData_t; 234 | 235 | // Flight Mode Data to pass to the telemetry frame. 236 | typedef struct flightModeData_s 237 | { 238 | char flightMode[CRSF_FRAME_FLIGHT_MODE_PAYLOAD_SIZE]; // Flight mode string. 239 | } flightModeData_t; 240 | 241 | // GPS Data to pass to the telemetry frame. 242 | typedef struct gpsData_s 243 | { 244 | int32_t latitude; 245 | int32_t longitude; 246 | uint16_t altitude; 247 | uint16_t speed; 248 | uint16_t groundCourse; 249 | uint8_t satellites; 250 | } gpsData_t; 251 | 252 | // Struct to hold data for the telemetry frame. 253 | typedef struct telemetryData_s 254 | { 255 | attitudeData_t attitude; 256 | baroAltitudeData_t baroAltitude; 257 | batterySensorData_t battery; 258 | flightModeData_t flightMode; 259 | gpsData_t gps; 260 | } telemetryData_t; 261 | 262 | enum baudRate_e 263 | { 264 | BAUD_RATE_LEGACY = 420000, 265 | BAUD_RATE = 416666 266 | }; 267 | } // namespace crsfProtocol 268 | -------------------------------------------------------------------------------- /src/SerialReceiver/SerialBuffer/SerialBuffer.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file SerialBuffer.cpp 3 | * @author Cassandra "ZZ Cat" Robinson (nicad.heli.flier@gmail.com) 4 | * @brief A generic serial buffer for the CRSF for Arduino library. 5 | * 6 | * @copyright Copyright (c) 2024, Cassandra "ZZ Cat" Robinson. All rights reserved. 7 | * 8 | * @section License GNU Affero General Public License v3.0 9 | * This source file is a part of the CRSF for Arduino library. 10 | * CRSF for Arduino is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * CRSF for Arduino is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with CRSF for Arduino. If not, see . 22 | * 23 | */ 24 | 25 | #include "SerialBuffer.hpp" 26 | #include "cstring" 27 | 28 | namespace genericStreamBuffer 29 | { 30 | SerialBuffer::SerialBuffer(size_t size) 31 | { 32 | bufferIndex = 0; 33 | bufferLength = 0; 34 | bufferSizeMax = size; 35 | buffer = new uint8_t[bufferSizeMax]; 36 | 37 | memset(buffer, 0, bufferSizeMax); 38 | } 39 | 40 | SerialBuffer::SerialBuffer(const SerialBuffer &serialBuffer) 41 | { 42 | bufferIndex = serialBuffer.bufferIndex; 43 | bufferLength = serialBuffer.bufferLength; 44 | bufferSizeMax = serialBuffer.bufferSizeMax; 45 | buffer = new uint8_t[bufferSizeMax]; 46 | 47 | memcpy(buffer, serialBuffer.buffer, bufferSizeMax); 48 | } 49 | 50 | SerialBuffer &SerialBuffer::operator=(const SerialBuffer &serialBuffer) 51 | { 52 | if (this != &serialBuffer) 53 | { 54 | bufferIndex = serialBuffer.bufferIndex; 55 | bufferLength = serialBuffer.bufferLength; 56 | bufferSizeMax = serialBuffer.bufferSizeMax; 57 | 58 | delete[] buffer; 59 | buffer = new uint8_t[bufferSizeMax]; 60 | 61 | memcpy(buffer, serialBuffer.buffer, bufferSizeMax); 62 | } 63 | 64 | return *this; 65 | } 66 | 67 | SerialBuffer::~SerialBuffer() 68 | { 69 | delete[] buffer; 70 | bufferIndex = 0; 71 | bufferLength = 0; 72 | bufferSizeMax = 0; 73 | } 74 | 75 | void SerialBuffer::reset() 76 | { 77 | bufferIndex = 0; 78 | bufferLength = 0; 79 | memset(buffer, 0, bufferSizeMax); 80 | } 81 | 82 | size_t SerialBuffer::write8(int8_t value) 83 | { 84 | if (bufferIndex + 1 > bufferSizeMax) 85 | { 86 | return 0; 87 | } 88 | 89 | buffer[bufferIndex++] = value; 90 | bufferLength = bufferIndex; 91 | 92 | return 1; 93 | } 94 | 95 | size_t SerialBuffer::write16(int16_t value) 96 | { 97 | if (bufferIndex + 2 > bufferSizeMax) 98 | { 99 | return 0; 100 | } 101 | 102 | buffer[bufferIndex++] = value & 0xFF; 103 | buffer[bufferIndex++] = (value >> 8) & 0xFF; 104 | bufferLength = bufferIndex; 105 | 106 | return 2; 107 | } 108 | 109 | size_t SerialBuffer::write32(int32_t value) 110 | { 111 | if (bufferIndex + 4 > bufferSizeMax) 112 | { 113 | return 0; 114 | } 115 | 116 | buffer[bufferIndex++] = value & 0xFF; 117 | buffer[bufferIndex++] = (value >> 8) & 0xFF; 118 | buffer[bufferIndex++] = (value >> 16) & 0xFF; 119 | buffer[bufferIndex++] = (value >> 24) & 0xFF; 120 | bufferLength = bufferIndex; 121 | 122 | return 4; 123 | } 124 | 125 | size_t SerialBuffer::writeU8(uint8_t value) 126 | { 127 | if (bufferIndex + 1 > bufferSizeMax) 128 | { 129 | return 0; 130 | } 131 | 132 | buffer[bufferIndex++] = value; 133 | bufferLength = bufferIndex; 134 | 135 | return 1; 136 | } 137 | 138 | size_t SerialBuffer::writeU16(uint16_t value) 139 | { 140 | if (bufferIndex + 2 > bufferSizeMax) 141 | { 142 | return 0; 143 | } 144 | 145 | buffer[bufferIndex++] = value & 0xFF; 146 | buffer[bufferIndex++] = (value >> 8) & 0xFF; 147 | bufferLength = bufferIndex; 148 | 149 | return 2; 150 | } 151 | 152 | size_t SerialBuffer::writeU32(uint32_t value) 153 | { 154 | if (bufferIndex + 4 > bufferSizeMax) 155 | { 156 | return 0; 157 | } 158 | 159 | buffer[bufferIndex++] = value & 0xFF; 160 | buffer[bufferIndex++] = (value >> 8) & 0xFF; 161 | buffer[bufferIndex++] = (value >> 16) & 0xFF; 162 | buffer[bufferIndex++] = (value >> 24) & 0xFF; 163 | bufferLength = bufferIndex; 164 | 165 | return 4; 166 | } 167 | 168 | size_t SerialBuffer::write8BE(int8_t value) 169 | { 170 | if (bufferIndex + 1 > bufferSizeMax) 171 | { 172 | return 0; 173 | } 174 | 175 | buffer[bufferIndex++] = value; 176 | bufferLength = bufferIndex; 177 | 178 | return 1; 179 | } 180 | 181 | size_t SerialBuffer::write16BE(int16_t value) 182 | { 183 | if (bufferIndex + 2 > bufferSizeMax) 184 | { 185 | return 0; 186 | } 187 | 188 | buffer[bufferIndex++] = (value >> 8) & 0xFF; 189 | buffer[bufferIndex++] = value & 0xFF; 190 | bufferLength = bufferIndex; 191 | 192 | return 2; 193 | } 194 | 195 | size_t SerialBuffer::write32BE(int32_t value) 196 | { 197 | if (bufferIndex + 4 > bufferSizeMax) 198 | { 199 | return 0; 200 | } 201 | 202 | buffer[bufferIndex++] = (value >> 24) & 0xFF; 203 | buffer[bufferIndex++] = (value >> 16) & 0xFF; 204 | buffer[bufferIndex++] = (value >> 8) & 0xFF; 205 | buffer[bufferIndex++] = value & 0xFF; 206 | bufferLength = bufferIndex; 207 | 208 | return 4; 209 | } 210 | 211 | size_t SerialBuffer::writeU8BE(uint8_t value) 212 | { 213 | if (bufferIndex + 1 > bufferSizeMax) 214 | { 215 | return 0; 216 | } 217 | 218 | buffer[bufferIndex++] = value; 219 | bufferLength = bufferIndex; 220 | 221 | return 1; 222 | } 223 | 224 | size_t SerialBuffer::writeU16BE(uint16_t value) 225 | { 226 | if (bufferIndex + 2 > bufferSizeMax) 227 | { 228 | return 0; 229 | } 230 | 231 | buffer[bufferIndex++] = (value >> 8) & 0xFF; 232 | buffer[bufferIndex++] = value & 0xFF; 233 | bufferLength = bufferIndex; 234 | 235 | return 2; 236 | } 237 | 238 | size_t SerialBuffer::writeU24BE(uint32_t value) 239 | { 240 | if (bufferIndex + 3 > bufferSizeMax) 241 | { 242 | return 0; 243 | } 244 | 245 | buffer[bufferIndex++] = (value >> 16) & 0xFF; 246 | buffer[bufferIndex++] = (value >> 8) & 0xFF; 247 | buffer[bufferIndex++] = value & 0xFF; 248 | bufferLength = bufferIndex; 249 | 250 | return 3; 251 | } 252 | 253 | size_t SerialBuffer::writeU32BE(uint32_t value) 254 | { 255 | if (bufferIndex + 4 > bufferSizeMax) 256 | { 257 | return 0; 258 | } 259 | 260 | buffer[bufferIndex++] = (value >> 24) & 0xFF; 261 | buffer[bufferIndex++] = (value >> 16) & 0xFF; 262 | buffer[bufferIndex++] = (value >> 8) & 0xFF; 263 | buffer[bufferIndex++] = value & 0xFF; 264 | bufferLength = bufferIndex; 265 | 266 | return 4; 267 | } 268 | 269 | size_t SerialBuffer::writeString(const char *string) 270 | { 271 | size_t length = strlen(string); 272 | 273 | if (bufferIndex + length > bufferSizeMax) 274 | { 275 | return 0; 276 | } 277 | 278 | memcpy(buffer + bufferIndex, string, length); 279 | bufferIndex += length; 280 | bufferLength = bufferIndex; 281 | 282 | return length; 283 | } 284 | 285 | size_t SerialBuffer::getLength() 286 | { 287 | return bufferLength; 288 | } 289 | 290 | size_t SerialBuffer::getMaxSize() 291 | { 292 | return bufferSizeMax; 293 | } 294 | 295 | size_t SerialBuffer::getIndex() 296 | { 297 | return bufferIndex; 298 | } 299 | 300 | uint8_t SerialBuffer::getByte(size_t index) 301 | { 302 | if (index >= bufferSizeMax) 303 | { 304 | return 0; 305 | } 306 | 307 | return buffer[index]; 308 | } 309 | 310 | uint8_t *SerialBuffer::getBuffer() 311 | { 312 | return buffer; 313 | } 314 | } // namespace genericStreamBuffer 315 | -------------------------------------------------------------------------------- /src/SerialReceiver/SerialBuffer/SerialBuffer.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file SerialBuffer.hpp 3 | * @author Cassandra "ZZ Cat" Robinson (nicad.heli.flier@gmail.com) 4 | * @brief A generic serial buffer for the CRSF for Arduino library. 5 | * 6 | * @copyright Copyright (c) 2024, Cassandra "ZZ Cat" Robinson. All rights reserved. 7 | * 8 | * @section License GNU Affero General Public License v3.0 9 | * This source file is a part of the CRSF for Arduino library. 10 | * CRSF for Arduino is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * CRSF for Arduino is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with CRSF for Arduino. If not, see . 22 | * 23 | */ 24 | 25 | #pragma once 26 | 27 | #include "stddef.h" 28 | #include "stdint.h" 29 | 30 | namespace genericStreamBuffer 31 | { 32 | class SerialBuffer 33 | { 34 | public: 35 | explicit SerialBuffer(size_t size = 64); 36 | SerialBuffer(const SerialBuffer &serialBuffer); 37 | SerialBuffer &operator=(const SerialBuffer &serialBuffer); 38 | ~SerialBuffer(); 39 | 40 | void reset(); 41 | 42 | size_t write8(int8_t value); 43 | size_t write16(int16_t value); 44 | size_t write32(int32_t value); 45 | 46 | size_t writeU8(uint8_t value); 47 | size_t writeU16(uint16_t value); 48 | size_t writeU32(uint32_t value); 49 | 50 | size_t write8BE(int8_t value); 51 | size_t write16BE(int16_t value); 52 | size_t write32BE(int32_t value); 53 | 54 | size_t writeU8BE(uint8_t value); 55 | size_t writeU16BE(uint16_t value); 56 | size_t writeU24BE(uint32_t value); 57 | size_t writeU32BE(uint32_t value); 58 | 59 | size_t writeString(const char *string); 60 | 61 | size_t getLength(); 62 | 63 | size_t getMaxSize(); 64 | 65 | size_t getIndex(); 66 | 67 | uint8_t getByte(size_t index); 68 | 69 | uint8_t *getBuffer(); 70 | 71 | private: 72 | size_t bufferSizeMax; 73 | size_t bufferLength; 74 | size_t bufferIndex; 75 | uint8_t *buffer; 76 | }; 77 | } // namespace genericStreamBuffer 78 | -------------------------------------------------------------------------------- /src/SerialReceiver/SerialReceiver.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file SerialReceiver.hpp 3 | * @author Cassandra "ZZ Cat" Robinson (nicad.heli.flier@gmail.com) 4 | * @brief The Serial Receiver layer for the CRSF for Arduino library. 5 | * 6 | * @copyright Copyright (c) 2024, Cassandra "ZZ Cat" Robinson. All rights reserved. 7 | * 8 | * @section License GNU Affero General Public License v3.0 9 | * This source file is a part of the CRSF for Arduino library. 10 | * CRSF for Arduino is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * CRSF for Arduino is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with CRSF for Arduino. If not, see . 22 | * 23 | */ 24 | 25 | #pragma once 26 | 27 | #ifndef ENV_DEFECT_DETECTOR 28 | #include "../CFA_Config.hpp" 29 | #endif 30 | #include "Arduino.h" 31 | #include "CRSF/CRSF.hpp" 32 | #include "Telemetry/Telemetry.hpp" 33 | 34 | namespace serialReceiverLayer 35 | { 36 | typedef enum flightModeId_e 37 | { 38 | FLIGHT_MODE_DISARMED = 0, 39 | FLIGHT_MODE_ACRO, 40 | FLIGHT_MODE_WAIT, 41 | FLIGHT_MODE_FAILSAFE, 42 | FLIGHT_MODE_GPS_RESCUE, 43 | FLIGHT_MODE_PASSTHROUGH, 44 | FLIGHT_MODE_ANGLE, 45 | FLIGHT_MODE_HORIZON, 46 | FLIGHT_MODE_AIRMODE, 47 | 48 | #if CRSF_CUSTOM_FLIGHT_MODES_ENABLED > 0 49 | CUSTOM_FLIGHT_MODE1, 50 | CUSTOM_FLIGHT_MODE2, 51 | CUSTOM_FLIGHT_MODE3, 52 | CUSTOM_FLIGHT_MODE4, 53 | CUSTOM_FLIGHT_MODE5, 54 | CUSTOM_FLIGHT_MODE6, 55 | CUSTOM_FLIGHT_MODE7, 56 | CUSTOM_FLIGHT_MODE8, 57 | #endif 58 | FLIGHT_MODE_COUNT 59 | } flightModeId_t; 60 | 61 | typedef struct rcChannels_s 62 | { 63 | bool valid; 64 | bool failsafe; 65 | uint16_t value[crsfProtocol::RC_CHANNEL_COUNT]; 66 | } rcChannels_t; 67 | 68 | /* Function pointers for callbacks. */ 69 | typedef void (*rcChannelsCallback_t)(rcChannels_t *); 70 | typedef void (*flightModeCallback_t)(flightModeId_t); 71 | typedef void (*linkStatisticsCallback_t)(link_statistics_t); 72 | typedef void (*rawDataCallback_t)(int8_t); 73 | 74 | typedef void (*linkUpCallback_t)(); 75 | typedef void (*linkDownCallback_t)(); 76 | 77 | class SerialReceiver 78 | { 79 | public: 80 | static const unsigned int CRSF_FAILSAFE_STAGE1_MS = 300; 81 | 82 | SerialReceiver(); 83 | explicit SerialReceiver(HardwareSerial *hwUartPort); 84 | SerialReceiver(HardwareSerial *hwUartPort, int8_t rxPin, int8_t txPin); 85 | SerialReceiver(const SerialReceiver &serialReceiver); 86 | SerialReceiver &operator=(const SerialReceiver &serialReceiver); 87 | virtual ~SerialReceiver(); 88 | 89 | bool begin(const uint32_t baudRate); 90 | void end(); 91 | 92 | #if CRSF_RC_ENABLED > 0 || CRSF_TELEMETRY_ENABLED > 0 || CRSF_LINK_STATISTICS_ENABLED > 0 93 | void processFrames(); 94 | #endif 95 | 96 | 97 | 98 | #if CRSF_LINK_STATISTICS_ENABLED > 0 99 | void setLinkStatisticsCallback(linkStatisticsCallback_t callback); 100 | #endif 101 | 102 | #if CRSF_RC_ENABLED > 0 103 | void setRcChannelsCallback(rcChannelsCallback_t callback); 104 | uint16_t getChannel(uint8_t channel); 105 | uint16_t rcToUs(uint16_t rc); 106 | uint16_t usToRc(uint16_t us); 107 | uint16_t readRcChannel(uint8_t channel, bool raw = false); 108 | 109 | #if CRSF_FLIGHTMODES_ENABLED > 0 110 | bool setFlightMode(flightModeId_t flightModeId, const char *flightModeName, uint8_t channel, uint16_t min, uint16_t max); 111 | bool setFlightMode(flightModeId_t flightMode, uint8_t channel, uint16_t min, uint16_t max); 112 | void setFlightModeCallback(flightModeCallback_t callback); 113 | void handleFlightMode(); 114 | #endif 115 | #endif 116 | 117 | void setRawDataCallback(rawDataCallback_t callback); 118 | void setLinkDownCallback(linkDownCallback_t callback); 119 | void setLinkUpCallback(linkUpCallback_t callback); 120 | bool isLinkUp() const; 121 | void checkLinkDown(); 122 | void setLinkUp(); 123 | 124 | #if CRSF_TELEMETRY_ENABLED > 0 125 | void telemetryWriteAttitude(int16_t roll, int16_t pitch, int16_t yaw); 126 | void telemetryWriteBaroAltitude(uint16_t altitude, int16_t vario); 127 | void telemetryWriteBattery(float voltage, float current, uint32_t fuel, uint8_t percent); 128 | void telemetryWriteFlightMode(flightModeId_t flightMode, bool disarmed = false); 129 | void telemetryWriteCustomFlightMode(const char *flightMode, bool armed = true); 130 | void telemetryWriteGPS(float latitude, float longitude, float altitude, float speed, float groundCourse, uint8_t satellites); 131 | #endif 132 | 133 | private: 134 | CRSF *crsf = nullptr; 135 | HardwareSerial *_uart; 136 | 137 | int8_t _rxPin = -1; 138 | int8_t _txPin = -1; 139 | bool _linkIsUp; 140 | uint32_t _lastChannelsPacket; 141 | 142 | #if CRSF_TELEMETRY_ENABLED > 0 143 | Telemetry *telemetry = nullptr; 144 | #endif 145 | 146 | #if CRSF_RC_ENABLED > 0 147 | rcChannels_t *_rcChannels = nullptr; 148 | rcChannelsCallback_t _rcChannelsCallback = nullptr; 149 | #endif 150 | 151 | #if CRSF_TELEMETRY_ENABLED > 0 152 | const char *flightModeStr = "ACRO"; 153 | #endif 154 | 155 | #if CRSF_LINK_STATISTICS_ENABLED > 0 156 | link_statistics_t _linkStatistics; 157 | linkStatisticsCallback_t _linkStatisticsCallback = nullptr; 158 | #endif 159 | 160 | #if CRSF_RC_ENABLED > 0 && CRSF_FLIGHTMODES_ENABLED > 0 161 | typedef struct flightMode_s 162 | { 163 | const char *name = nullptr; 164 | uint8_t channel = 0; 165 | uint16_t min = 0; 166 | uint16_t max = 0; 167 | } flightMode_t; 168 | 169 | flightMode_t *_flightModes = nullptr; 170 | flightModeCallback_t _flightModeCallback = nullptr; 171 | #endif 172 | 173 | #if CRSF_RC_ENABLED > 0 || CRSF_TELEMETRY_ENABLED > 0 174 | void flushRemainingFrames(); 175 | #endif 176 | 177 | rawDataCallback_t _rawDataCallback = nullptr; 178 | linkUpCallback_t _linkUpCallback = nullptr; 179 | linkDownCallback_t _linkDownCallback = nullptr; 180 | }; 181 | } // namespace serialReceiverLayer 182 | -------------------------------------------------------------------------------- /src/SerialReceiver/Telemetry/Telemetry.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Telemetry.cpp 3 | * @author Cassandra "ZZ Cat" Robinson (nicad.heli.flier@gmail.com) 4 | * @brief This encodes data into CRSF telemetry frames for transmission to the RC handset. 5 | * 6 | * @copyright Copyright (c) 2024, Cassandra "ZZ Cat" Robinson. All rights reserved. 7 | * 8 | * @section License GNU Affero General Public License v3.0 9 | * This source file is a part of the CRSF for Arduino library. 10 | * CRSF for Arduino is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * CRSF for Arduino is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with CRSF for Arduino. If not, see . 22 | * 23 | */ 24 | 25 | #include "Telemetry.hpp" 26 | #ifndef ENV_DEFECT_DETECTOR 27 | #include "CFA_Config.hpp" 28 | #endif 29 | 30 | using namespace crsfProtocol; 31 | 32 | namespace serialReceiverLayer 33 | { 34 | #ifndef PI 35 | #define PI 3.1415926535897932384626433832795F 36 | #endif 37 | 38 | #ifndef RAD 39 | #define RAD PI / 180.0F 40 | #endif 41 | 42 | Telemetry::Telemetry() : 43 | SerialBuffer(CRSF_FRAME_SIZE_MAX) 44 | { 45 | _telemetryFrameScheduleCount = 0; 46 | memset(_telemetryFrameSchedule, 0, sizeof(_telemetryFrameSchedule)); 47 | memset(&_telemetryData, 0, sizeof(_telemetryData)); 48 | } 49 | 50 | Telemetry::~Telemetry() 51 | { 52 | } 53 | 54 | void Telemetry::begin() 55 | { 56 | SerialBuffer::reset(); 57 | 58 | uint8_t index = 0; 59 | #if CRSF_TELEMETRY_ENABLED > 0 && CRSF_TELEMETRY_ATTITUDE_ENABLED > 0 60 | _telemetryFrameSchedule[index++] = (1 << CRSF_TELEMETRY_FRAME_ATTITUDE_INDEX); 61 | #endif 62 | 63 | #if CRSF_TELEMETRY_ENABLED > 0 && CRSF_TELEMETRY_BAROALTITUDE_ENABLED > 0 64 | _telemetryFrameSchedule[index++] = (1 << CRSF_TELEMETRY_FRAME_BARO_ALTITUDE_INDEX); 65 | #endif 66 | 67 | #if CRSF_TELEMETRY_ENABLED > 0 && CRSF_TELEMETRY_BATTERY_ENABLED > 0 68 | _telemetryFrameSchedule[index++] = (1 << CRSF_TELEMETRY_FRAME_BATTERY_SENSOR_INDEX); 69 | #endif 70 | 71 | #if CRSF_TELEMETRY_ENABLED > 0 && CRSF_TELEMETRY_FLIGHTMODE_ENABLED > 0 72 | _telemetryFrameSchedule[index++] = (1 << CRSF_TELEMETRY_FRAME_FLIGHT_MODE_INDEX); 73 | #endif 74 | 75 | #if CRSF_TELEMETRY_ENABLED > 0 && CRSF_TELEMETRY_GPS_ENABLED > 0 76 | _telemetryFrameSchedule[index++] = (1 << CRSF_TELEMETRY_FRAME_GPS_INDEX); 77 | #endif 78 | 79 | _telemetryFrameScheduleCount = index; 80 | } 81 | 82 | void Telemetry::end() 83 | { 84 | SerialBuffer::reset(); 85 | } 86 | 87 | bool Telemetry::update() 88 | { 89 | #if CRSF_TELEMETRY_ENABLED > 0 90 | bool sendFrame = false; 91 | 92 | static uint8_t scheduleIndex = 0; 93 | const uint8_t currentSchedule = _telemetryFrameSchedule[scheduleIndex]; 94 | 95 | #if CRSF_TELEMETRY_ATTITUDE_ENABLED > 0 96 | if (currentSchedule & (1 << CRSF_TELEMETRY_FRAME_ATTITUDE_INDEX)) 97 | { 98 | _initialiseFrame(); 99 | _appendAttitudeData(); 100 | _finaliseFrame(); 101 | sendFrame = true; 102 | } 103 | #endif 104 | 105 | #if CRSF_TELEMETRY_BAROALTITUDE_ENABLED > 0 106 | if (currentSchedule & (1 << CRSF_TELEMETRY_FRAME_BARO_ALTITUDE_INDEX)) 107 | { 108 | _initialiseFrame(); 109 | _appendBaroAltitudeData(); 110 | _finaliseFrame(); 111 | sendFrame = true; 112 | } 113 | #endif 114 | 115 | #if CRSF_TELEMETRY_BATTERY_ENABLED > 0 116 | if (currentSchedule & (1 << CRSF_TELEMETRY_FRAME_BATTERY_SENSOR_INDEX)) 117 | { 118 | _initialiseFrame(); 119 | _appendBatterySensorData(); 120 | _finaliseFrame(); 121 | sendFrame = true; 122 | } 123 | #endif 124 | 125 | #if CRSF_TELEMETRY_FLIGHTMODE_ENABLED > 0 126 | if (currentSchedule & (1 << CRSF_TELEMETRY_FRAME_FLIGHT_MODE_INDEX)) 127 | { 128 | _initialiseFrame(); 129 | _appendFlightModeData(); 130 | _finaliseFrame(); 131 | sendFrame = true; 132 | } 133 | #endif 134 | 135 | #if CRSF_TELEMETRY_GPS_ENABLED > 0 136 | if (currentSchedule & (1 << CRSF_TELEMETRY_FRAME_GPS_INDEX)) 137 | { 138 | _initialiseFrame(); 139 | _appendGPSData(); 140 | _finaliseFrame(); 141 | sendFrame = true; 142 | } 143 | #endif 144 | 145 | scheduleIndex = (scheduleIndex + 1) % _telemetryFrameScheduleCount; 146 | 147 | return sendFrame; 148 | #else 149 | return false; 150 | #endif 151 | } 152 | 153 | void Telemetry::setAttitudeData(int16_t roll, int16_t pitch, int16_t yaw) 154 | { 155 | #if CRSF_TELEMETRY_ENABLED > 0 && CRSF_TELEMETRY_ATTITUDE_ENABLED > 0 156 | _telemetryData.attitude.roll = _decidegreeToRadians(roll); 157 | _telemetryData.attitude.pitch = -_decidegreeToRadians(pitch); 158 | _telemetryData.attitude.yaw = _decidegreeToRadians(yaw); 159 | #else 160 | (void)roll; 161 | (void)pitch; 162 | (void)yaw; 163 | #endif 164 | } 165 | 166 | void Telemetry::setBaroAltitudeData(uint16_t altitude, int16_t vario) 167 | { 168 | #if CRSF_TELEMETRY_ENABLED > 0 && CRSF_TELEMETRY_BAROALTITUDE_ENABLED > 0 169 | _telemetryData.baroAltitude.altitude = altitude + 10000; 170 | _telemetryData.baroAltitude.vario = vario; 171 | #else 172 | (void)altitude; 173 | (void)vario; 174 | #endif 175 | } 176 | 177 | void Telemetry::setBatteryData(float voltage, float current, uint32_t capacity, uint8_t percent) 178 | { 179 | #if CRSF_TELEMETRY_ENABLED > 0 && CRSF_TELEMETRY_BATTERY_ENABLED > 0 180 | _telemetryData.battery.voltage = (voltage + 5) / 10; 181 | _telemetryData.battery.current = current / 10; 182 | _telemetryData.battery.capacity = capacity; 183 | _telemetryData.battery.percent = percent; 184 | #else 185 | (void)voltage; 186 | (void)current; 187 | (void)capacity; 188 | (void)percent; 189 | #endif 190 | } 191 | 192 | void Telemetry::setFlightModeData(const char *flightMode, bool armed) 193 | { 194 | #if CRSF_TELEMETRY_ENABLED > 0 && CRSF_TELEMETRY_FLIGHTMODE_ENABLED > 0 195 | size_t length = strlen(flightMode); 196 | memset(_telemetryData.flightMode.flightMode, 0, sizeof(_telemetryData.flightMode.flightMode)); 197 | memcpy(_telemetryData.flightMode.flightMode, flightMode, length); 198 | 199 | if (armed) 200 | { 201 | strcat(_telemetryData.flightMode.flightMode, "*"); 202 | } 203 | #else 204 | (void)flightMode; 205 | (void)armed; 206 | #endif 207 | } 208 | 209 | void Telemetry::setGPSData(float latitude, float longitude, float altitude, float speed, float course, uint8_t satellites) 210 | { 211 | #if CRSF_TELEMETRY_ENABLED > 0 && CRSF_TELEMETRY_GPS_ENABLED > 0 212 | _telemetryData.gps.latitude = latitude * 10000000; 213 | _telemetryData.gps.longitude = longitude * 10000000; 214 | _telemetryData.gps.altitude = (constrain(altitude, 0, 5000 * 100) / 100) + 1000; 215 | _telemetryData.gps.speed = ((speed * 36 + 50) / 100); 216 | _telemetryData.gps.groundCourse = (course * 100); 217 | _telemetryData.gps.satellites = satellites; 218 | #else 219 | (void)latitude; 220 | (void)longitude; 221 | (void)altitude; 222 | (void)speed; 223 | (void)course; 224 | (void)satellites; 225 | #endif 226 | } 227 | 228 | void Telemetry::sendTelemetryData(HardwareSerial *db) 229 | { 230 | uint8_t *buffer = SerialBuffer::getBuffer(); 231 | size_t length = SerialBuffer::getLength(); 232 | 233 | db->write(buffer, length); 234 | } 235 | 236 | int16_t Telemetry::_decidegreeToRadians(int16_t decidegrees) 237 | { 238 | while (decidegrees > 18000) 239 | { 240 | decidegrees -= 36000; 241 | } 242 | while (decidegrees < -18000) 243 | { 244 | decidegrees += 36000; 245 | } 246 | return (int16_t)(RAD * 1000.0F * decidegrees); 247 | } 248 | 249 | void Telemetry::_initialiseFrame() 250 | { 251 | SerialBuffer::reset(); 252 | SerialBuffer::writeU8(CRSF_SYNC_BYTE); 253 | } 254 | 255 | void Telemetry::_appendAttitudeData() 256 | { 257 | SerialBuffer::writeU8(CRSF_FRAME_ATTITUDE_PAYLOAD_SIZE + CRSF_FRAME_LENGTH_TYPE_CRC); 258 | SerialBuffer::writeU8(CRSF_FRAMETYPE_ATTITUDE); 259 | 260 | SerialBuffer::writeU16BE(_telemetryData.attitude.pitch); 261 | SerialBuffer::writeU16BE(_telemetryData.attitude.roll); 262 | SerialBuffer::writeU16BE(_telemetryData.attitude.yaw); 263 | } 264 | 265 | void Telemetry::_appendBaroAltitudeData() 266 | { 267 | SerialBuffer::writeU8(CRSF_FRAME_BARO_ALTITUDE_PAYLOAD_SIZE + CRSF_FRAME_LENGTH_TYPE_CRC); 268 | SerialBuffer::writeU8(CRSF_FRAMETYPE_BARO_ALTITUDE); 269 | 270 | SerialBuffer::writeU16BE(_telemetryData.baroAltitude.altitude); 271 | SerialBuffer::writeU16BE(_telemetryData.baroAltitude.vario); 272 | } 273 | 274 | void Telemetry::_appendBatterySensorData() 275 | { 276 | SerialBuffer::writeU8(CRSF_FRAME_BATTERY_SENSOR_PAYLOAD_SIZE + CRSF_FRAME_LENGTH_TYPE_CRC); 277 | SerialBuffer::writeU8(CRSF_FRAMETYPE_BATTERY_SENSOR); 278 | 279 | SerialBuffer::writeU16BE(_telemetryData.battery.voltage); 280 | SerialBuffer::writeU16BE(_telemetryData.battery.current); 281 | SerialBuffer::writeU24BE(_telemetryData.battery.capacity); 282 | SerialBuffer::writeU8(_telemetryData.battery.percent); 283 | } 284 | 285 | #if CRSF_TELEMETRY_ENABLED > 0 && CRSF_TELEMETRY_FLIGHTMODE_ENABLED > 0 286 | void Telemetry::_appendFlightModeData() 287 | { 288 | size_t length = strlen(_telemetryData.flightMode.flightMode) + 1; 289 | if (length > CRSF_FRAME_FLIGHT_MODE_PAYLOAD_SIZE) 290 | { 291 | return; 292 | } 293 | 294 | SerialBuffer::writeU8(length + CRSF_FRAME_LENGTH_TYPE_CRC); 295 | SerialBuffer::writeU8(CRSF_FRAMETYPE_FLIGHT_MODE); 296 | 297 | SerialBuffer::writeString(_telemetryData.flightMode.flightMode); 298 | 299 | SerialBuffer::writeU8('\0'); 300 | } 301 | #endif 302 | 303 | void Telemetry::_appendGPSData() 304 | { 305 | SerialBuffer::writeU8(CRSF_FRAME_GPS_PAYLOAD_SIZE + CRSF_FRAME_LENGTH_TYPE_CRC); 306 | SerialBuffer::writeU8(CRSF_FRAMETYPE_GPS); 307 | 308 | SerialBuffer::write32BE(_telemetryData.gps.latitude); 309 | SerialBuffer::write32BE(_telemetryData.gps.longitude); 310 | SerialBuffer::writeU16BE(_telemetryData.gps.speed); 311 | SerialBuffer::writeU16BE(_telemetryData.gps.groundCourse); 312 | SerialBuffer::writeU16BE(_telemetryData.gps.altitude); 313 | SerialBuffer::writeU8(_telemetryData.gps.satellites); 314 | } 315 | 316 | void Telemetry::_finaliseFrame() 317 | { 318 | uint8_t *buffer = SerialBuffer::getBuffer(); 319 | uint8_t length = SerialBuffer::getLength(); 320 | uint8_t crc = GenericCRC::calculate(2, buffer[2], buffer, length); 321 | 322 | SerialBuffer::writeU8(crc); 323 | } 324 | } // namespace serialReceiverLayer 325 | -------------------------------------------------------------------------------- /src/SerialReceiver/Telemetry/Telemetry.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Telemetry.hpp 3 | * @author Cassandra "ZZ Cat" Robinson (nicad.heli.flier@gmail.com) 4 | * @brief This encodes data into CRSF telemetry frames for transmission to the RC handset. 5 | * 6 | * @copyright Copyright (c) 2024, Cassandra "ZZ Cat" Robinson. All rights reserved. 7 | * 8 | * @section License GNU Affero General Public License v3.0 9 | * This source file is a part of the CRSF for Arduino library. 10 | * CRSF for Arduino is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * CRSF for Arduino is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with CRSF for Arduino. If not, see . 22 | * 23 | */ 24 | 25 | #pragma once 26 | 27 | #include "Arduino.h" 28 | 29 | #include "../CRC/CRC.hpp" 30 | #include "../CRSF/CRSFProtocol.hpp" 31 | #include "../SerialBuffer/SerialBuffer.hpp" 32 | 33 | namespace serialReceiverLayer 34 | { 35 | class Telemetry : private genericCrc::GenericCRC, private genericStreamBuffer::SerialBuffer 36 | { 37 | public: 38 | Telemetry(); 39 | ~Telemetry(); 40 | 41 | void begin(); 42 | void end(); 43 | 44 | bool update(); 45 | 46 | void setAttitudeData(int16_t roll, int16_t pitch, int16_t yaw); 47 | void setBaroAltitudeData(uint16_t altitude, int16_t vario); 48 | void setBatteryData(float voltage, float current, uint32_t capacity, uint8_t percent); 49 | void setFlightModeData(const char *flightMode, bool armed = false); 50 | void setGPSData(float latitude, float longitude, float altitude, float speed, float course, uint8_t satellites); 51 | 52 | void sendTelemetryData(HardwareSerial *db); 53 | 54 | private: 55 | uint8_t _telemetryFrameScheduleCount; 56 | uint8_t _telemetryFrameSchedule[crsfProtocol::CRSF_TELEMETRY_FRAME_SCHEDULE_MAX]; 57 | crsfProtocol::telemetryData_t _telemetryData; 58 | 59 | int16_t _decidegreeToRadians(int16_t decidegrees); 60 | 61 | void _initialiseFrame(); 62 | void _appendAttitudeData(); 63 | void _appendBaroAltitudeData(); 64 | void _appendBatterySensorData(); 65 | #if CRSF_TELEMETRY_ENABLED > 0 && CRSF_TELEMETRY_FLIGHTMODE_ENABLED > 0 66 | void _appendFlightModeData(); 67 | #endif 68 | void _appendGPSData(); 69 | void _finaliseFrame(); 70 | }; 71 | } // namespace serialReceiverLayer 72 | -------------------------------------------------------------------------------- /src/build/scripts/build.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import sys 3 | 4 | if __name__ == "__main__": 5 | # Run the defect detector to check for any issues in the code. 6 | command = "pio check -e defect_detector --fail-on-defect=low --fail-on-defect=medium --fail-on-defect=high" 7 | try: 8 | subprocess.run(command, shell=True, check=True, text=True, capture_output=True) 9 | except subprocess.CalledProcessError as e: 10 | print(f"{e.stdout.strip()}") 11 | print(f"{e.stderr.strip()}") 12 | print("There were issues detected in the code-base. Please resolve them before proceeding.") 13 | exit(1) 14 | 15 | # Get the command line arguments. 16 | args = sys.argv[1:] 17 | # If --deploy was specified as a command line argument, deploy the code to the device. 18 | if "--deploy" in args: 19 | # Run the PlatformIO upload command. 20 | command = "pio run -e development -t upload" 21 | try: 22 | subprocess.run(command, shell=True, check=True, text=True, capture_output=True) 23 | except subprocess.CalledProcessError as e: 24 | print(f"{e.stdout.strip()}") 25 | print(f"{e.stderr.strip()}") 26 | print("Deployment failed. Please resolve the issues before proceeding.") 27 | exit(1) 28 | 29 | # If --deploy-and-monitor was specified as a command line argument, 30 | # deploy the code to the device, wait for it to finish, and then start the serial monitor. 31 | elif "--deploy-and-monitor" in args: 32 | # Run the PlatformIO upload command. 33 | command = "pio run -e development -t upload" 34 | try: 35 | subprocess.run(command, shell=True, check=True, text=True, capture_output=True) 36 | except subprocess.CalledProcessError as e: 37 | print(f"{e.stdout.strip()}") 38 | print(f"{e.stderr.strip()}") 39 | print("Deployment failed. Please resolve the issues before proceeding.") 40 | exit(1) 41 | # Start the serial monitor. 42 | command = "pio device monitor" 43 | try: 44 | subprocess.run(command, shell=True, check=True, text=True) 45 | except subprocess.CalledProcessError as e: 46 | print(f"{e.stdout.strip()}") 47 | print(f"{e.stderr.strip()}") 48 | print("Failed to start the serial monitor. Please resolve the issues before proceeding.") 49 | exit(1) 50 | 51 | # Check compatibility across verified devices. 52 | # TO-DO: 53 | # - Conduct static analysis across verified devices. 54 | # - May need to update platformio.ini and the other targets files for this. 55 | # - For now, this just works. 56 | elif "--build-on-compatible-devices" in args: 57 | # No arguments provided—defaults to building across commonly used devices. 58 | # Because these are the ones that are tested and verified It Just Works. 59 | command = "pio run" 60 | try: 61 | subprocess.run(command, shell=True, check=True, capture_output=True) 62 | except subprocess.CalledProcessError as e: 63 | print(f"{e.stdout.strip()}") 64 | print(f"{e.stderr.strip()}") 65 | print("Build failed. One or more devices are not compatible.") 66 | exit(1) 67 | 68 | # If no arguments were specified, run the build command. 69 | else: 70 | # Run the PlatformIO build command. 71 | command = "pio run -e development" 72 | try: 73 | subprocess.run(command, shell=True, check=True, text=True, capture_output=True) 74 | except subprocess.CalledProcessError as e: 75 | print(f"{e.stdout.strip()}") 76 | print(f"{e.stderr.strip()}") 77 | print("Build failed. Please resolve the issues before proceeding.") 78 | exit(1) 79 | 80 | # Exit with code 0 if everything is fine. 81 | exit(0) 82 | -------------------------------------------------------------------------------- /src/build/scripts/platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [platformio] 12 | core_dir = ../../../.pio/core 13 | workspace_dir = ../../../.pio 14 | default_envs = 15 | ; defect_detector 16 | ; development 17 | ${build.commonly_used} 18 | extra_configs = 19 | ../../../src/build/targets/common.ini 20 | ../../../src/build/targets/quality_control.ini 21 | ../../../src/build/targets/unified_esp32.ini 22 | ../../../src/build/targets/unified_rp2040.ini 23 | ../../../src/build/targets/unified_samd21.ini 24 | ../../../src/build/targets/unified_samd51.ini 25 | ../../../src/build/targets/unified_stm32.ini 26 | ../../../src/build/targets/unified_teensy3x.ini 27 | ../../../src/build/targets/unified_teensy4x.ini 28 | include_dir = ../../../src 29 | lib_dir = ../../../src 30 | src_dir = ../../../src 31 | test_dir = 32 | 33 | [env:development] 34 | board = adafruit_metro_m4 35 | build_src_filter = 36 | +<../examples/platformio/main.cpp> 37 | + 38 | + 39 | + 40 | + 41 | + 42 | + 43 | + 44 | build_type = debug 45 | extends = env_common_samd51 46 | -------------------------------------------------------------------------------- /src/build/targets/common.ini: -------------------------------------------------------------------------------- 1 | [optimise_level] 2 | tiny = -Oz 3 | default = -Os 4 | debug = -Og 5 | fast = -O2 6 | faster = -O3 7 | fastest = -Ofast 8 | here_be_dragons = -Ofast -funroll-loops 9 | 10 | [common] 11 | build_flags = 12 | -DCRC_OPTIMISATION_LEVEL=0 13 | ${optimise_level.fastest} 14 | 15 | [env_common_esp32] 16 | platform = espressif32@6.4.0 17 | 18 | [env_common_rp2040] 19 | platform = https://github.com/maxgerhardt/platform-raspberrypi.git 20 | board_build.core = earlephilhower 21 | board_build.filesystem_size = 0.5m 22 | platform_packages = 23 | maxgerhardt/framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#master 24 | 25 | [env_common_samd21] 26 | platform = atmelsam@8.2.0 27 | 28 | [env_common_samd51] 29 | platform = atmelsam@8.2.0 30 | 31 | [env_common_stm32] 32 | platform = ststm32@17.2.0 33 | 34 | [env_common_teensy] 35 | platform = teensy@4.18.0 36 | 37 | [env] 38 | framework = arduino 39 | build_src_filter = 40 | +<../examples/platformio/main.cpp> 41 | + 42 | + 43 | + 44 | + 45 | + 46 | + 47 | + 48 | ; -<../examples/platformio/cfa_code_test.cpp> 49 | ; +<../examples/platformio/main.cpp> 50 | ; +<*/*/*.cpp> 51 | ; +<*.cpp> 52 | build_unflags = 53 | -Os 54 | build_flags = 55 | ${common.build_flags} 56 | -------------------------------------------------------------------------------- /src/build/targets/quality_control.ini: -------------------------------------------------------------------------------- 1 | [env:defect_detector] 2 | board = adafruit_metro_m4 3 | build_flags = 4 | ; An isolated configuration from CFA_Config.hpp is used here to test EVERY possible 5 | ; configuration, and ensure that defects (if any) are picked up. 6 | ; The `ENV_DEFECT_DETECTOR` define configures CRSF for Arduino for use with the Defect Detector. 7 | -DENV_DEFECT_DETECTOR 8 | 9 | -DCRSF_FAILSAFE_LQI_THRESHOLD=80 10 | -DCRSF_FAILSAFE_RSSI_THRESHOLD=105 11 | 12 | -DCRSF_RC_ENABLED=1 13 | -DCRSF_RC_MAX_CHANNELS=16 14 | -DCRSF_RC_CHANNEL_MIN=172 15 | -DCRSF_RC_CHANNEL_MAX=1811 16 | -DCRSF_RC_CHANNEL_CENTER=992 17 | -DCRSF_RC_INITIALISE_CHANNELS=1 18 | -DCRSF_RC_INITIALISE_ARMCHANNEL=1 19 | -DCRSF_RC_INITIALISE_THROTTLECHANNEL=1 20 | 21 | -DCRSF_FLIGHTMODES_ENABLED=1 22 | -DCRSF_CUSTOM_FLIGHT_MODES_ENABLED=1 23 | 24 | -DCRSF_TELEMETRY_ENABLED=1 25 | -DCRSF_TELEMETRY_ATTITUDE_ENABLED=1 26 | -DCRSF_TELEMETRY_BAROALTITUDE_ENABLED=1 27 | -DCRSF_TELEMETRY_BATTERY_ENABLED=1 28 | -DCRSF_TELEMETRY_FLIGHTMODE_ENABLED=1 29 | -DCRSF_TELEMETRY_GPS_ENABLED=1 30 | 31 | -DCRSF_LINK_STATISTICS_ENABLED=1 32 | 33 | build_src_filter = 34 | +<../examples/platformio/main.cpp> 35 | + 36 | + 37 | + 38 | + 39 | + 40 | + 41 | + 42 | ; +<../examples/platformio/main.cpp> 43 | ; +<*/*/*.cpp> 44 | ; +<*.cpp> 45 | build_type = debug 46 | check_flags = 47 | --disable=unusedFunction 48 | check_severity = low, medium, high 49 | check_skip_packages = yes 50 | check_tool = cppcheck 51 | extends = env_common_samd51 52 | 53 | [build] 54 | ; Intentionally left blank to allow PlatformIO to process _all_ targets CRSF for Arduino is compatible with. 55 | all = 56 | 57 | ; Build check will be restricted to commonly used targets, if this is selected in platformio.ini. 58 | commonly_used = 59 | adafruit_feather_m0 60 | adafruit_feather_m4 61 | adafruit_feather_m4_can 62 | adafruit_metro_m4 63 | adafruit_qtpy_esp32 64 | adafruit_qtpy_esp32c3 65 | adafruit_qtpy_esp32s2 66 | adafruit_qtpy_esp32s3_nopsram 67 | arduino_nano_rp2040_connect 68 | seeed_xiao_esp32c3 69 | seeed_xiao_esp32s3 70 | stm32f103cb 71 | stm32f405rg 72 | stm32f411re 73 | stm32h750vb 74 | teensy_41 75 | -------------------------------------------------------------------------------- /src/build/targets/unified_esp32.ini: -------------------------------------------------------------------------------- 1 | [env:adafruit_qtpy_esp32] 2 | extends = env_common_esp32 3 | board = adafruit_qtpy_esp32 4 | 5 | [env:adafruit_qtpy_esp32c3] 6 | extends = env_common_esp32 7 | board = adafruit_qtpy_esp32c3 8 | 9 | [env:adafruit_qtpy_esp32s2] 10 | extends = env_common_esp32 11 | board = adafruit_qtpy_esp32s2 12 | 13 | [env:adafruit_qtpy_esp32s3_nopsram] 14 | extends = env_common_esp32 15 | board = adafruit_qtpy_esp32s3_nopsram 16 | 17 | [env:adafruit_feather_esp32s3] 18 | extends = env_common_esp32 19 | board = adafruit_feather_esp32s3 20 | 21 | [env:adafruit_feather_esp32s3_nopsram] 22 | extends = env_common_esp32 23 | board = adafruit_feather_esp32s3_nopsram 24 | 25 | [env:adafruit_itsybitsy_esp32] 26 | extends = env_common_esp32 27 | board = adafruit_itsybitsy_esp32 28 | 29 | [env:adafruit_metro_esp32s2] 30 | extends = env_common_esp32 31 | board = adafruit_metro_esp32s2 32 | 33 | [env:adafruit_feather_esp32] 34 | extends = env_common_esp32 35 | board = featheresp32 36 | 37 | [env:adafruit_feather_esp32-s2] 38 | extends = env_common_esp32 39 | board = featheresp32-s2 40 | 41 | [env:arduino_nano_esp32] 42 | extends = env_common_esp32 43 | board = arduino_nano_esp32 44 | 45 | [env:esp32-c3-devkit-02] 46 | extends = env_common_esp32 47 | board = esp32-c3-devkitc-02 48 | 49 | [env:esp32-c3-devkitm-1] 50 | extends = env_common_esp32 51 | board = esp32-c3-devkitm-1 52 | 53 | [env:esp32-s3-devkitc-1] 54 | extends = env_common_esp32 55 | board = esp32-s3-devkitc-1 56 | 57 | [env:seeed_xiao_esp32c3] 58 | extends = env_common_esp32 59 | board = seeed_xiao_esp32c3 60 | 61 | [env:seeed_xiao_esp32s3] 62 | extends = env_common_esp32 63 | board = seeed_xiao_esp32s3 64 | 65 | [env:sparkfun_esp32_thing] 66 | extends = env_common_esp32 67 | board = esp32thing 68 | 69 | [env:sparkfun_esp32_thing_plus] 70 | extends = env_common_esp32 71 | board = esp32thing_plus 72 | 73 | [env:sparkfun_esp32_iot_redboard] 74 | extends = env_common_esp32 75 | board = sparkfun_esp32_iot_redboard 76 | 77 | [env:sparkfun_esp32s2_thing_plus] 78 | extends = env_common_esp32 79 | board = sparkfun_esp32s2_thing_plus 80 | -------------------------------------------------------------------------------- /src/build/targets/unified_rp2040.ini: -------------------------------------------------------------------------------- 1 | [env:arduino_nano_rp2040_connect] 2 | extends = env_common_rp2040 3 | board = arduino_nano_connect 4 | 5 | [env:raspberrypi_pico_rp2040] 6 | extends = env_common_rp2040 7 | board = rpipico 8 | -------------------------------------------------------------------------------- /src/build/targets/unified_samd21.ini: -------------------------------------------------------------------------------- 1 | [env:adafruit_feather_m0] 2 | extends = env_common_samd21 3 | board = adafruit_feather_m0 4 | 5 | [env:adafruit_feather_m0_express] 6 | extends = env_common_samd21 7 | board = adafruit_feather_m0_express 8 | 9 | [env:adafruit_itsybitsy_m0] 10 | extends = env_common_samd21 11 | board = adafruit_itsybitsy_m0 12 | 13 | [env:adafruit_metro_m0] 14 | extends = env_common_samd21 15 | board = adafruit_metro_m0 16 | 17 | [env:arduino_mkrwan1310] 18 | extends = env_common_samd21 19 | board = mkrwan1310 20 | 21 | [env:arduino_mkrwifi1010] 22 | extends = env_common_samd21 23 | board = mkrwifi1010 24 | 25 | [env:arduino_mkrzero] 26 | extends = env_common_samd21 27 | board = mkrzero 28 | 29 | [env:arduino_nano_33_iot] 30 | extends = env_common_samd21 31 | board = nano_33_iot 32 | 33 | [env:arduino_zero] 34 | extends = env_common_samd21 35 | board = zero 36 | 37 | [env:arduino_zeroUSB] 38 | extends = env_common_samd21 39 | board = zeroUSB 40 | 41 | [env:seeed_xiao_samd21] 42 | extends = env_common_samd21 43 | board = seeed_xiao 44 | -------------------------------------------------------------------------------- /src/build/targets/unified_samd51.ini: -------------------------------------------------------------------------------- 1 | [env:adafruit_feather_m4] 2 | extends = env_common_samd51 3 | board = adafruit_feather_m4 4 | 5 | [env:adafruit_feather_m4_can] 6 | extends = env_common_samd51 7 | board = adafruit_feather_m4_can 8 | 9 | [env:adafruit_grandcentral_m4] 10 | extends = env_common_samd51 11 | board = adafruit_grandcentral_m4 12 | 13 | [env:adafruit_itsybitsy_m4] 14 | extends = env_common_samd51 15 | board = adafruit_itsybitsy_m4 16 | 17 | [env:adafruit_metro_m4] 18 | extends = env_common_samd51 19 | board = adafruit_metro_m4 20 | 21 | [env:adafruit_metro_m4_airliftlite] 22 | extends = env_common_samd51 23 | board = adafruit_metro_m4_airliftlite 24 | -------------------------------------------------------------------------------- /src/build/targets/unified_stm32.ini: -------------------------------------------------------------------------------- 1 | [env:adafruit_feather_f405] 2 | extends = env_common_stm32 3 | board = adafruit_feather_f405 4 | 5 | [env:arduino_nicla_vision] 6 | extends = env_common_stm32 7 | board = nicla_vision 8 | 9 | [env:arduino_nicla_vision_m4_core] 10 | extends = env_common_stm32 11 | board = nicla_vision_m4 12 | 13 | [env:arduino_opta] 14 | extends = env_common_stm32 15 | board = opta 16 | 17 | [env:arduino_nicla_opta_m4_core] 18 | extends = env_common_stm32 19 | board = opta_m4 20 | 21 | [env:arduino_portenta_m4_core] 22 | extends = env_common_stm32 23 | board = portenta_h7_m4 24 | 25 | [env:arduino_portenta_m7_core] 26 | extends = env_common_stm32 27 | board = portenta_h7_m7 28 | 29 | [env:blackpill_stm32f103c8] 30 | extends = env_common_stm32 31 | board = blackpill_f103c8 32 | 33 | [env:blackpill_stm32f103c8_128] 34 | extends = env_common_stm32 35 | board = blackpill_f103c8_128 36 | 37 | [env:blackpill_stm32f401cc] 38 | extends = env_common_stm32 39 | board = blackpill_f401cc 40 | 41 | [env:blackpill_stm32f401ce] 42 | extends = env_common_stm32 43 | board = blackpill_f401ce 44 | 45 | [env:blackpill_stm32f411ce] 46 | extends = env_common_stm32 47 | board = blackpill_f411ce 48 | 49 | [env:bluepill_stm32f103c6] 50 | extends = env_common_stm32 51 | board = bluepill_f103c6 52 | 53 | [env:bluepill_stm32f103c8] 54 | extends = env_common_stm32 55 | board = bluepill_f103c8 56 | 57 | [env:bluepill_stm32f103c8_128] 58 | extends = env_common_stm32 59 | board = bluepill_f103c8_128k 60 | 61 | [env:st_black_stm32f407ve] 62 | extends = env_common_stm32 63 | board = black_f407ve 64 | 65 | [env:st_black_stm32f407vg] 66 | extends = env_common_stm32 67 | board = black_f407vg 68 | 69 | [env:st_black_stm32f407ze] 70 | extends = env_common_stm32 71 | board = black_f407ze 72 | 73 | [env:st_black_stm32f407zg] 74 | extends = env_common_stm32 75 | board = black_f407zg 76 | 77 | [env:st_blue_stm32f407ve_mini] 78 | extends = env_common_stm32 79 | board = blue_f407ve_mini 80 | 81 | [env:st_discovery_f413] 82 | extends = env_common_stm32 83 | board = disco_f413zh 84 | 85 | [env:st_discovery_f746] 86 | extends = env_common_stm32 87 | board = disco_f746ng 88 | 89 | [env:st_nucleo_f401re] 90 | extends = env_common_stm32 91 | board = nucleo_f401re 92 | 93 | [env:st_nucleo_f411re] 94 | extends = env_common_stm32 95 | board = nucleo_f411re 96 | 97 | [env:st_nucleo_f429zi] 98 | extends = env_common_stm32 99 | board = nucleo_f429zi 100 | 101 | [env:st_nucleo_f446re] 102 | extends = env_common_stm32 103 | board = nucleo_f446re 104 | 105 | [env:st_nucleo_f722ze] 106 | extends = env_common_stm32 107 | board = nucleo_f722ze 108 | 109 | [env:st_nucleo_f746zg] 110 | extends = env_common_stm32 111 | board = nucleo_f746zg 112 | 113 | [env:st_nucleo_f756zg] 114 | extends = env_common_stm32 115 | board = nucleo_f756zg 116 | 117 | [env:st_nucleo_f767zi] 118 | extends = env_common_stm32 119 | board = nucleo_f767zi 120 | 121 | [env:st_nucleo_h723zg] 122 | extends = env_common_stm32 123 | board = nucleo_h723zg 124 | 125 | [env:st_nucleo_h743zi] 126 | extends = env_common_stm32 127 | board = nucleo_h743zi 128 | 129 | [env:stm32f103c6] 130 | extends = env_common_stm32 131 | board = genericSTM32F103C6 132 | 133 | [env:stm32f103c8] 134 | extends = env_common_stm32 135 | board = genericSTM32F103C8 136 | 137 | [env:stm32f103cb] 138 | extends = env_common_stm32 139 | board = genericSTM32F103CB 140 | 141 | [env:stm32f103r6] 142 | extends = env_common_stm32 143 | board = genericSTM32F103R6 144 | 145 | [env:stm32f103r8] 146 | extends = env_common_stm32 147 | board = genericSTM32F103R8 148 | 149 | [env:stm32f103rb] 150 | extends = env_common_stm32 151 | board = genericSTM32F103RB 152 | 153 | [env:stm32f103rc] 154 | extends = env_common_stm32 155 | board = genericSTM32F103RC 156 | 157 | [env:stm32f103rd] 158 | extends = env_common_stm32 159 | board = genericSTM32F103RD 160 | 161 | [env:stm32f103re] 162 | extends = env_common_stm32 163 | board = genericSTM32F103RE 164 | 165 | [env:stm32f103rf] 166 | extends = env_common_stm32 167 | board = genericSTM32F103RF 168 | 169 | [env:stm32f103rg] 170 | extends = env_common_stm32 171 | board = genericSTM32F103RG 172 | 173 | [env:stm32f103t6] 174 | extends = env_common_stm32 175 | board = genericSTM32F103T6 176 | 177 | [env:stm32f103t8] 178 | extends = env_common_stm32 179 | board = genericSTM32F103T8 180 | 181 | [env:stm32f103tb] 182 | extends = env_common_stm32 183 | board = genericSTM32F103TB 184 | 185 | [env:stm32f103v8] 186 | extends = env_common_stm32 187 | board = genericSTM32F103V8 188 | 189 | [env:stm32f103vb] 190 | extends = env_common_stm32 191 | board = genericSTM32F103VB 192 | 193 | [env:stm32f103vc] 194 | extends = env_common_stm32 195 | board = genericSTM32F103VC 196 | 197 | [env:stm32f103vd] 198 | extends = env_common_stm32 199 | board = genericSTM32F103VD 200 | 201 | [env:stm32f103ve] 202 | extends = env_common_stm32 203 | board = genericSTM32F103VE 204 | 205 | [env:stm32f103vf] 206 | extends = env_common_stm32 207 | board = genericSTM32F103VF 208 | 209 | [env:stm32f103vg] 210 | extends = env_common_stm32 211 | board = genericSTM32F103VG 212 | 213 | [env:stm32f103zc] 214 | extends = env_common_stm32 215 | board = genericSTM32F103ZC 216 | 217 | [env:stm32f103zd] 218 | extends = env_common_stm32 219 | board = genericSTM32F103ZD 220 | 221 | [env:stm32f103ze] 222 | extends = env_common_stm32 223 | board = genericSTM32F103ZE 224 | 225 | [env:stm32f103zf] 226 | extends = env_common_stm32 227 | board = genericSTM32F103ZF 228 | 229 | [env:stm32f103zg] 230 | extends = env_common_stm32 231 | board = genericSTM32F103ZG 232 | 233 | [env:stm32f401cb] 234 | extends = env_common_stm32 235 | board = genericSTM32F401CB 236 | 237 | [env:stm32f401cc] 238 | extends = env_common_stm32 239 | board = genericSTM32F401CC 240 | 241 | [env:stm32f401cd] 242 | extends = env_common_stm32 243 | board = genericSTM32F401CD 244 | 245 | [env:stm32f401ce] 246 | extends = env_common_stm32 247 | board = genericSTM32F401CE 248 | 249 | [env:stm32f401rb] 250 | extends = env_common_stm32 251 | board = genericSTM32F401RB 252 | 253 | [env:stm32f401rc] 254 | extends = env_common_stm32 255 | board = genericSTM32F401RC 256 | 257 | [env:stm32f401rd] 258 | extends = env_common_stm32 259 | board = genericSTM32F401RD 260 | 261 | [env:stm32f401re] 262 | extends = env_common_stm32 263 | board = genericSTM32F401RE 264 | 265 | [env:stm32f405rg] 266 | extends = env_common_stm32 267 | board = genericSTM32F405RG 268 | 269 | [env:stm32f407ve] 270 | extends = env_common_stm32 271 | board = genericSTM32F407VET6 272 | 273 | [env:stm32f407vg] 274 | extends = env_common_stm32 275 | board = genericSTM32F407VGT6 276 | 277 | [env:stm32f410c8] 278 | extends = env_common_stm32 279 | board = genericSTM32F410C8 280 | 281 | [env:stm32f410cb] 282 | extends = env_common_stm32 283 | board = genericSTM32F410CB 284 | 285 | [env:stm32f410r8] 286 | extends = env_common_stm32 287 | board = genericSTM32F410R8 288 | 289 | [env:stm32f410rb] 290 | extends = env_common_stm32 291 | board = genericSTM32F410RB 292 | 293 | [env:stm32f411ce] 294 | extends = env_common_stm32 295 | board = genericSTM32F411CE 296 | 297 | [env:stm32f411rc] 298 | extends = env_common_stm32 299 | board = genericSTM32F411RC 300 | 301 | [env:stm32f411re] 302 | extends = env_common_stm32 303 | board = genericSTM32F411RE 304 | 305 | [env:stm32f412ce] 306 | extends = env_common_stm32 307 | board = genericSTM32F412CE 308 | 309 | [env:stm32f412cg] 310 | extends = env_common_stm32 311 | board = genericSTM32F412CG 312 | 313 | [env:stm32f412re] 314 | extends = env_common_stm32 315 | board = genericSTM32F412RE 316 | 317 | [env:stm32f412rg] 318 | extends = env_common_stm32 319 | board = genericSTM32F412RG 320 | 321 | [env:stm32f413cg] 322 | extends = env_common_stm32 323 | board = genericSTM32F413CG 324 | 325 | [env:stm32f413ch] 326 | extends = env_common_stm32 327 | board = genericSTM32F413CH 328 | 329 | [env:stm32f413rg] 330 | extends = env_common_stm32 331 | board = genericSTM32F413RG 332 | 333 | [env:stm32f413rh] 334 | extends = env_common_stm32 335 | board = genericSTM32F413RH 336 | 337 | [env:stm32f415rg] 338 | extends = env_common_stm32 339 | board = genericSTM32F415RG 340 | 341 | [env:stm32f417ve] 342 | extends = env_common_stm32 343 | board = genericSTM32F417VE 344 | 345 | [env:stm32f417vg] 346 | extends = env_common_stm32 347 | board = genericSTM32F417VG 348 | 349 | [env:stm32f423ch] 350 | extends = env_common_stm32 351 | board = genericSTM32F423CH 352 | 353 | [env:stm32f423rh] 354 | extends = env_common_stm32 355 | board = genericSTM32F423RH 356 | 357 | [env:stm32f446rc] 358 | extends = env_common_stm32 359 | board = genericSTM32F446RC 360 | 361 | [env:stm32f446re] 362 | extends = env_common_stm32 363 | board = genericSTM32F446RE 364 | 365 | [env:stm32h750vb] 366 | extends = env_common_stm32 367 | board = genericSTM32H750VB 368 | 369 | [env:sparkfun_micromod_f405] 370 | extends = env_common_stm32 371 | board = sparkfun_micromod_f405 372 | -------------------------------------------------------------------------------- /src/build/targets/unified_teensy3x.ini: -------------------------------------------------------------------------------- 1 | [env:teensy_30] 2 | extends = env_common_teensy 3 | board = teensy30 4 | 5 | [env:teensy_31] 6 | extends = env_common_teensy 7 | board = teensy31 8 | 9 | [env:teensy_32] 10 | extends = env_common_teensy 11 | board = teensy31 12 | 13 | [env:teensy_35] 14 | extends = env_common_teensy 15 | board = teensy35 16 | 17 | [env:teensy_36] 18 | extends = env_common_teensy 19 | board = teensy36 20 | -------------------------------------------------------------------------------- /src/build/targets/unified_teensy4x.ini: -------------------------------------------------------------------------------- 1 | [env:teensy_40] 2 | extends = env_common_teensy 3 | board = teensy40 4 | 5 | [env:teensy_41] 6 | extends = env_common_teensy 7 | board = teensy41 8 | -------------------------------------------------------------------------------- /src/hal/CompatibilityTable/CompatibilityTable.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file CompatibilityTable.hpp 3 | * @author Cassandra "ZZ Cat" Robinson (nicad.heli.flier@gmail.com) 4 | * @brief The Compatibility Table determines if the target development board is compatible with CRSF for Arduino. 5 | * 6 | * @copyright Copyright (c) 2024, Cassandra "ZZ Cat" Robinson. All rights reserved. 7 | * 8 | * @section License GNU Affero General Public License v3.0 9 | * This source file is a part of the CRSF for Arduino library. 10 | * CRSF for Arduino is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * CRSF for Arduino is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with CRSF for Arduino. If not, see . 22 | * 23 | */ 24 | 25 | #pragma once 26 | 27 | namespace hal 28 | { 29 | class CompatibilityTable 30 | { 31 | public: 32 | CompatibilityTable(); 33 | virtual ~CompatibilityTable(); 34 | 35 | bool isDevboardCompatible(const char *name); 36 | const char *getDevboardName(); 37 | 38 | private: 39 | typedef enum ct_devboards_e 40 | { 41 | // Incompatible device. Non-permissive. 42 | DEVBOARD_IS_INCOMPATIBLE = 0, 43 | 44 | // Permissive incompatibility: 45 | // - The architecture and chip is known, but the board is not. 46 | // - The architecture is known, but the board and chip are not. 47 | DEVBOARD_IS_PERMISSIVELY_INCOMPATIBLE_UNKNOWN_BOARD, 48 | DEVBOARD_IS_PERMISSIVELY_INCOMPATIBLE_UNKNOWN_BOARD_AND_CHIP, 49 | 50 | // Adafruit ESP32 boards. 51 | DEVBOARD_ADAFRUIT_FEATHER_ESP32, 52 | DEVBOARD_ADAFRUIT_FEATHER_ESP32S2, 53 | DEVBOARD_ADAFRUIT_FEATHER_ESP32S3, 54 | DEVBOARD_ADAFRUIT_FEATHER_ESP32S3_NOPSRAM, 55 | DEVBOARD_ADAFRUIT_ITSYBITSY_ESP32, 56 | DEVBOARD_ADAFRUIT_METRO_ESP32S2, 57 | DEVBOARD_ADAFRUIT_QTPY_ESP32C3, 58 | DEVBOARD_ADAFRUIT_QTPY_ESP32_PICO, 59 | DEVBOARD_ADAFRUIT_QTPY_ESP32S2, 60 | DEVBOARD_ADAFRUIT_QTPY_ESP32S3, 61 | 62 | // Adafruit SAMD21 boards. 63 | DEVBOARD_ADAFRUIT_FEATHER_M0, 64 | DEVBOARD_ADAFRUIT_FEATHER_M0_EXPRESS, 65 | DEVBOARD_ADAFRUIT_ITSYBITSY_M0_EXPRESS, 66 | DEVBOARD_ADAFRUIT_METRO_M0_EXPRESS, 67 | DEVBOARD_ADAFRUIT_QTPY_M0, 68 | DEVBOARD_ADAFRUIT_TRINKET_M0, 69 | 70 | // Adafruit SAMD51 boards. 71 | DEVBOARD_ADAFRUIT_FEATHER_M4_EXPRESS, 72 | DEVBOARD_ADAFRUIT_GRAND_CENTRAL_M4, 73 | DEVBOARD_ADAFRUIT_ITSYBITSY_M4_EXPRESS, 74 | DEVBOARD_ADAFRUIT_METRO_M4_AIRLIFT_LITE, 75 | DEVBOARD_ADAFRUIT_METRO_M4_EXPRESS, 76 | 77 | // Adafruit SAME51 boards. 78 | DEVBOARD_ADAFRUIT_FEATHER_M4_CAN, 79 | 80 | // Arduino ESP32 boards. 81 | DEVBOARD_ARDUINO_NANO_ESP32, 82 | 83 | // Arduino RP2040 boards. 84 | DEVBOARD_ARDUINO_NANO_RP2040_CONNECT, 85 | 86 | // Arduino SAMD21 boards. 87 | DEVBOARD_ARDUINO_MKR1000, 88 | DEVBOARD_ARDUINO_MKRFOX1200, 89 | DEVBOARD_ARDUINO_MKRGSM1400, 90 | DEVBOARD_ARDUINO_MKRNB1500, 91 | DEVBOARD_ARDUINO_MKRVIDOR4000, 92 | DEVBOARD_ARDUINO_MKRWAN1300, 93 | DEVBOARD_ARDUINO_MKRWAN1310, 94 | DEVBOARD_ARDUINO_MKRWIFI1010, 95 | DEVBOARD_ARDUINO_MKRZERO, 96 | DEVBOARD_ARDUINO_NANO_33_IOT, 97 | DEVBOARD_ARDUINO_ZERO, 98 | 99 | // Arduino STM32 boards. 100 | DEVBOARD_ARDUINO_NICLA_VISION, 101 | DEVBOARD_ARDUINO_OPTA, 102 | DEVBOARD_ARDUINO_PORTENTA_H7, 103 | DEVBOARD_ARDUINO_PORTENTA_H7_M4, 104 | 105 | // Espressif ESP32 boards. 106 | DEVBOARD_ESPRESSIF_ESP32C3_DEVKIT, 107 | DEVBOARD_ESPRESSIF_ESP32S3_DEVKIT, 108 | 109 | // Raspberry Pi RP2040 boards. 110 | DEVBOARD_RASPBERRYPI_PICO, 111 | 112 | // Seeed Studio boards. 113 | DEVBOARD_SEEEDSTUDIO_XIAO_ESP32C3, 114 | DEVBOARD_SEEEDSTUDIO_XIAO_ESP32S3, 115 | DEVBOARD_SEEEDSTUDIO_XIAO_M0, 116 | 117 | // SparkFun Boards 118 | DEVBOARD_SPARKFUN_MICROMOD_F405, 119 | DEVBOARD_SPARKFUN_REDBOARD_ESP32_IOT, 120 | DEVBOARD_SPARKFUN_THING_ESP32, 121 | DEVBOARD_SPARKFUN_THING_PLUS_ESP32, 122 | DEVBOARD_SPARKFUN_THING_PLUS_ESP32S2, 123 | 124 | // STM32 boards. 125 | DEVBOARD_ADAFRUIT_FEATHER_F405, 126 | DEVBOARD_ST_BLACK_F407VE, 127 | DEVBOARD_ST_BLACK_F407VG, 128 | DEVBOARD_ST_BLACK_F407ZE, 129 | DEVBOARD_ST_BLACK_F407ZG, 130 | DEVBOARD_ST_BLUE_F407VE_MINI, 131 | DEVBOARD_ST_DISCOVERY_F413ZH, 132 | DEVBOARD_ST_DISCOVERY_F746NG, 133 | DEVBOARD_ST_NUCLEO_F401RE, 134 | DEVBOARD_ST_NUCLEO_F411RE, 135 | DEVBOARD_ST_NUCLEO_F429ZI, 136 | DEVBOARD_ST_NUCLEO_F446RE, 137 | DEVBOARD_ST_NUCLEO_F722ZE, 138 | DEVBOARD_ST_NUCLEO_F746ZG, 139 | DEVBOARD_ST_NUCLEO_F756ZG, 140 | DEVBOARD_ST_NUCLEO_F767ZI, 141 | DEVBOARD_ST_NUCLEO_H723ZG, 142 | DEVBOARD_ST_NUCLEO_H743ZI, 143 | DEVBOARD_STM32_BLACKPILL_STM32F103C8, 144 | DEVBOARD_STM32_BLACKPILL_STM32F401CC, 145 | DEVBOARD_STM32_BLACKPILL_STM32F411CE, 146 | DEVBOARD_STM32_BLUEPILL_STM32F103C6, 147 | DEVBOARD_STM32_BLUEPILL_STM32F103C8, 148 | DEVBOARD_STM32F103C6, 149 | DEVBOARD_STM32F103C8, 150 | DEVBOARD_STM32F103CB, 151 | DEVBOARD_STM32F103R6, 152 | DEVBOARD_STM32F103R8, 153 | DEVBOARD_STM32F103RB, 154 | DEVBOARD_STM32F103RC, 155 | DEVBOARD_STM32F103RD, 156 | DEVBOARD_STM32F103RE, 157 | DEVBOARD_STM32F103RF, 158 | DEVBOARD_STM32F103RG, 159 | DEVBOARD_STM32F103T6, 160 | DEVBOARD_STM32F103T8, 161 | DEVBOARD_STM32F103TB, 162 | DEVBOARD_STM32F103V8, 163 | DEVBOARD_STM32F103VB, 164 | DEVBOARD_STM32F103VC, 165 | DEVBOARD_STM32F103VD, 166 | DEVBOARD_STM32F103VE, 167 | DEVBOARD_STM32F103VF, 168 | DEVBOARD_STM32F103VG, 169 | DEVBOARD_STM32F103ZC, 170 | DEVBOARD_STM32F103ZD, 171 | DEVBOARD_STM32F103ZE, 172 | DEVBOARD_STM32F103ZF, 173 | DEVBOARD_STM32F103ZG, 174 | DEVBOARD_STM32F401CB, 175 | DEVBOARD_STM32F401CC, 176 | DEVBOARD_STM32F401CD, 177 | DEVBOARD_STM32F401CE, 178 | DEVBOARD_STM32F401RB, 179 | DEVBOARD_STM32F401RC, 180 | DEVBOARD_STM32F401RD, 181 | DEVBOARD_STM32F401RE, 182 | DEVBOARD_STM32F405RG, 183 | DEVBOARD_STM32F407VE, 184 | DEVBOARD_STM32F407VG, 185 | DEVBOARD_STM32F410C8, 186 | DEVBOARD_STM32F410CB, 187 | DEVBOARD_STM32F410R8, 188 | DEVBOARD_STM32F410RB, 189 | DEVBOARD_STM32F411CE, 190 | DEVBOARD_STM32F411RC, 191 | DEVBOARD_STM32F411RE, 192 | DEVBOARD_STM32F412CE, 193 | DEVBOARD_STM32F412CG, 194 | DEVBOARD_STM32F412RE, 195 | DEVBOARD_STM32F412RG, 196 | DEVBOARD_STM32F413CG, 197 | DEVBOARD_STM32F413CH, 198 | DEVBOARD_STM32F413RG, 199 | DEVBOARD_STM32F413RH, 200 | DEVBOARD_STM32F415RG, 201 | DEVBOARD_STM32F417VE, 202 | DEVBOARD_STM32F417VG, 203 | DEVBOARD_STM32F423CH, 204 | DEVBOARD_STM32F423RH, 205 | DEVBOARD_STM32F446RC, 206 | DEVBOARD_STM32F446RE, 207 | DEVBOARD_STM32H750BT, 208 | 209 | // STM32duino STM32F405 boards. 210 | DEVBOARD_STM32F405OE, 211 | DEVBOARD_STM32F405OG, 212 | DEVBOARD_STM32F405VG, 213 | DEVBOARD_STM32F405ZG, 214 | 215 | // STM32duino STM32F722 boards. 216 | DEVBOARD_STM32F722IC, 217 | DEVBOARD_STM32F722IE, 218 | DEVBOARD_STM32F722RC, 219 | DEVBOARD_STM32F722RE, 220 | DEVBOARD_STM32F722VC, 221 | DEVBOARD_STM32F722VE, 222 | DEVBOARD_STM32F722ZC, 223 | 224 | // STM32duino STM32H745 boards. 225 | DEVBOARD_STM32H745BG, 226 | DEVBOARD_STM32H745BI, 227 | DEVBOARD_STM32H745IG, 228 | DEVBOARD_STM32H745II, 229 | DEVBOARD_STM32H745ZG, 230 | DEVBOARD_STM32H745ZI, 231 | 232 | // Teensy boards. 233 | DEVBOARD_TEENSY_30, 234 | DEVBOARD_TEENSY_31_32, 235 | DEVBOARD_TEENSY_35, 236 | DEVBOARD_TEENSY_36, 237 | DEVBOARD_TEENSY_40, 238 | DEVBOARD_TEENSY_41, 239 | 240 | DEVBOARD_COUNT 241 | } ct_devboards_t; 242 | 243 | typedef struct ct_devicetypes_s 244 | { 245 | ct_devboards_t devboard; 246 | } ct_devicetypes_t; 247 | 248 | typedef struct ct_devices_s 249 | { 250 | ct_devicetypes_t type; 251 | } ct_devices_t; 252 | 253 | ct_devices_t device; 254 | 255 | const char *deviceNames[DEVBOARD_COUNT] = { 256 | "Incompatible device", 257 | "Permissively incompatible device (unknown board)", 258 | "Permissively incompatible device (unknown board and chip)", 259 | "Adafruit Feather ESP32", 260 | "Adafruit Feather ESP32-S2", 261 | "Adafruit Feather ESP32-S3", 262 | "Adafruit Feather ESP32-S3 (no PSRAM)", 263 | "Adafruit ItsyBitsy ESP32", 264 | "Adafruit Metro ESP32-S2", 265 | "Adafruit QT Py ESP32-C3", 266 | "Adafruit QT Py ESP32 Pico", 267 | "Adafruit QT Py ESP32-S2", 268 | "Adafruit QT Py ESP32-S3", 269 | "Adafruit Feather M0", 270 | "Adafruit Feather M0 Express", 271 | "Adafruit ItsyBitsy M0 Express", 272 | "Adafruit Metro M0 Express", 273 | "Adafruit QT Py M0", 274 | "Adafruit Trinket M0", 275 | "Adafruit Feather M4 Express", 276 | "Adafruit Grand Central M4", 277 | "Adafruit ItsyBitsy M4 Express", 278 | "Adafruit Metro M4 AirLift Lite", 279 | "Adafruit Metro M4 Express", 280 | "Adafruit Feather M4 CAN", 281 | "Arduino Nano ESP32", 282 | "Arduino Nano RP2040 Connect", 283 | "Arduino MKR1000", 284 | "Arduino MKRFOX1200", 285 | "Arduino MKRGSM1400", 286 | "Arduino MKRNB1500", 287 | "Arduino MKRVIDOR4000", 288 | "Arduino MKRWAN1300", 289 | "Arduino MKRWAN1310", 290 | "Arduino MKRWIFI1010", 291 | "Arduino MKRZERO", 292 | "Arduino Nano 33 IoT", 293 | "Arduino Zero", 294 | "Arduino Nicla Vision", 295 | "Arduino Opta", 296 | "Arduino Portenta H7", 297 | "Arduino Portenta H7 (M4 Core)", 298 | "Espressif ESP32-C3 DevKit", 299 | "Espressif ESP32-S3 DevKit", 300 | "Raspberry Pi Pico", 301 | "Seeed Studio Xiao ESP32-C3", 302 | "Seeed Studio Xiao ESP32-S3", 303 | "Seeed Studio Xiao SAMD21", 304 | "SparkFun MicroMod F405", 305 | "SparkFun RedBoard ESP32 IoT", 306 | "SparkFun Thing ESP32", 307 | "SparkFun Thing Plus ESP32", 308 | "SparkFun Thing Plus ESP32-S2", 309 | "Adafruit Feather F405", 310 | "ST Black F407VE", 311 | "ST Black F407VG", 312 | "ST Black F407ZE", 313 | "ST Black F407ZG", 314 | "ST Blue F407VE Mini", 315 | "ST Discovery F413ZH", 316 | "ST Discovery F746NG", 317 | "ST Nucleo F401RE", 318 | "ST Nucleo F411RE", 319 | "ST Nucleo F429ZI", 320 | "ST Nucleo F446RE", 321 | "ST Nucleo F722ZE", 322 | "ST Nucleo F746ZG", 323 | "ST Nucleo F756ZG", 324 | "ST Nucleo F767ZI", 325 | "ST Nucleo H723ZG", 326 | "ST Nucleo H743ZI", 327 | "STM32 BlackPill STM32F103C8", 328 | "STM32 BlackPill STM32F401CC", 329 | "STM32 BlackPill STM32F411CE", 330 | "STM32 BluePill STM32F103C6", 331 | "STM32 BluePill STM32F103C8", 332 | "STM32F103C6", 333 | "STM32F103C8", 334 | "STM32F103CB", 335 | "STM32F103R6", 336 | "STM32F103R8", 337 | "STM32F103RB", 338 | "STM32F103RC", 339 | "STM32F103RD", 340 | "STM32F103RE", 341 | "STM32F103RF", 342 | "STM32F103RG", 343 | "STM32F103T6", 344 | "STM32F103T8", 345 | "STM32F103TB", 346 | "STM32F103V8", 347 | "STM32F103VB", 348 | "STM32F103VC", 349 | "STM32F103VD", 350 | "STM32F103VE", 351 | "STM32F103VF", 352 | "STM32F103VG", 353 | "STM32F103ZC", 354 | "STM32F103ZD", 355 | "STM32F103ZE", 356 | "STM32F103ZF", 357 | "STM32F103ZG", 358 | "STM32F401CB", 359 | "STM32F401CC", 360 | "STM32F401CD", 361 | "STM32F401CE", 362 | "STM32F401RB", 363 | "STM32F401RC", 364 | "STM32F401RD", 365 | "STM32F401RE", 366 | "STM32F405RG", 367 | "STM32F407VE", 368 | "STM32F407VG", 369 | "STM32F410C8", 370 | "STM32F410CB", 371 | "STM32F410R8", 372 | "STM32F410RB", 373 | "STM32F411CE", 374 | "STM32F411RC", 375 | "STM32F411RE", 376 | "STM32F412CE", 377 | "STM32F412CG", 378 | "STM32F412RE", 379 | "STM32F412RG", 380 | "STM32F413CG", 381 | "STM32F413CH", 382 | "STM32F413RG", 383 | "STM32F413RH", 384 | "STM32F415RG", 385 | "STM32F417VE", 386 | "STM32F417VG", 387 | "STM32F423CH", 388 | "STM32F423RH", 389 | "STM32F446RC", 390 | "STM32F446RE", 391 | "STM32H750BT", 392 | "STM32F405OE", 393 | "STM32F405OG", 394 | "STM32F405VG", 395 | "STM32F405ZG", 396 | "STM32F722IC", 397 | "STM32F722IE", 398 | "STM32F722RC", 399 | "STM32F722RE", 400 | "STM32F722VC", 401 | "STM32F722VE", 402 | "STM32F722ZC", 403 | "STM32H745BG", 404 | "STM32H745BI", 405 | "STM32H745IG", 406 | "STM32H745II", 407 | "STM32H745ZG", 408 | "STM32H745ZI", 409 | "Teensy 3.0", 410 | "Teensy 3.1/3.2", 411 | "Teensy 3.5", 412 | "Teensy 3.6", 413 | "Teensy 4.0", 414 | "Teensy 4.1"}; 415 | }; 416 | } // namespace hal 417 | --------------------------------------------------------------------------------