mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-05-25 15:27:35 +08:00
Compare commits
518 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| abb1c5b0c8 | |||
| aa998d88b8 | |||
| 7e776a7b9c | |||
| 57cf570bb4 | |||
| 55b62e5f2b | |||
| 8d99569643 | |||
| fa42687082 | |||
| 3502205661 | |||
| 7c1dee0b41 | |||
| 70e98f17ff | |||
| e3e26b4bfd | |||
| 51b56a7390 | |||
| 05d94b9820 | |||
| a38cf4d9e6 | |||
| d72d99f2d8 | |||
| a2808a991c | |||
| 20ded97d8a | |||
| e5071beaa3 | |||
| 2c337b77ab | |||
| a36334de50 | |||
| 02d9c32645 | |||
| 10e3c15c54 | |||
| 359b43e575 | |||
| 107b708918 | |||
| b0cc29319f | |||
| 454b690c4b | |||
| 377bec1e85 | |||
| 358574f9f6 | |||
| a32b43af0a | |||
| 48b6ec84bd | |||
| 1a0b7dae9d | |||
| 0640cc9e35 | |||
| 6fd3c88bb0 | |||
| cdacb01f55 | |||
| 029edb50b3 | |||
| b6164107d1 | |||
| af3cfaea25 | |||
| 44b2d8f845 | |||
| ee636a0e3d | |||
| a631716265 | |||
| 1dadd92a86 | |||
| 339882c6ad | |||
| 3e396f65e5 | |||
| 3aa499dfce | |||
| 4da97eb4fd | |||
| 343fd01e19 | |||
| ec56d2d83b | |||
| 26969c25ff | |||
| 4429c53f93 | |||
| 5cdf5ac482 | |||
| 7ee02968ac | |||
| ce828af85c | |||
| b5deafdc92 | |||
| 6558928069 | |||
| c85b3cdd61 | |||
| 8340415962 | |||
| 1a67c3d50a | |||
| 5500ebb1d0 | |||
| 52203a6bb7 | |||
| 844ba41a28 | |||
| 010f6dcbae | |||
| 61d2173524 | |||
| 16d938cda9 | |||
| 7c4c773858 | |||
| 19b5292dff | |||
| fa2d1c3662 | |||
| 7f3e0e9679 | |||
| 9228dca9bd | |||
| 40133e0b2c | |||
| c677cb75df | |||
| b79ed50615 | |||
| 94c3765712 | |||
| 30b6938f5e | |||
| 62d0620eff | |||
| 102b64f604 | |||
| 41b40e34fa | |||
| b37733459d | |||
| 5528ebec64 | |||
| b4d5eca3c0 | |||
| 9fcb6bcc0a | |||
| ab318cb636 | |||
| f11329784f | |||
| 4d85c1ad93 | |||
| 040b885dbd | |||
| 84e7c8e681 | |||
| ddb83e8d4d | |||
| 31977d8d19 | |||
| 1cb2debbb9 | |||
| 2cb9b9bfbe | |||
| 85ca947e09 | |||
| 88a57c5b65 | |||
| 8ed35be826 | |||
| de9698e7fa | |||
| c5d22f5fea | |||
| 192ac7bb54 | |||
| 7c78efe0c4 | |||
| ebc2093e52 | |||
| 6b51eddecc | |||
| 60872afd90 | |||
| 906b87581c | |||
| 38eb03c91f | |||
| 6bf73d9d89 | |||
| 4ac8ceff31 | |||
| c7aaacc8b4 | |||
| b9093340e2 | |||
| f8157f9308 | |||
| 3be015c3cb | |||
| a01044655b | |||
| 2fd131d3cf | |||
| 67c4256c08 | |||
| 16e6036536 | |||
| 18a07d2d7c | |||
| defab5114d | |||
| 24197831e6 | |||
| 2d69eaee74 | |||
| 7f5de5d141 | |||
| 43aa8de22b | |||
| f53a96be5d | |||
| 9c2e8aff0f | |||
| 6bf4745144 | |||
| 9cb3ea44fb | |||
| 764e621b63 | |||
| a38e0405f1 | |||
| d9a7c75ae5 | |||
| 963a776fa6 | |||
| d5a47925ab | |||
| c2ea4e6121 | |||
| 12babb33cb | |||
| 02e73e3b6f | |||
| 9e41ffd537 | |||
| e5a483e4b7 | |||
| 209c971c1b | |||
| a74724193e | |||
| 1550c8d245 | |||
| d495014878 | |||
| affc18a056 | |||
| 25cfb4f790 | |||
| 344bb6af83 | |||
| cccff98454 | |||
| fab9874183 | |||
| 726cb2c6eb | |||
| 57a380d8ec | |||
| 11378639ae | |||
| 9ff373c325 | |||
| d317113dc8 | |||
| c424edd055 | |||
| 4bfc5bce78 | |||
| 59358ae60d | |||
| 2450eb3d95 | |||
| 14e3a2da03 | |||
| bea52104b7 | |||
| c6a2fce9a3 | |||
| c0af8b9952 | |||
| 444897081e | |||
| 1beb6d70f0 | |||
| 106276978d | |||
| 539b1def78 | |||
| 3c961de3fe | |||
| ce564b4448 | |||
| 4a4c15765c | |||
| 5395b3823a | |||
| c1938b8a32 | |||
| 8a8496d57e | |||
| 48525073aa | |||
| cf51a72846 | |||
| fef12c9038 | |||
| 8fbe4a4f7d | |||
| 50c2055efe | |||
| b0b6771118 | |||
| 4226b7d4ec | |||
| 04f4ca8966 | |||
| 54c1d121e4 | |||
| a5c67b90a9 | |||
| 4a0e257fc9 | |||
| bd76832f34 | |||
| 6e418096b7 | |||
| b0b99de767 | |||
| 55d35ddf37 | |||
| 207456fd35 | |||
| a4f9786c3d | |||
| 8fd3d3268a | |||
| af25a31861 | |||
| 77a3ab7aad | |||
| 51be1e3fb9 | |||
| 637cece115 | |||
| d965df930c | |||
| 845a7efd58 | |||
| 30cbf84fd8 | |||
| 76fbac4dee | |||
| 24833f41e5 | |||
| 8d352cd8e0 | |||
| e2708705a8 | |||
| 039ac8c4c1 | |||
| 4880bd5d8c | |||
| 8f870a1346 | |||
| 9048a40277 | |||
| 9fb0ff0e80 | |||
| 7297364484 | |||
| 9e796daee8 | |||
| 17242bc1a4 | |||
| 8a9be9a8f0 | |||
| b346fcfa00 | |||
| 1d852907a2 | |||
| dd177ac8cf | |||
| 68b533f79f | |||
| 7daac63809 | |||
| 2ef5b0a20f | |||
| 2e2067173a | |||
| 93f06f6a23 | |||
| ca0dec5a33 | |||
| 790c2d3369 | |||
| 8478503349 | |||
| 6a18fd045f | |||
| 06942bbfcc | |||
| 89c4980e55 | |||
| e756574420 | |||
| df00901bfa | |||
| 918efc8f97 | |||
| 8602849847 | |||
| d564c5b4c2 | |||
| 245ae58264 | |||
| 996060f581 | |||
| a120773793 | |||
| 6cf8d80bdd | |||
| b4601278db | |||
| 0d66981dcc | |||
| 62f5f5267e | |||
| fd7edaa4fe | |||
| b343eb6a11 | |||
| b8877c4cfc | |||
| 64e996b475 | |||
| 1d45f699be | |||
| be126454c0 | |||
| ce3e62841f | |||
| 657854ae1b | |||
| 5613313107 | |||
| eafb6c396b | |||
| 2ebfd40bba | |||
| 368dd362c5 | |||
| 7332f264f0 | |||
| 06d6c31614 | |||
| d9448f3e99 | |||
| 50a42680d7 | |||
| 70a84a0c1b | |||
| 05de941399 | |||
| adc9a6d35d | |||
| fe91ace0bb | |||
| 977777f40a | |||
| 4afdf38378 | |||
| d0004403a3 | |||
| 2e651117e8 | |||
| 18c176beef | |||
| 602add3ec1 | |||
| 47d5971f42 | |||
| 7ef57f6262 | |||
| 2a0b795760 | |||
| 911fc81c59 | |||
| b2fc5993cc | |||
| d5ddc9135d | |||
| 6b67ccb0ad | |||
| 0e6b904e80 | |||
| 864df9fc7b | |||
| d17a5b2c26 | |||
| d46a9266ce | |||
| 3a6f566e80 | |||
| d84903d520 | |||
| 4331f880f5 | |||
| 11007dc893 | |||
| fd4b958790 | |||
| a06f062bf7 | |||
| 79bf7810d4 | |||
| 066e8f7fea | |||
| 08dc2a776e | |||
| 061fe4806e | |||
| 4bebbbae93 | |||
| e52ce5c43b | |||
| 32c94bd3b1 | |||
| b08fefa903 | |||
| 302d0601bf | |||
| c90811a277 | |||
| 79a7ef2869 | |||
| b9f4de0b51 | |||
| 14cbcee49f | |||
| f38aba3c5b | |||
| 84933cfbdf | |||
| be3e1fb2ef | |||
| 82850cb149 | |||
| 3d457528d2 | |||
| d74007dc87 | |||
| 47b3f5f6f9 | |||
| 6d8441dc89 | |||
| f29afe1342 | |||
| 9849d90877 | |||
| 497704f3b9 | |||
| b60aa5dd2b | |||
| 5d151c54a4 | |||
| 83a4d648e3 | |||
| 0646fa6c9d | |||
| d9b3e48ec5 | |||
| 29fefeeada | |||
| 618a6aa98f | |||
| 8a007d38e7 | |||
| e831c66ae1 | |||
| 021eee0c5c | |||
| 385450ca37 | |||
| 23c9af20da | |||
| d196d37ef2 | |||
| dc4aa749d3 | |||
| 767eb75662 | |||
| c29630f6ae | |||
| c511e72d4f | |||
| a235b5c87f | |||
| 87163c1578 | |||
| 841fccf6b9 | |||
| 8a3e227dc0 | |||
| ad0b6bdc6b | |||
| aecd1461d7 | |||
| 8017baa6e6 | |||
| 103a61450e | |||
| df242827d2 | |||
| d9996742be | |||
| f518f87d0f | |||
| 6a123298e5 | |||
| a38b10c9d0 | |||
| d641cc3986 | |||
| 731d754a15 | |||
| 7edf21414e | |||
| ec278758ed | |||
| 53a14d10cd | |||
| d6c4dd22da | |||
| 4117912506 | |||
| e239c017d1 | |||
| 5f0e3f600f | |||
| 61da2505fe | |||
| 27d831fbcd | |||
| 71fbc63d67 | |||
| a6d50d02bc | |||
| c56bc4208d | |||
| 741892c30f | |||
| ad6ee2aadf | |||
| b3c05bf4da | |||
| 0134d7dd3b | |||
| 07d9167d66 | |||
| e945c91f88 | |||
| 8bb1e44c10 | |||
| 36b5e1e1bb | |||
| 01b348c86b | |||
| 907823f9c5 | |||
| d886db8aab | |||
| 1c929da8ba | |||
| 6362a44cc4 | |||
| 42b0061217 | |||
| caf9feba73 | |||
| f751974b41 | |||
| 3e1b7d4d0a | |||
| 6ef0e57eeb | |||
| 4c866fc9b2 | |||
| 93fd9d4bae | |||
| 2ec9937369 | |||
| 3c80be8e48 | |||
| 89b5a61f79 | |||
| 06bb7a6e1b | |||
| d63f47b5b3 | |||
| 61a67b3a09 | |||
| 0966e92e36 | |||
| 273d3cedac | |||
| 2290096e25 | |||
| 0efded2fa7 | |||
| 1c13f28121 | |||
| 94514bd811 | |||
| 85aa863c56 | |||
| 380ae3047d | |||
| ba72434fe4 | |||
| 60df456168 | |||
| f11ea548da | |||
| 3fdd3e3e43 | |||
| f50d8b258c | |||
| dfcde09899 | |||
| b3c27bc91b | |||
| c66584879c | |||
| 7aca05a531 | |||
| c032138a34 | |||
| bc75b5cc0d | |||
| b70da3968c | |||
| 853fa088de | |||
| e80abe4e29 | |||
| 593192df54 | |||
| 166e7a2fd7 | |||
| 57843adb48 | |||
| 7be82b08c4 | |||
| d530bd5a7f | |||
| b6ace98962 | |||
| 75e2a38a05 | |||
| f73ac7a257 | |||
| 1a46de3a25 | |||
| 239a7bc4f1 | |||
| c5c66ee261 | |||
| 87f1eb69b1 | |||
| 86c38ae384 | |||
| b43a842bc8 | |||
| d227cfc174 | |||
| 24ac02fbac | |||
| bc6cc5f82a | |||
| 90169b932b | |||
| 7f010c1430 | |||
| d018551047 | |||
| 509b4ba96d | |||
| 64e3cb16fa | |||
| 77d8e4f213 | |||
| ecf8191aad | |||
| 3fb1459c33 | |||
| ee196fadb8 | |||
| fd9abf76fd | |||
| a98d68f919 | |||
| 7d2d85e6ac | |||
| 34845a62b0 | |||
| 7715a4ab4c | |||
| ea2ca45cf9 | |||
| 14864814ef | |||
| 3a6a57bd45 | |||
| e0af9f4ef4 | |||
| 0294d4a794 | |||
| d411e1c40d | |||
| 833e4536b7 | |||
| 1b7e12cf90 | |||
| aacb7e35dd | |||
| 1dbee4100a | |||
| dbb00d500f | |||
| 61a8ae80a6 | |||
| f8329ff80d | |||
| b4b1b44c6a | |||
| f5a56ae42f | |||
| c4535683a7 | |||
| f9cdd095b8 | |||
| 61c990c5ee | |||
| 8115cf2597 | |||
| eb43d21730 | |||
| 5e54d727fc | |||
| ecb222c7e7 | |||
| a5a7dd802c | |||
| 7b72335876 | |||
| 446895fdc0 | |||
| 3eb0255922 | |||
| 271d3f01a3 | |||
| e8fca6e991 | |||
| de1314f995 | |||
| 1bfc0da258 | |||
| 41966774c2 | |||
| 82e3322e0c | |||
| e2e89def7e | |||
| d5c4ace615 | |||
| 390c9d6ccf | |||
| 63c4f4ac3e | |||
| 40dc011d82 | |||
| 8d97013822 | |||
| 4e59a060a8 | |||
| f8c1e8b81f | |||
| 6be1a14e06 | |||
| 3075724f9e | |||
| e37a216393 | |||
| 90fec17427 | |||
| 03264ce1a7 | |||
| ac4f419b50 | |||
| 193a4478ed | |||
| 744548e9f2 | |||
| b9bd820186 | |||
| e0f1022681 | |||
| 4c184f309c | |||
| adf1bab518 | |||
| f5c5f2ed8c | |||
| 09d3f05bcd | |||
| b7d9876cd9 | |||
| bd6b0699cc | |||
| 684ba28fbf | |||
| 1797ce4e88 | |||
| 05517935dc | |||
| 4af33cef43 | |||
| c90095e8b4 | |||
| 65cedc8bf8 | |||
| 9be7585add | |||
| 2282330102 | |||
| 0676647d8a | |||
| 50bb31491b | |||
| cac3c3c133 | |||
| c2490e01a5 | |||
| 01d8113f8b | |||
| 091ac918b1 | |||
| c0c265cd1f | |||
| fad4450d7d | |||
| 1cfab8feb2 | |||
| e2864e521f | |||
| 9460625c99 | |||
| 32fc5cb5b9 | |||
| 7a6506f2dd | |||
| a416437561 | |||
| 554b52c6a1 | |||
| fc992385a9 | |||
| 0577a40440 | |||
| 2239c10192 | |||
| 8117fce790 | |||
| 18c3d889fe | |||
| 31c7d70342 | |||
| 6386f10ba2 | |||
| deb9a1ad4e | |||
| f685df32bc | |||
| 432b0e8c58 | |||
| 024b3d27ac | |||
| 5d5e1db97f | |||
| d3da4fe608 | |||
| 76b58b6f0b | |||
| 75d7395daa | |||
| 11f4d5c4e7 | |||
| 522c15284f | |||
| 93ab802202 | |||
| 3381b270ea | |||
| aed8a78c1d | |||
| 9cf07c2452 | |||
| 230276540f |
+76
@@ -105,6 +105,82 @@ Checks: '*,
|
||||
-readability-redundant-declaration,
|
||||
-readability-static-accessed-through-instance,
|
||||
-readability-static-definition-in-anonymous-namespace,
|
||||
-altera-struct-pack-align,
|
||||
-bugprone-easily-swappable-parameters,
|
||||
-concurrency-mt-unsafe,
|
||||
-cppcoreguidelines-avoid-const-or-ref-data-members,
|
||||
-cppcoreguidelines-macro-usage,
|
||||
-cppcoreguidelines-non-private-member-variables-in-classes,
|
||||
-hicpp-uppercase-literal-suffix,
|
||||
-llvm-qualified-auto,
|
||||
-misc-non-private-member-variables-in-classes,
|
||||
-misc-use-anonymous-namespace,
|
||||
-modernize-concat-nested-namespaces,
|
||||
-readability-const-return-type,
|
||||
-readability-identifier-length,
|
||||
-readability-isolate-declaration,
|
||||
-readability-qualified-auto,
|
||||
-readability-redundant-access-specifiers,
|
||||
-cppcoreguidelines-avoid-do-while,
|
||||
-misc-include-cleaner,
|
||||
-misc-const-correctness,
|
||||
-llvm-else-after-return,
|
||||
-readability-function-cognitive-complexity,
|
||||
-cppcoreguidelines-init-variables,
|
||||
-bugprone-reserved-identifier,
|
||||
-cert-dcl37-c,
|
||||
-cert-dcl51-cpp,
|
||||
-modernize-use-nodiscard,
|
||||
-misc-confusable-identifiers,
|
||||
-cert-err33-c,
|
||||
-readability-redundant-inline-specifier,
|
||||
-readability-uppercase-literal-suffix,
|
||||
-bugprone-narrowing-conversions,
|
||||
-cppcoreguidelines-narrowing-conversions,
|
||||
-bugprone-switch-missing-default-case,
|
||||
-cppcoreguidelines-avoid-goto,
|
||||
-hicpp-avoid-goto,
|
||||
-bugprone-branch-clone,
|
||||
-bugprone-unhandled-self-assignment,
|
||||
-cert-oop54-cpp,
|
||||
-performance-enum-size,
|
||||
-readability-avoid-nested-conditional-operator,
|
||||
-cppcoreguidelines-prefer-member-initializer,
|
||||
-cppcoreguidelines-explicit-virtual-functions,
|
||||
-readability-convert-member-functions-to-static,
|
||||
-readability-make-member-function-const,
|
||||
-bugprone-implicit-widening-of-multiplication-result,
|
||||
-bugprone-macro-parentheses,
|
||||
-bugprone-multi-level-implicit-pointer-conversion,
|
||||
-bugprone-signed-char-misuse,
|
||||
-cppcoreguidelines-avoid-non-const-global-variables,
|
||||
-cppcoreguidelines-use-default-member-init,
|
||||
-hicpp-multiway-paths-covered,
|
||||
-hicpp-named-parameter,
|
||||
-misc-header-include-cycle,
|
||||
-misc-no-recursion,
|
||||
-performance-no-int-to-ptr,
|
||||
-readability-avoid-return-with-void-value,
|
||||
-readability-avoid-unconditional-preprocessor-if,
|
||||
-readability-delete-null-pointer,
|
||||
-readability-redundant-casting,
|
||||
-readability-redundant-member-init,
|
||||
-readability-reference-to-constructed-temporary,
|
||||
-readability-simplify-boolean-expr,
|
||||
-cert-msc32-c,
|
||||
-cert-msc33-c,
|
||||
-cert-msc51-cpp,
|
||||
-cert-str34-c,
|
||||
-cppcoreguidelines-macro-to-enum,
|
||||
-modernize-macro-to-enum,
|
||||
-abseil-string-find-str-contains,
|
||||
-bugprone-suspicious-include,
|
||||
-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,
|
||||
-clang-analyzer-optin.core.EnumCastOutOfRange,
|
||||
-modernize-type-traits,
|
||||
-misc-definitions-in-headers,
|
||||
-bugprone-casting-through-void,
|
||||
-readability-redundant-string-init,
|
||||
'
|
||||
WarningsAsErrors: '*'
|
||||
CheckOptions:
|
||||
|
||||
@@ -21,8 +21,10 @@ applyTo: "docs/en/**"
|
||||
- Do not apply bold or italic styling inside a heading.
|
||||
- **Formatting:**
|
||||
- **Bold:** Only for UI elements (buttons, menu items).
|
||||
- **Italics (Emphasis):** For tool names (e.g., *QGroundControl*).
|
||||
- **Inline Code:** Use backticks for file paths, parameters, and CLI commands (e.g., `prettier`).
|
||||
Backticks are optional for hyperlinked CLI commands and tool names.
|
||||
- **Italics (Emphasis):** Use for application names (e.g., *QGroundControl*).
|
||||
Emphasis is optional for hyperlinked applications.
|
||||
- **Structure:** End every line at the end of a sentence (Semantic Line Breaks).
|
||||
|
||||
## Linking & Navigation
|
||||
|
||||
@@ -2,6 +2,37 @@
|
||||
# - If you want to keep the tests running in GitHub Actions you need to uncomment the "runs-on: ubuntu-latest" lines
|
||||
# and comment the "runs-on: [runs-on,runner=..." lines.
|
||||
# - If you would like to duplicate this setup try setting up "RunsOn" on your own AWS account try https://runs-on.com
|
||||
#
|
||||
# ===================================================================================
|
||||
# RELEASE UPLOAD LOGIC
|
||||
# ===================================================================================
|
||||
# This workflow handles building firmware and uploading to S3 + GitHub Releases.
|
||||
#
|
||||
# S3 Bucket Structure (s3://px4-travis/Firmware/):
|
||||
# - master/ <- Latest main branch build (for QGC compatibility)
|
||||
# - stable/ <- Latest stable release, controlled by 'stable' branch
|
||||
# - beta/ <- Latest pre-release, controlled by 'beta' branch
|
||||
# - vX.Y.Z/ <- Archived stable release
|
||||
# - vX.Y.Z-beta1/ <- Archived pre-release
|
||||
#
|
||||
# Trigger Behavior:
|
||||
# - Tag v1.16.1 -> Upload to: v1.16.1/ only (versioned archive)
|
||||
# - Tag v1.17.0-beta1 -> Upload to: v1.17.0-beta1/ only (versioned archive)
|
||||
# - Branch main -> Upload to: master/ (for QGC compatibility)
|
||||
# - Branch stable -> Upload to: stable/ (QGC stable firmware)
|
||||
# - Branch beta -> Upload to: beta/ (QGC beta firmware)
|
||||
# - Branch release/** -> Build only, no S3 upload (CI validation)
|
||||
# - Pull requests -> Build only, no S3 upload (CI validation)
|
||||
#
|
||||
# GitHub Releases:
|
||||
# - All version tags create a draft GitHub Release
|
||||
# - Pre-releases (alpha/beta/rc suffixes) are automatically marked as such
|
||||
#
|
||||
# IMPORTANT: Version tags do NOT upload to stable/ or beta/. Only the
|
||||
# corresponding branch pushes control those directories. This prevents
|
||||
# pre-release tags from accidentally overwriting stable firmware (#26340)
|
||||
# and avoids race conditions between tag and branch builds.
|
||||
# ===================================================================================
|
||||
|
||||
name: Build all targets
|
||||
|
||||
@@ -29,6 +60,7 @@ concurrency:
|
||||
permissions:
|
||||
contents: write
|
||||
actions: read
|
||||
packages: read
|
||||
|
||||
jobs:
|
||||
group_targets:
|
||||
@@ -94,6 +126,9 @@ jobs:
|
||||
fail-fast: false
|
||||
container:
|
||||
image: ${{ matrix.container }}
|
||||
credentials:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/checkout@v4
|
||||
@@ -159,6 +194,13 @@ jobs:
|
||||
path: ~/.ccache
|
||||
key: ${{ steps.cc_restore.outputs.cache-primary-key }}
|
||||
|
||||
# ===========================================================================
|
||||
# ARTIFACT UPLOAD JOB
|
||||
# ===========================================================================
|
||||
# Uploads build artifacts to S3 and creates GitHub Releases.
|
||||
# Runs for version tags (v*), main, stable, and beta branch pushes.
|
||||
# See header comments for full upload logic documentation.
|
||||
# ===========================================================================
|
||||
artifacts:
|
||||
name: Upload Artifacts
|
||||
# runs-on: ubuntu-latest
|
||||
@@ -177,31 +219,31 @@ jobs:
|
||||
- name: Choose Upload Location
|
||||
id: upload-location
|
||||
run: |
|
||||
# Determine upload location based on branch or tag with the following considerations:
|
||||
# Destination: AWS S3 bucket px4-travis in folder Firmware/
|
||||
# - If branch is main -> upload to master/
|
||||
# - Older versions of QGC are hardocded to look for master/
|
||||
# - If branch is stable or beta -> upload to stable/ or beta/
|
||||
# - If a tag vX.Y.Z -> upload to vX.Y.Z/
|
||||
# - Also update stable/ to point to the same version
|
||||
#. - Older versions of QGC are hardocded to look for stable/
|
||||
# - If a pull request -> do not upload
|
||||
set -euo pipefail
|
||||
|
||||
ref="${GITHUB_REF}"
|
||||
branch=${{ needs.group_targets.outputs.branchname }}
|
||||
location="$branch"
|
||||
is_prerelease="false"
|
||||
|
||||
# Main branch uploads to "master" for QGC backward compatibility
|
||||
if [[ "$branch" == "main" ]]; then
|
||||
location="master"
|
||||
fi
|
||||
|
||||
# Version tags: upload to versioned directory (e.g., v1.16.1/)
|
||||
if [[ "$ref" == refs/tags/v[0-9]* ]]; then
|
||||
tag="${ref#refs/tags/}"
|
||||
location="$tag"
|
||||
|
||||
# Pre-release tags contain -alpha, -beta, or -rc suffix
|
||||
if [[ "$tag" =~ -(alpha|beta|rc) ]]; then
|
||||
is_prerelease="true"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "uploadlocation=$location" >> $GITHUB_OUTPUT
|
||||
echo "is_prerelease=$is_prerelease" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Uploading Artifacts to S3 [${{ steps.upload-location.outputs.uploadlocation }}]
|
||||
uses: jakejarvis/s3-sync-action@master
|
||||
@@ -215,28 +257,13 @@ jobs:
|
||||
SOURCE_DIR: artifacts/
|
||||
DEST_DIR: Firmware/${{ steps.upload-location.outputs.uploadlocation }}/
|
||||
|
||||
# if we are uploading artifacts to a versioned folder
|
||||
# we should also update the stable folder in the s3 bucket
|
||||
- name: Uploading Artifacts to S3 [stable]
|
||||
uses: jakejarvis/s3-sync-action@master
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
with:
|
||||
args: --acl public-read
|
||||
env:
|
||||
AWS_S3_BUCKET: 'px4-travis'
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
AWS_REGION: 'us-west-1'
|
||||
SOURCE_DIR: artifacts/
|
||||
DEST_DIR: Firmware/stable/
|
||||
|
||||
# if build is a release triggered by a versioned tag then create a github release
|
||||
# and upload the build artifacts. A draft release is created so that the release
|
||||
# can be reviewed before publishing
|
||||
# Create a draft GitHub Release for all version tags
|
||||
# Pre-releases are automatically marked as such
|
||||
- name: Upload Artifacts to GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
with:
|
||||
draft: true
|
||||
prerelease: ${{ steps.upload-location.outputs.is_prerelease == 'true' }}
|
||||
files: artifacts/*.px4
|
||||
name: ${{ steps.upload-location.outputs.uploadlocation }}
|
||||
|
||||
@@ -19,6 +19,10 @@ concurrency:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
container:
|
||||
image: px4io/px4-dev:v1.16.0-rc1-258-g0369abd556
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -35,20 +39,17 @@ jobs:
|
||||
"px4_sitl_allyes",
|
||||
"module_documentation",
|
||||
]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Building [${{ matrix.check }}]
|
||||
uses: addnab/docker-run-action@v3
|
||||
with:
|
||||
image: px4io/px4-dev:v1.16.0-rc1-258-g0369abd556
|
||||
options: -v ${{ github.workspace }}:/workspace
|
||||
run: |
|
||||
cd /workspace
|
||||
git config --global --add safe.directory /workspace
|
||||
make ${{ matrix.check }}
|
||||
run: |
|
||||
cd "$GITHUB_WORKSPACE"
|
||||
git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||
make ${{ matrix.check }}
|
||||
|
||||
- name: Uploading Coverage to Codecov.io
|
||||
if: contains(matrix.check, 'coverage')
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Clang Tidy
|
||||
name: Static Analysis
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -11,20 +11,59 @@ on:
|
||||
- '**'
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
clang_tidy:
|
||||
name: Clang-Tidy
|
||||
runs-on: [runs-on, runner=16cpu-linux-x64, "run-id=${{ github.run_id }}", "extras=s3-cache"]
|
||||
container:
|
||||
image: px4io/px4-dev:v1.17.0-beta1
|
||||
steps:
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
- name: Testing (clang-tidy)
|
||||
uses: addnab/docker-run-action@v3
|
||||
- name: Configure Git Safe Directory
|
||||
run: git config --system --add safe.directory '*'
|
||||
|
||||
- name: Restore Compiler Cache
|
||||
id: cc_restore
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
image: px4io/px4-dev-clang:2021-09-08
|
||||
options: -v ${{ github.workspace }}:/workspace
|
||||
run: |
|
||||
cd /workspace
|
||||
git config --global --add safe.directory /workspace
|
||||
make clang-tidy
|
||||
path: ~/.ccache
|
||||
key: ccache-clang-tidy-${{ github.head_ref || github.ref_name }}
|
||||
restore-keys: |
|
||||
ccache-clang-tidy-${{ github.head_ref || github.ref_name }}-
|
||||
ccache-clang-tidy-main-
|
||||
ccache-clang-tidy-
|
||||
|
||||
- name: Configure Compiler Cache
|
||||
run: |
|
||||
mkdir -p ~/.ccache
|
||||
echo "base_dir = ${GITHUB_WORKSPACE}" > ~/.ccache/ccache.conf
|
||||
echo "compression = true" >> ~/.ccache/ccache.conf
|
||||
echo "compression_level = 6" >> ~/.ccache/ccache.conf
|
||||
echo "max_size = 120M" >> ~/.ccache/ccache.conf
|
||||
echo "hash_dir = false" >> ~/.ccache/ccache.conf
|
||||
echo "compiler_check = content" >> ~/.ccache/ccache.conf
|
||||
ccache -s
|
||||
ccache -z
|
||||
|
||||
- name: Run Clang-Tidy Analysis
|
||||
run: make -j16 clang-tidy
|
||||
|
||||
- name: Compiler Cache Stats
|
||||
if: always()
|
||||
run: ccache -s
|
||||
|
||||
- name: Save Compiler Cache
|
||||
if: always()
|
||||
uses: actions/cache/save@v4
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ${{ steps.cc_restore.outputs.cache-primary-key }}
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
name: Commit Quality
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, edited, synchronize, reopened]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
IS_FORK: ${{ github.event.pull_request.head.repo.full_name != github.repository }}
|
||||
|
||||
jobs:
|
||||
pr-title:
|
||||
name: PR Title
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: Tools/ci
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Check PR title
|
||||
id: check
|
||||
run: |
|
||||
python3 Tools/ci/check_pr_title.py "${{ github.event.pull_request.title }}" --markdown-file comment.md && rc=0 || rc=$?
|
||||
echo "exit_code=$rc" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Post or clear comment
|
||||
if: env.IS_FORK == 'false'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
if [ "${{ steps.check.outputs.exit_code }}" != "0" ]; then
|
||||
python3 Tools/ci/pr_comment.py --marker pr-title --pr "$PR_NUMBER" --result fail < comment.md
|
||||
else
|
||||
python3 Tools/ci/pr_comment.py --marker pr-title --pr "$PR_NUMBER" --result pass
|
||||
fi
|
||||
|
||||
- name: Result
|
||||
if: steps.check.outputs.exit_code != '0'
|
||||
run: |
|
||||
echo "::error::PR title does not follow conventional commits format. See the PR comment for details."
|
||||
exit 1
|
||||
|
||||
commit-messages:
|
||||
name: Commit Messages
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: Tools/ci
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Check commit messages
|
||||
id: check
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
gh api \
|
||||
"repos/${{ github.repository }}/pulls/${PR_NUMBER}/commits?per_page=100" \
|
||||
| python3 Tools/ci/check_commit_messages.py --markdown-file comment.md && rc=0 || rc=$?
|
||||
echo "exit_code=$rc" >> "$GITHUB_OUTPUT"
|
||||
# Check for warnings (non-empty markdown on exit 0)
|
||||
if [ "$rc" -eq 0 ] && [ -s comment.md ]; then
|
||||
echo "has_warnings=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "has_warnings=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Post or clear comment
|
||||
if: env.IS_FORK == 'false'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
if [ "${{ steps.check.outputs.exit_code }}" != "0" ]; then
|
||||
python3 Tools/ci/pr_comment.py --marker commit-msgs --pr "$PR_NUMBER" --result fail < comment.md
|
||||
elif [ "${{ steps.check.outputs.has_warnings }}" == "true" ]; then
|
||||
python3 Tools/ci/pr_comment.py --marker commit-msgs --pr "$PR_NUMBER" --result warn < comment.md
|
||||
else
|
||||
python3 Tools/ci/pr_comment.py --marker commit-msgs --pr "$PR_NUMBER" --result pass
|
||||
fi
|
||||
|
||||
- name: Result
|
||||
if: steps.check.outputs.exit_code != '0'
|
||||
run: |
|
||||
echo "::error::Commit message errors found. See the PR comment for details."
|
||||
exit 1
|
||||
|
||||
pr-body:
|
||||
name: PR Description
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
if: env.IS_FORK == 'false'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: Tools/ci
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Check PR body
|
||||
id: check
|
||||
env:
|
||||
PR_BODY: ${{ github.event.pull_request.body }}
|
||||
run: |
|
||||
message=""
|
||||
if [ -z "$PR_BODY" ]; then
|
||||
message="PR description is empty. Please add a summary of the changes."
|
||||
echo "::warning::PR description is empty."
|
||||
else
|
||||
cleaned=$(echo "$PR_BODY" | sed 's/<!--.*-->//g' | tr -d '[:space:]')
|
||||
if [ -z "$cleaned" ]; then
|
||||
message="PR description contains only template comments. Please fill in the details."
|
||||
echo "::warning::PR description contains only template comments."
|
||||
fi
|
||||
fi
|
||||
echo "message=$message" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Post or clear comment
|
||||
if: env.IS_FORK == 'false'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
if [ -n "${{ steps.check.outputs.message }}" ]; then
|
||||
printf '%s\n' \
|
||||
"## PR Description (advisory)" \
|
||||
"" \
|
||||
"This is **not blocking**, but your PR description appears to be empty or incomplete." \
|
||||
"" \
|
||||
"${{ steps.check.outputs.message }}" \
|
||||
"" \
|
||||
"A good PR description helps reviewers understand what changed and why." \
|
||||
"" \
|
||||
"---" \
|
||||
"*This comment will be automatically removed once the issue is resolved.*" \
|
||||
| python3 Tools/ci/pr_comment.py --marker pr-body --pr "$PR_NUMBER" --result warn
|
||||
else
|
||||
python3 Tools/ci/pr_comment.py --marker pr-body --pr "$PR_NUMBER" --result pass
|
||||
fi
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
shell: cmake -P {0}
|
||||
run: |
|
||||
string(TIMESTAMP current_date "%Y-%m-%d-%H;%M;%S" UTC)
|
||||
message("::set-output name=timestamp::${current_date}")
|
||||
file(APPEND "$ENV{GITHUB_OUTPUT}" "timestamp=${current_date}\n")
|
||||
- name: ccache cache files
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
|
||||
@@ -0,0 +1,416 @@
|
||||
# Docs - Orchestrator
|
||||
#
|
||||
# Trigger paths:
|
||||
# push (main, release/**) → metadata-regen → build-site → deploy-aws
|
||||
# pull_request → detect-changes → pr-metadata-regen → link-check → build-site (if docs/source changed)
|
||||
# workflow_dispatch → metadata-regen → build-site → deploy-aws
|
||||
#
|
||||
# Container jobs (pr-metadata-regen, metadata-regen) run in px4-dev image and
|
||||
# require safe.directory + fetch-depth: 0 for git operations.
|
||||
|
||||
name: Docs - Orchestrator
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
- "release/**"
|
||||
paths:
|
||||
- "docs/**"
|
||||
- "src/**"
|
||||
- "msg/**"
|
||||
- "ROMFS/**"
|
||||
- "Tools/module_config/**"
|
||||
- ".github/workflows/docs-orchestrator.yml"
|
||||
pull_request:
|
||||
paths:
|
||||
- "docs/**"
|
||||
- ".github/workflows/docs-orchestrator.yml"
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: docs-orchestrator-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# =============================================================================
|
||||
# Detect Changes (PR only)
|
||||
# =============================================================================
|
||||
detect-changes:
|
||||
name: "T1: Detect Changes"
|
||||
if: github.event_name == 'pull_request'
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
source_changed: ${{ steps.changes.outputs.source }}
|
||||
docs_changed: ${{ steps.changes.outputs.docs }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dorny/paths-filter@v3
|
||||
id: changes
|
||||
with:
|
||||
filters: |
|
||||
source:
|
||||
- 'src/**'
|
||||
- 'msg/**'
|
||||
- 'ROMFS/**'
|
||||
- 'Tools/module_config/**'
|
||||
docs:
|
||||
- 'docs/**'
|
||||
|
||||
# =============================================================================
|
||||
# PR Metadata Regen (conditional - only when PR touches source files)
|
||||
# =============================================================================
|
||||
pr-metadata-regen:
|
||||
name: "T2: PR Metadata"
|
||||
needs: [detect-changes]
|
||||
if: github.event_name == 'pull_request' && needs.detect-changes.outputs.source_changed == 'true'
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: [runs-on,runner=4cpu-linux-x64,image=ubuntu24-full-x64,"run-id=${{ github.run_id }}",spot=false,extras=s3-cache]
|
||||
container:
|
||||
image: px4io/px4-dev:v1.17.0-beta1
|
||||
steps:
|
||||
- uses: runs-on/action@v1
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: Git ownership workaround
|
||||
run: git config --system --add safe.directory '*'
|
||||
|
||||
- name: Cache Restore - ccache
|
||||
id: cache-ccache
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ccache-docs-metadata-${{ github.sha }}
|
||||
restore-keys: |
|
||||
ccache-docs-metadata-
|
||||
|
||||
- name: Setup ccache
|
||||
run: |
|
||||
mkdir -p ~/.ccache
|
||||
echo "max_size = 1G" > ~/.ccache/ccache.conf
|
||||
|
||||
- name: Build px4_sitl_default
|
||||
run: |
|
||||
make px4_sitl_default
|
||||
env:
|
||||
CCACHE_DIR: ~/.ccache
|
||||
|
||||
- name: Cache Save - ccache
|
||||
uses: actions/cache/save@v4
|
||||
if: always()
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ccache-docs-metadata-${{ github.sha }}
|
||||
|
||||
- name: Generate and sync metadata
|
||||
run: Tools/ci/metadata_sync.sh --generate --sync parameters airframes modules msg_docs failsafe_web
|
||||
env:
|
||||
CCACHE_DIR: ~/.ccache
|
||||
|
||||
- name: Upload metadata artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pr-metadata
|
||||
path: docs/
|
||||
retention-days: 1
|
||||
|
||||
# =============================================================================
|
||||
# Push Metadata Regen (main/release branches)
|
||||
# =============================================================================
|
||||
metadata-regen:
|
||||
name: "T2: Metadata Sync"
|
||||
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
|
||||
permissions:
|
||||
contents: write
|
||||
runs-on: [runs-on,runner=4cpu-linux-x64,image=ubuntu24-full-x64,"run-id=${{ github.run_id }}",spot=false,extras=s3-cache]
|
||||
container:
|
||||
image: px4io/px4-dev:v1.17.0-beta1
|
||||
steps:
|
||||
- uses: runs-on/action@v1
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
token: ${{ secrets.PX4BUILTBOT_PERSONAL_ACCESS_TOKEN }}
|
||||
|
||||
- name: Git ownership workaround
|
||||
run: git config --system --add safe.directory '*'
|
||||
|
||||
- name: Cache Restore - ccache
|
||||
id: cache-ccache
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ccache-docs-metadata-${{ github.sha }}
|
||||
restore-keys: |
|
||||
ccache-docs-metadata-
|
||||
|
||||
- name: Setup ccache
|
||||
run: |
|
||||
mkdir -p ~/.ccache
|
||||
echo "max_size = 1G" > ~/.ccache/ccache.conf
|
||||
|
||||
- name: Build px4_sitl_default
|
||||
run: |
|
||||
make px4_sitl_default
|
||||
env:
|
||||
CCACHE_DIR: ~/.ccache
|
||||
|
||||
- name: Cache Save - ccache
|
||||
uses: actions/cache/save@v4
|
||||
if: always()
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ccache-docs-metadata-${{ github.sha }}
|
||||
|
||||
- name: Generate and sync metadata
|
||||
run: Tools/ci/metadata_sync.sh --generate --sync parameters airframes modules msg_docs failsafe_web
|
||||
env:
|
||||
CCACHE_DIR: ~/.ccache
|
||||
|
||||
- name: Install Node.js and Yarn
|
||||
run: |
|
||||
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
|
||||
apt-get install -y nodejs
|
||||
corepack enable
|
||||
|
||||
- name: Format markdown with Prettier
|
||||
run: |
|
||||
cd docs
|
||||
yarn install --frozen-lockfile
|
||||
yarn prettier --write "en/**/*.md"
|
||||
|
||||
- name: Commit and push changes
|
||||
run: |
|
||||
git config --global user.name "${{ secrets.PX4BUILDBOT_USER }}"
|
||||
git config --global user.email "${{ secrets.PX4BUILDBOT_EMAIL }}"
|
||||
git add docs/
|
||||
if git diff --staged --quiet; then
|
||||
echo "No changes to commit"
|
||||
else
|
||||
git commit -m "docs: auto-sync metadata [skip ci]
|
||||
|
||||
Co-Authored-By: PX4 BuildBot <${{ secrets.PX4BUILDBOT_EMAIL }}>"
|
||||
git push
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# Link Check
|
||||
# =============================================================================
|
||||
link-check:
|
||||
name: "T2: Link Check"
|
||||
needs: [detect-changes, pr-metadata-regen]
|
||||
if: always() && (github.event_name == 'pull_request')
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download metadata artifact
|
||||
if: needs.pr-metadata-regen.result == 'success'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: pr-metadata
|
||||
path: docs/
|
||||
|
||||
- name: Get changed doc files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v46.0.5
|
||||
with:
|
||||
json: true
|
||||
write_output_files: true
|
||||
output_dir: ./logs
|
||||
base_sha: ${{ github.event.pull_request.base.sha }}
|
||||
sha: ${{ github.event.pull_request.head.sha }}
|
||||
files: |
|
||||
docs/en/**/*.md
|
||||
|
||||
- name: Save changed files list
|
||||
run: |
|
||||
mv ./logs/all_changed_files.json ./logs/prFiles.json
|
||||
echo "Changed files:"
|
||||
cat ./logs/prFiles.json
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Run filtered link checker (changed files)
|
||||
run: |
|
||||
npm -g install markdown_link_checker_sc@0.0.138
|
||||
if [ "$(jq length ./logs/prFiles.json)" -gt 0 ]; then
|
||||
markdown_link_checker_sc \
|
||||
-r "$GITHUB_WORKSPACE" \
|
||||
-d docs \
|
||||
-e en \
|
||||
-f ./logs/prFiles.json \
|
||||
-i assets \
|
||||
-u docs.px4.io/main/ \
|
||||
> ./logs/filtered-link-check-results.md || true
|
||||
fi
|
||||
if [ ! -s ./logs/filtered-link-check-results.md ]; then
|
||||
echo "No broken links found in changed files." > ./logs/filtered-link-check-results.md
|
||||
fi
|
||||
cat ./logs/filtered-link-check-results.md
|
||||
|
||||
- name: Run full link checker
|
||||
run: |
|
||||
markdown_link_checker_sc \
|
||||
-r "$GITHUB_WORKSPACE" \
|
||||
-d docs \
|
||||
-e en \
|
||||
-i assets \
|
||||
-u docs.px4.io/main/ \
|
||||
> ./logs/link-check-results.md || true
|
||||
cat ./logs/link-check-results.md
|
||||
|
||||
- name: Post PR comment with link check results
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
uses: marocchino/sticky-pull-request-comment@v2
|
||||
with:
|
||||
header: flaws
|
||||
path: ./logs/filtered-link-check-results.md
|
||||
|
||||
- name: Upload link check results
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: link-check-results
|
||||
path: logs/
|
||||
retention-days: 7
|
||||
|
||||
# =============================================================================
|
||||
# Build Site
|
||||
# =============================================================================
|
||||
build-site:
|
||||
name: "T3: Build Site"
|
||||
needs: [detect-changes, pr-metadata-regen, metadata-regen, link-check]
|
||||
if: >-
|
||||
always() &&
|
||||
(needs.metadata-regen.result == 'success' || needs.metadata-regen.result == 'skipped') &&
|
||||
(needs.link-check.result == 'success' || needs.link-check.result == 'skipped') &&
|
||||
(github.event_name != 'pull_request' || needs.detect-changes.outputs.docs_changed == 'true' || needs.detect-changes.outputs.source_changed == 'true')
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: [runs-on,runner=4cpu-linux-x64,image=ubuntu24-full-x64,"run-id=${{ github.run_id }}",spot=false,extras=s3-cache]
|
||||
outputs:
|
||||
branchname: ${{ steps.set-branch.outputs.branchname }}
|
||||
releaseversion: ${{ steps.set-version.outputs.releaseversion }}
|
||||
steps:
|
||||
- uses: runs-on/action@v1
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Download metadata artifact (PR)
|
||||
if: github.event_name == 'pull_request' && needs.pr-metadata-regen.result == 'success'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: pr-metadata
|
||||
path: docs/
|
||||
|
||||
- id: set-branch
|
||||
run: echo "branchname=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT
|
||||
|
||||
- id: set-version
|
||||
run: |
|
||||
branch="${{ steps.set-branch.outputs.branchname }}"
|
||||
if [[ "$branch" == "main" ]]; then
|
||||
version="main"
|
||||
elif [[ "$branch" =~ ^release/ ]]; then
|
||||
version="v${branch#release/}"
|
||||
elif [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||
version="main"
|
||||
else
|
||||
echo "::error::Unsupported branch for docs deploy: $branch (expected main or release/*)"
|
||||
exit 1
|
||||
fi
|
||||
echo "releaseversion=$version" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
cache-dependency-path: ./docs/yarn.lock
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile --cwd ./docs
|
||||
|
||||
- name: Build with VitePress
|
||||
working-directory: ./docs
|
||||
env:
|
||||
BRANCH_NAME: ${{ steps.set-version.outputs.releaseversion }}
|
||||
run: |
|
||||
npm run docs:build_ubuntu
|
||||
touch .vitepress/dist/.nojekyll
|
||||
npm run docs:sitemap
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: px4_docs_build
|
||||
path: docs/.vitepress/dist/
|
||||
retention-days: 1
|
||||
|
||||
# =============================================================================
|
||||
# Deploy to AWS (push + workflow_dispatch)
|
||||
# =============================================================================
|
||||
deploy-aws:
|
||||
name: "T4: Deploy"
|
||||
needs: [metadata-regen, build-site]
|
||||
if: >-
|
||||
always() &&
|
||||
needs.metadata-regen.result == 'success' &&
|
||||
needs.build-site.result == 'success' &&
|
||||
(github.event_name == 'push' || github.event_name == 'workflow_dispatch')
|
||||
permissions:
|
||||
id-token: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download Artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: px4_docs_build
|
||||
path: ~/_book
|
||||
|
||||
- name: Configure AWS from OIDC
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
|
||||
aws-region: us-west-2
|
||||
|
||||
- name: Sanity check AWS credentials
|
||||
run: aws sts get-caller-identity
|
||||
|
||||
- name: Upload HTML with short cache
|
||||
run: |
|
||||
aws s3 sync ~/_book/ s3://px4-docs/${{ needs.build-site.outputs.releaseversion }}/ \
|
||||
--delete \
|
||||
--exclude "*" --include "*.html" \
|
||||
--cache-control "public, max-age=60"
|
||||
|
||||
- name: Upload assets with long cache
|
||||
run: |
|
||||
aws s3 sync ~/_book/ s3://px4-docs/${{ needs.build-site.outputs.releaseversion }}/ \
|
||||
--delete \
|
||||
--exclude "*.html" \
|
||||
--cache-control "public, max-age=86400, immutable"
|
||||
@@ -34,13 +34,13 @@ jobs:
|
||||
upload_sources: false
|
||||
upload_translations: false
|
||||
download_translations: true
|
||||
commit_message: New Crowdin translations - ${{ matrix.lc }}
|
||||
commit_message: 'docs(i18n): PX4 guide translations (Crowdin) - ${{ matrix.lc }}'
|
||||
localization_branch_name: l10n_crowdin_docs_translations_${{ matrix.lc }}
|
||||
crowdin_branch_name: main
|
||||
create_pull_request: true
|
||||
pull_request_base_branch_name: 'main'
|
||||
pull_request_title: New PX4 guide translations (Crowdin) - ${{ matrix.lc }}
|
||||
pull_request_body: 'New PX4 guide Crowdin translations by [Crowdin GH Action](https://github.com/crowdin/github-action) for ${{ matrix.lc }}'
|
||||
pull_request_title: 'docs(i18n): PX4 guide translations (Crowdin) - ${{ matrix.lc }}'
|
||||
pull_request_body: 'docs(i18n): PX4 guide Crowdin translations by [Crowdin GH Action](https://github.com/crowdin/github-action) for ${{ matrix.lc }}'
|
||||
pull_request_labels: 'Documentation 📑'
|
||||
pull_request_reviewers: hamishwillee
|
||||
download_language: ${{ matrix.lc }}
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
name: Docs - Deploy PX4 User Guide to AWS
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
- "release/**"
|
||||
paths:
|
||||
- "docs/en/**"
|
||||
- "docs/zh/**"
|
||||
- "docs/uk/**"
|
||||
- "docs/ko/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- "docs/en/**"
|
||||
- "docs/zh/**"
|
||||
- "docs/uk/**"
|
||||
- "docs/ko/**"
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
id-token: write # for AWS OIDC
|
||||
|
||||
concurrency:
|
||||
group: docs-deploy
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: [runs-on,runner=4cpu-linux-x64,image=ubuntu24-full-x64,"run-id=${{ github.run_id }}",spot=false,extras=s3-cache]
|
||||
outputs:
|
||||
branchname: ${{ steps.set-branch.outputs.branchname }}
|
||||
releaseversion: ${{ steps.set-version.outputs.releaseversion }}
|
||||
steps:
|
||||
- uses: runs-on/action@v1
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- id: set-branch
|
||||
run: echo "branchname=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT
|
||||
|
||||
- id: set-version
|
||||
run: |
|
||||
branch="${{ steps.set-branch.outputs.branchname }}"
|
||||
if [[ "$branch" == "main" ]]; then
|
||||
version="main"
|
||||
else
|
||||
version="v${branch#release/}"
|
||||
fi
|
||||
echo "releaseversion=$version" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
cache-dependency-path: ./docs/yarn.lock
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile --cwd ./docs
|
||||
|
||||
- name: Build with VitePress
|
||||
working-directory: ./docs
|
||||
env:
|
||||
BRANCH_NAME: ${{ steps.set-version.outputs.releaseversion }}
|
||||
run: |
|
||||
npm run docs:build_ubuntu
|
||||
touch .vitepress/dist/.nojekyll
|
||||
npm run docs:sitemap
|
||||
|
||||
- name: Upload artifact
|
||||
if: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.merged) || github.event_name == 'workflow_dispatch' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: px4_docs_build
|
||||
path: docs/.vitepress/dist/
|
||||
retention-days: 1
|
||||
|
||||
deploy:
|
||||
if: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.merged) || github.event_name == 'workflow_dispatch' }}
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Download Artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: px4_docs_build
|
||||
path: ~/_book
|
||||
|
||||
- name: Configure AWS from OIDC
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
|
||||
aws-region: us-west-2
|
||||
|
||||
- name: Sanity check AWS credentials
|
||||
run: aws sts get-caller-identity
|
||||
|
||||
- name: Upload HTML with short cache
|
||||
run: |
|
||||
aws s3 sync ~/_book/ s3://px4-docs/${{ needs.build.outputs.releaseversion }}/ \
|
||||
--delete \
|
||||
--exclude "*" --include "*.html" \
|
||||
--cache-control "public, max-age=60"
|
||||
|
||||
- name: Upload assets with long cache
|
||||
run: |
|
||||
aws s3 sync ~/_book/ s3://px4-docs/${{ needs.build.outputs.releaseversion }}/ \
|
||||
--delete \
|
||||
--exclude "*.html" \
|
||||
--cache-control "public, max-age=86400, immutable"
|
||||
@@ -1,85 +0,0 @@
|
||||
name: Docs - Check for flaws in PX4 Guide Source
|
||||
# So far:
|
||||
# Modifications of translations files
|
||||
# Broken internal links
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, edited, synchronize]
|
||||
paths:
|
||||
- 'docs/en/**'
|
||||
|
||||
jobs:
|
||||
check_flaws:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18'
|
||||
|
||||
- name: Create logs directory
|
||||
run: |
|
||||
mkdir logs
|
||||
|
||||
- name: Get changed english doc files
|
||||
id: get_changed_markdown_english
|
||||
uses: tj-actions/changed-files@v46.0.5
|
||||
with:
|
||||
json: true
|
||||
base_sha: "${{ github.event.pull_request.base.sha }}"
|
||||
sha: "${{ github.event.pull_request.head.sha }}"
|
||||
# Below are used to output files to a directory. May use in flaw checker.
|
||||
# write_output_files: true
|
||||
# output_dir: "./logs"
|
||||
files: |
|
||||
docs/en/**/*.md
|
||||
|
||||
- name: Save JSON file containing files to link check
|
||||
run: |
|
||||
echo "$ALL_CHANGED_FILES"
|
||||
# echo "$ALL_CHANGED_FILES" > ./logs/prFiles.json
|
||||
echo "$ALL_CHANGED_FILES" | sed 's/\\//g' | jq '.' > ./logs/prFiles.json
|
||||
env:
|
||||
ALL_CHANGED_FILES: ${{ steps.get_changed_markdown_english.outputs.all_changed_files }}
|
||||
|
||||
- name: Run link checker
|
||||
id: link-check
|
||||
run: |
|
||||
npm -g install markdown_link_checker_sc@0.0.138
|
||||
markdown_link_checker_sc \
|
||||
-r "$GITHUB_WORKSPACE" \
|
||||
-d docs \
|
||||
-e en \
|
||||
-f ./logs/prFiles.json \
|
||||
-i assets \
|
||||
-u docs.px4.io/main/ \
|
||||
> ./logs/errorsFilteredByPrPages.md
|
||||
mkdir -p ./pr
|
||||
cp ./logs/errorsFilteredByPrPages.md ./pr/errorsFilteredByPrPages.md
|
||||
|
||||
- name: Read errorsFilteredByPrPages.md file
|
||||
id: read-errors-by-page
|
||||
uses: juliangruber/read-file-action@v1
|
||||
with:
|
||||
path: ./logs/errorsFilteredByPrPages.md
|
||||
|
||||
- name: Echo Errors by Page
|
||||
run: echo "$ERRORS"
|
||||
env:
|
||||
ERRORS: ${{ steps.read-errors-by-page.outputs.content }}
|
||||
|
||||
- name: Save PR number
|
||||
run: echo "$PR_NUMBER" > ./pr/pr_number
|
||||
env:
|
||||
PR_NUMBER: ${{ github.event.number }}
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pr_number
|
||||
path: pr/
|
||||
@@ -1,111 +0,0 @@
|
||||
name: Docs - Comment Workflow
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["Docs - Check for flaws in PX4 Guide Source"]
|
||||
types:
|
||||
- completed
|
||||
|
||||
jobs:
|
||||
comment:
|
||||
permissions:
|
||||
pull-requests: write # for marocchino/sticky-pull-request-comment
|
||||
name: Comments
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 'Download PR artifact'
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
run_id: context.payload.workflow_run.id,
|
||||
});
|
||||
let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
|
||||
return artifact.name == "pr_number"
|
||||
})[0];
|
||||
let download = await github.rest.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: matchArtifact.id,
|
||||
archive_format: 'zip',
|
||||
});
|
||||
let fs = require('fs');
|
||||
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/pr_number.zip`, Buffer.from(download.data));
|
||||
|
||||
- name: 'Unzip artifact'
|
||||
run: unzip pr_number.zip
|
||||
|
||||
# Doesn't work across workflows
|
||||
#- name: Get artifacts from flaw checker workflow
|
||||
# uses: actions/download-artifact@v3
|
||||
# with:
|
||||
# name: logs_and_errors
|
||||
# #path: ./logs
|
||||
|
||||
- name: Read errorsFilteredByPrPages.md file
|
||||
id: read-errors-by-page
|
||||
uses: juliangruber/read-file-action@v1
|
||||
with:
|
||||
path: ./errorsFilteredByPrPages.md
|
||||
|
||||
- name: Read PR number
|
||||
id: read-error-pr-number
|
||||
uses: juliangruber/read-file-action@v1
|
||||
with:
|
||||
path: ./pr_number
|
||||
|
||||
- name: File detail info
|
||||
run: |
|
||||
echo "$ERRORS"
|
||||
echo "$PRNUM"
|
||||
env:
|
||||
ERRORS: ${{ steps.read-errors-by-page.outputs.content }}
|
||||
PRNUM: ${{ steps.read-error-pr-number.outputs.content }}
|
||||
|
||||
- name: Create or update comment
|
||||
id: comment_to_pr
|
||||
uses: marocchino/sticky-pull-request-comment@v2
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
recreate: true
|
||||
number: ${{ steps.read-error-pr-number.outputs.content }}
|
||||
header: flaws
|
||||
message: ${{ steps.read-errors-by-page.outputs.content || 'No flaws found' }}
|
||||
|
||||
#- name: Dump GitHub context
|
||||
# env:
|
||||
# GITHUB_CONTEXT: ${{ toJSON(github) }}
|
||||
# run: echo "$GITHUB_CONTEXT"
|
||||
|
||||
# Would like to do this, but it doesn't work (for me).
|
||||
# Moving to time-based, or triggering on workflow
|
||||
#- name: Wait for artifacts upload to succeed
|
||||
# uses: lewagon/wait-on-check-action@v1.3.1
|
||||
# with:
|
||||
# ref: ${{ github.ref }}
|
||||
# check-name: 'Archive production artifacts'
|
||||
# repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# wait-interval: 80
|
||||
|
||||
# Not needed for now - trying to trigger off the workflow
|
||||
#- name: Sleep for 80 seconds
|
||||
# run: sleep 80s
|
||||
# shell: bash
|
||||
#- name: Find Comment
|
||||
# uses: peter-evans/find-comment@v2
|
||||
# id: fc
|
||||
# with:
|
||||
# issue-number: ${{ steps.read-error-pr-number.outputs.content }}
|
||||
# comment-author: 'github-actions[bot]'
|
||||
# body-includes: Flaws (may be none)
|
||||
|
||||
#- name: Create or update comment
|
||||
# uses: peter-evans/create-or-update-comment@v3
|
||||
# with:
|
||||
# comment-id: ${{ steps.fc.outputs.comment-id }}
|
||||
# issue-number: ${{ steps.read-error-pr-number.outputs.content }}
|
||||
# body: |
|
||||
# Flaws (may be none)
|
||||
# ${{ steps.read-errors-by-page.outputs.content }}
|
||||
# edit-mode: replace
|
||||
@@ -15,21 +15,21 @@ concurrency:
|
||||
jobs:
|
||||
unit_tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: main test
|
||||
uses: addnab/docker-run-action@v3
|
||||
with:
|
||||
image: px4io/px4-dev:v1.16.0-rc1-258-g0369abd556
|
||||
options: -v ${{ github.workspace }}:/workspace
|
||||
container:
|
||||
image: px4io/px4-dev:v1.16.0-rc1-258-g0369abd556
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: main test
|
||||
run: |
|
||||
cd /workspace
|
||||
git config --global --add safe.directory /workspace
|
||||
cd "$GITHUB_WORKSPACE"
|
||||
git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||
make tests TESTFILTER=EKF
|
||||
|
||||
- name: Check if there is a functional change
|
||||
run: git diff --exit-code
|
||||
working-directory: src/modules/ekf2/test/change_indication
|
||||
- name: Check if there is a functional change
|
||||
run: git diff --exit-code
|
||||
working-directory: src/modules/ekf2/test/change_indication
|
||||
|
||||
@@ -8,40 +8,47 @@ on:
|
||||
jobs:
|
||||
unit_tests:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
container:
|
||||
image: px4io/px4-dev:v1.16.0-rc1-258-g0369abd556
|
||||
|
||||
env:
|
||||
GIT_COMMITTER_EMAIL: bot@px4.io
|
||||
GIT_COMMITTER_NAME: PX4BuildBot
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: main test
|
||||
uses: addnab/docker-run-action@v3
|
||||
with:
|
||||
image: px4io/px4-dev:v1.16.0-rc1-258-g0369abd556
|
||||
options: -v ${{ github.workspace }}:/workspace
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: main test
|
||||
run: |
|
||||
cd /workspace
|
||||
git config --global --add safe.directory /workspace
|
||||
cd "$GITHUB_WORKSPACE"
|
||||
git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||
make tests TESTFILTER=EKF
|
||||
|
||||
- name: Check if there exists diff and save result in variable
|
||||
id: diff-check
|
||||
run: echo "CHANGE_INDICATED=$(git diff --exit-code --output=/dev/null || echo $?)" >> $GITHUB_OUTPUT
|
||||
working-directory: src/modules/ekf2/test/change_indication
|
||||
- name: Check if there exists diff and save result in variable
|
||||
id: diff-check
|
||||
working-directory: src/modules/ekf2/test/change_indication
|
||||
run: |
|
||||
if git diff --quiet; then
|
||||
echo "CHANGE_INDICATED=false" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "CHANGE_INDICATED=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: auto-commit any changes to change indication
|
||||
uses: stefanzweifel/git-auto-commit-action@v4
|
||||
with:
|
||||
file_pattern: 'src/modules/ekf2/test/change_indication/*.csv'
|
||||
commit_user_name: ${GIT_COMMITTER_NAME}
|
||||
commit_user_email: ${GIT_COMMITTER_EMAIL}
|
||||
commit_message: |
|
||||
'[AUTO COMMIT] update change indication'
|
||||
- name: auto-commit any changes to change indication
|
||||
if: steps.diff-check.outputs.CHANGE_INDICATED == 'true'
|
||||
uses: stefanzweifel/git-auto-commit-action@v4
|
||||
with:
|
||||
file_pattern: 'src/modules/ekf2/test/change_indication/*.csv'
|
||||
commit_user_name: ${{ env.GIT_COMMITTER_NAME }}
|
||||
commit_user_email: ${{ env.GIT_COMMITTER_EMAIL }}
|
||||
commit_message: |
|
||||
[AUTO COMMIT] update change indication
|
||||
|
||||
See .github/workflopws/ekf_update_change_indicator.yml for more details
|
||||
See .github/workflows/ekf_update_change_indicator.yml for more details
|
||||
|
||||
- name: if there is a functional change, fail check
|
||||
if: ${{ steps.diff-check.outputs.CHANGE_INDICATED }}
|
||||
run: exit 1
|
||||
- name: if there is a functional change, fail check
|
||||
if: steps.diff-check.outputs.CHANGE_INDICATED == 'true'
|
||||
run: exit 1
|
||||
|
||||
@@ -19,25 +19,27 @@ concurrency:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- {vehicle: "iris", mission: "MC_mission_box"}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Build SITL and Run Tests
|
||||
uses: addnab/docker-run-action@v3
|
||||
with:
|
||||
image: px4io/px4-dev-ros-melodic:2021-09-08
|
||||
options: -v ${{ github.workspace }}:/workspace
|
||||
- name: Build SITL and Run Tests (inside old ROS container)
|
||||
run: |
|
||||
cd /workspace
|
||||
git config --global --add safe.directory /workspace
|
||||
make px4_sitl_default
|
||||
make px4_sitl_default sitl_gazebo-classic
|
||||
./test/rostest_px4_run.sh mavros_posix_test_mission.test mission:=${{matrix.config.mission}} vehicle:=${{matrix.config.vehicle}}
|
||||
docker run --rm \
|
||||
-v "${GITHUB_WORKSPACE}:/workspace" \
|
||||
-w /workspace \
|
||||
px4io/px4-dev-ros-melodic:2021-09-08 \
|
||||
bash -c '
|
||||
git config --global --add safe.directory /workspace
|
||||
make px4_sitl_default
|
||||
make px4_sitl_default sitl_gazebo-classic
|
||||
./test/rostest_px4_run.sh \
|
||||
mavros_posix_test_mission.test \
|
||||
mission:=MC_mission_box \
|
||||
vehicle:=iris
|
||||
'
|
||||
|
||||
@@ -19,27 +19,26 @@ concurrency:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- {test_file: "mavros_posix_tests_offboard_posctl.test", vehicle: "iris"}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Build PX4 and Run Tests
|
||||
uses: addnab/docker-run-action@v3
|
||||
with:
|
||||
image: px4io/px4-dev-ros-melodic:2021-09-08
|
||||
options: -v ${{ github.workspace }}:/workspace
|
||||
- name: Build SITL and Run Tests (inside old ROS container)
|
||||
run: |
|
||||
cd /workspace
|
||||
git config --global --add safe.directory /workspace
|
||||
make px4_sitl_default
|
||||
make px4_sitl_default sitl_gazebo-classic
|
||||
./test/rostest_px4_run.sh ${{matrix.config.test_file}} vehicle:=${{matrix.config.vehicle}}
|
||||
docker run --rm \
|
||||
-v "${GITHUB_WORKSPACE}:/workspace" \
|
||||
-w /workspace \
|
||||
px4io/px4-dev-ros-melodic:2021-09-08 \
|
||||
bash -c '
|
||||
git config --global --add safe.directory /workspace
|
||||
make px4_sitl_default
|
||||
make px4_sitl_default sitl_gazebo-classic
|
||||
./test/rostest_px4_run.sh \
|
||||
mavros_posix_tests_offboard_posctl.test \
|
||||
vehicle:=iris
|
||||
'
|
||||
|
||||
@@ -19,27 +19,28 @@ concurrency:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
container:
|
||||
image: px4io/px4-dev:v1.16.0-rc1-258-g0369abd556
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
config: [
|
||||
px4_fmu-v5_default,
|
||||
]
|
||||
config:
|
||||
- px4_fmu-v5_default
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Build PX4 and Run Test [${{ matrix.config }}]
|
||||
uses: addnab/docker-run-action@v3
|
||||
with:
|
||||
image: px4io/px4-dev:v1.16.0-rc1-258-g0369abd556
|
||||
options: -v ${{ github.workspace }}:/workspace
|
||||
- name: Build PX4 and Run Test [${{ matrix.config }}]
|
||||
run: |
|
||||
cd /workspace
|
||||
git config --global --add safe.directory /workspace
|
||||
export PX4_EXTRA_NUTTX_CONFIG="CONFIG_NSH_LOGIN_PASSWORD=\"test\";CONFIG_NSH_CONSOLE_LOGIN=y"
|
||||
cd "$GITHUB_WORKSPACE"
|
||||
git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||
export PX4_EXTRA_NUTTX_CONFIG='CONFIG_NSH_LOGIN_PASSWORD="test";CONFIG_NSH_CONSOLE_LOGIN=y'
|
||||
echo "PX4_EXTRA_NUTTX_CONFIG: $PX4_EXTRA_NUTTX_CONFIG"
|
||||
|
||||
make ${{ matrix.config }} nuttx_context
|
||||
|
||||
echo "Check that the config option is set"
|
||||
grep CONFIG_NSH_LOGIN_PASSWORD build/${{ matrix.config }}/NuttX/nuttx/.config
|
||||
|
||||
@@ -90,6 +90,9 @@ jobs:
|
||||
mkdir -p /opt/px4_ws/src
|
||||
cd /opt/px4_ws/src
|
||||
git clone --recursive https://github.com/Auterion/px4-ros2-interface-lib.git
|
||||
# Ignore python packages due to compilation issue (can be enabled when updating ROS)
|
||||
touch px4-ros2-interface-lib/px4_ros2_py/COLCON_IGNORE || true
|
||||
touch px4-ros2-interface-lib/examples/python/COLCON_IGNORE || true
|
||||
cd ..
|
||||
# Copy messages to ROS workspace
|
||||
"${PX4_DIR}/Tools/copy_to_ros_ws.sh" "$(pwd)"
|
||||
|
||||
@@ -33,8 +33,10 @@ jobs:
|
||||
matrix:
|
||||
config:
|
||||
- {model: "iris", latitude: "59.617693", longitude: "-151.145316", altitude: "48", build_type: "RelWithDebInfo" } # Alaska
|
||||
- {model: "tailsitter" , latitude: "29.660316", longitude: "-82.316658", altitude: "30", build_type: "RelWithDebInfo" } # Florida
|
||||
- {model: "standard_vtol", latitude: "47.397742", longitude: "8.545594", altitude: "488", build_type: "Coverage" } # Zurich
|
||||
# VTOL/tailsitter disabled: persistent flaky CI failures (timeouts, erratic
|
||||
# transitions). Re-enable once the test infrastructure is stabilized.
|
||||
# - {model: "tailsitter" , latitude: "29.660316", longitude: "-82.316658", altitude: "30", build_type: "RelWithDebInfo" } # Florida
|
||||
# - {model: "standard_vtol", latitude: "47.397742", longitude: "8.545594", altitude: "488", build_type: "Coverage" } # Zurich
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@@ -109,3 +109,6 @@ src/systemcmds/topic_listener/listener_generated.cpp
|
||||
# colcon
|
||||
log/
|
||||
keys/
|
||||
|
||||
# metadata
|
||||
_emscripten_sdk/
|
||||
|
||||
@@ -109,3 +109,9 @@
|
||||
[submodule "src/lib/rl_tools/rl_tools"]
|
||||
path = src/lib/rl_tools/rl_tools
|
||||
url = https://github.com/rl-tools/rl-tools.git
|
||||
[submodule "libmodal-json"]
|
||||
path = boards/modalai/voxl2/src/lib/mpa/libmodal-json
|
||||
url = https://gitlab.com/voxl-public/voxl-sdk/core-libs/libmodal-json.git
|
||||
[submodule "libmodal-pipe"]
|
||||
path = boards/modalai/voxl2/src/lib/mpa/libmodal-pipe
|
||||
url = https://gitlab.com/voxl-public/voxl-sdk/core-libs/libmodal-pipe.git
|
||||
|
||||
Vendored
+5
@@ -336,6 +336,11 @@ CONFIG:
|
||||
buildType: MinSizeRel
|
||||
settings:
|
||||
CONFIG: cuav_x25-evo_default
|
||||
cuav_x25-super_default:
|
||||
short: cuav_x25-super
|
||||
buildType: MinSizeRel
|
||||
settings:
|
||||
CONFIG: cuav_x25-super_default
|
||||
cubepilot_cubeorange_test:
|
||||
short: cubepilot_cubeorange
|
||||
buildType: MinSizeRel
|
||||
|
||||
+126
-20
@@ -1,44 +1,150 @@
|
||||
# Contributing to PX4 Firmware
|
||||
# Contributing to PX4-Autopilot
|
||||
|
||||
We follow the [Github flow](https://guides.github.com/introduction/flow/) development model.
|
||||
We follow the [GitHub flow](https://guides.github.com/introduction/flow/) development model.
|
||||
|
||||
### Fork the project, then clone your repo
|
||||
## Fork the project, then clone your repo
|
||||
|
||||
First [fork and clone](https://help.github.com/articles/fork-a-repo) the project project.
|
||||
First [fork and clone](https://help.github.com/articles/fork-a-repo) the project.
|
||||
|
||||
### Create a feature branch
|
||||
## Create a feature branch
|
||||
|
||||
*Always* branch off main for new features.
|
||||
Always branch off `main` for new features.
|
||||
|
||||
```
|
||||
git checkout -b mydescriptivebranchname
|
||||
```
|
||||
|
||||
### Edit and build the code
|
||||
## Edit and build the code
|
||||
|
||||
The [developer guide](https://docs.px4.io/main/en/development/development.html) explains how to set up the development environment on Mac OS, Linux or Windows. Please take note of our [coding style](https://docs.px4.io/main/en/contribute/code.html) when editing files.
|
||||
|
||||
### Commit your changes
|
||||
## Commit message convention
|
||||
|
||||
Always write descriptive commit messages and add a fixes or relates note to them with an [issue number](https://github.com/px4/Firmware/issues) (Github will link these then conveniently)
|
||||
PX4 uses [conventional commits](https://www.conventionalcommits.org/) for all commit messages and PR titles.
|
||||
|
||||
**Example:**
|
||||
### Format
|
||||
|
||||
```
|
||||
Change how the attitude controller works
|
||||
|
||||
- Fixes rate feed forward
|
||||
- Allows a local body rate override
|
||||
|
||||
Fixes issue #123
|
||||
type(scope): short description of the change
|
||||
```
|
||||
|
||||
### Test your changes
|
||||
| Part | Rule |
|
||||
|------|------|
|
||||
| **type** | Category of change (see types table below) |
|
||||
| **scope** | The module, driver, board, or area of PX4 affected |
|
||||
| **`!`** (optional) | Append before `:` to mark a breaking change |
|
||||
| **description** | What the change does, at least 5 characters, written in imperative form |
|
||||
|
||||
Since we care about safety, we will regularly ask you for test results. Best is to do a test flight (or bench test where it applies) and upload the logfile from it (on the microSD card in the logs directory) to Google Drive or Dropbox and share the link.
|
||||
### Types
|
||||
|
||||
### Push your changes
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `feat` | A new feature |
|
||||
| `fix` | A bug fix |
|
||||
| `docs` | Documentation only changes |
|
||||
| `style` | Formatting, whitespace, no code change |
|
||||
| `refactor` | Code change that neither fixes a bug nor adds a feature |
|
||||
| `perf` | Performance improvement |
|
||||
| `test` | Adding or correcting tests |
|
||||
| `build` | Build system or external dependencies |
|
||||
| `ci` | CI configuration files and scripts |
|
||||
| `chore` | Other changes that don't modify src or test files |
|
||||
| `revert` | Reverts a previous commit |
|
||||
|
||||
Push changes to your repo and send a [pull request](https://github.com/PX4/Firmware/compare/).
|
||||
### Scopes
|
||||
|
||||
The scope identifies which part of PX4 is affected. Common scopes:
|
||||
|
||||
| Scope | Area |
|
||||
|-------|------|
|
||||
| `ekf2` | Extended Kalman Filter (state estimation) |
|
||||
| `mavlink` | MAVLink messaging protocol |
|
||||
| `commander` | Commander and mode management |
|
||||
| `navigator` | Mission, RTL, Land, and other navigation modes |
|
||||
| `sensors` | Sensor drivers and processing |
|
||||
| `drivers` | Hardware drivers |
|
||||
| `boards/px4_fmu-v6x` | Board-specific changes (use the board name) |
|
||||
| `mc_att_control` | Multicopter attitude control |
|
||||
| `mc_pos_control` | Multicopter position control |
|
||||
| `fw_att_control` | Fixed-wing attitude control |
|
||||
| `vtol` | VTOL-specific logic |
|
||||
| `actuators` | Mixer and actuator output |
|
||||
| `battery` | Battery monitoring and estimation |
|
||||
| `logger` | On-board logging |
|
||||
| `param` | Parameter system |
|
||||
| `simulation` | SITL, Gazebo, SIH |
|
||||
| `ci` | Continuous integration and workflows |
|
||||
| `docs` | Documentation |
|
||||
| `build` | CMake, toolchain, build system |
|
||||
| `uorb` | Inter-module messaging |
|
||||
|
||||
For changes spanning multiple subsystems, use the primary one affected. Look at the directory path of the files you changed to find the right scope: `src/modules/ekf2/` uses `ekf2`, `src/drivers/imu/` uses `drivers/imu`, `.github/workflows/` uses `ci`.
|
||||
|
||||
### Breaking changes
|
||||
|
||||
Append `!` before the colon to indicate a breaking change:
|
||||
|
||||
```
|
||||
feat(ekf2)!: remove deprecated height fusion API
|
||||
```
|
||||
|
||||
### Good commit messages
|
||||
|
||||
```
|
||||
feat(ekf2): add height fusion timeout
|
||||
fix(mavlink): correct BATTERY_STATUS_V2 parsing
|
||||
refactor(navigator): simplify RTL altitude logic
|
||||
ci(workflows): migrate to reusable workflows
|
||||
docs(ekf2): update tuning guide
|
||||
feat(boards/px4_fmu-v6x)!: remove deprecated driver API
|
||||
perf(mc_rate_control): reduce loop latency
|
||||
```
|
||||
|
||||
### Commits to avoid
|
||||
|
||||
These will be flagged by CI and should be squashed or reworded before merging:
|
||||
|
||||
```
|
||||
fix # too vague, no type or scope
|
||||
update # too vague, no type or scope
|
||||
ekf2: fix something # missing type prefix
|
||||
apply suggestions from code review # squash into parent commit
|
||||
do make format # squash into parent commit
|
||||
WIP: trying something # not ready for main
|
||||
oops # not descriptive
|
||||
```
|
||||
|
||||
### PR titles
|
||||
|
||||
The PR title follows the same `type(scope): description` format. This is enforced by CI and is especially important because the PR title becomes the commit message when a PR is squash-merged.
|
||||
|
||||
### Merge policy
|
||||
|
||||
Commits should be atomic and independently revertable. Squash at reviewer discretion for obvious cases (multiple WIP commits, messy review-response history). When your commits are clean and logical, they will be preserved as individual commits on `main`.
|
||||
|
||||
### Cleaning up commits
|
||||
|
||||
If CI flags your commit messages, you can fix them with an interactive rebase:
|
||||
|
||||
```bash
|
||||
# Squash all commits into one:
|
||||
git rebase -i HEAD~N # replace N with the number of commits
|
||||
# mark all commits except the first as 'squash' or 'fixup'
|
||||
# reword the remaining commit to follow the format
|
||||
git push --force-with-lease
|
||||
|
||||
# Or reword specific commits:
|
||||
git rebase -i HEAD~N
|
||||
# mark the bad commits as 'reword'
|
||||
git push --force-with-lease
|
||||
```
|
||||
|
||||
## Test your changes
|
||||
|
||||
Since we care about safety, we will regularly ask you for test results. Best is to do a test flight (or bench test where it applies) and upload the log file from it (on the microSD card in the logs directory) to Google Drive or Dropbox and share the link.
|
||||
|
||||
## Push your changes
|
||||
|
||||
Push changes to your repo and send a [pull request](https://github.com/PX4/PX4-Autopilot/compare/).
|
||||
|
||||
Make sure to provide some testing feedback and if possible the link to a flight log file. Upload flight log files to [Flight Review](http://logs.px4.io) and link the resulting report.
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
## This file should be placed in the root directory of your project.
|
||||
## Then modify the CMakeLists.txt file in the root directory of your
|
||||
## project to incorporate the testing dashboard.
|
||||
##
|
||||
## # The following are required to submit to the CDash dashboard:
|
||||
## ENABLE_TESTING()
|
||||
## INCLUDE(CTest)
|
||||
|
||||
set(CTEST_PROJECT_NAME "PX4 Firmware")
|
||||
set(CTEST_NIGHTLY_START_TIME "00:00:00 EST")
|
||||
|
||||
set(CTEST_DROP_METHOD "http")
|
||||
set(CTEST_DROP_SITE "my.cdash.org")
|
||||
set(CTEST_DROP_LOCATION "/submit.php?project=PX4+Firmware")
|
||||
set(CTEST_DROP_SITE_CDASH TRUE)
|
||||
Vendored
+1
@@ -101,6 +101,7 @@ pipeline {
|
||||
echo $0;
|
||||
git clone https://github.com/emscripten-core/emsdk.git _emscripten_sdk;
|
||||
cd _emscripten_sdk;
|
||||
git checkout 4.0.15;
|
||||
./emsdk install latest;
|
||||
./emsdk activate latest;
|
||||
cd ..;
|
||||
|
||||
@@ -332,6 +332,7 @@ bootloaders_update: \
|
||||
cuav_7-nano_bootloader \
|
||||
cuav_fmu-v6x_bootloader \
|
||||
cuav_x25-evo_bootloader \
|
||||
cuav_x25-super_bootloader \
|
||||
cubepilot_cubeorange_bootloader \
|
||||
cubepilot_cubeorangeplus_bootloader \
|
||||
hkust_nxt-dual_bootloader \
|
||||
@@ -412,7 +413,7 @@ tests:
|
||||
$(call cmake-build,px4_sitl_test)
|
||||
|
||||
# work around lcov bug #316; remove once lcov is fixed (see https://github.com/linux-test-project/lcov/issues/316)
|
||||
LCOBUG = --ignore-errors mismatch
|
||||
LCOBUG = --ignore-errors mismatch,negative
|
||||
tests_coverage:
|
||||
@$(MAKE) clean
|
||||
@$(MAKE) --no-print-directory tests PX4_CMAKE_BUILD_TYPE=Coverage
|
||||
@@ -492,13 +493,29 @@ px4_sitl_default-clang:
|
||||
@cd "$(SRC_DIR)"/build/px4_sitl_default-clang && cmake "$(SRC_DIR)" $(CMAKE_ARGS) -G"$(PX4_CMAKE_GENERATOR)" -DCONFIG=px4_sitl_default -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
|
||||
@$(PX4_MAKE) -C "$(SRC_DIR)"/build/px4_sitl_default-clang
|
||||
|
||||
# Paths to exclude from clang-tidy (auto-generated from .gitmodules + manual additions):
|
||||
# - All submodules (external code we consume, not edit)
|
||||
# - Test code (allowed looser style)
|
||||
# - Example code (educational, not production)
|
||||
# - Vendored third-party code (e.g., CMSIS_5)
|
||||
# - NuttX-only drivers excluded at CMake level (mcp_common); I2C-dependent libs excluded here (smbus)
|
||||
# - GPIO excluded here (NuttX platform headers)
|
||||
# - Emscripten failsafe web build: source path + Unity build path (failsafe_test.dir)
|
||||
# because CMake Unity Builds merge sources into a generated .cxx under build/
|
||||
#
|
||||
# To add manual exclusions, append to CLANG_TIDY_EXCLUDE_EXTRA below.
|
||||
# Submodules are automatically excluded - no action needed when adding new ones.
|
||||
CLANG_TIDY_SUBMODULES := $(shell git config --file .gitmodules --get-regexp path | awk '{print $$2}' | tr '\n' '|' | sed 's/|$$//')
|
||||
CLANG_TIDY_EXCLUDE_EXTRA := src/systemcmds/tests|src/examples|src/modules/gyro_fft/CMSIS_5|src/lib/drivers/smbus|src/drivers/gpio|src/modules/commander/failsafe/emscripten|failsafe_test\.dir|\.pb\.cc
|
||||
CLANG_TIDY_EXCLUDE := $(CLANG_TIDY_SUBMODULES)|$(CLANG_TIDY_EXCLUDE_EXTRA)
|
||||
|
||||
clang-tidy: px4_sitl_default-clang
|
||||
@cd "$(SRC_DIR)"/build/px4_sitl_default-clang && "$(SRC_DIR)"/Tools/run-clang-tidy.py -header-filter=".*\.hpp" -j$(j_clang_tidy) -p .
|
||||
@cd "$(SRC_DIR)"/build/px4_sitl_default-clang && "$(SRC_DIR)"/Tools/run-clang-tidy.py -header-filter=".*\.hpp" -j$(j_clang_tidy) -exclude="$(CLANG_TIDY_EXCLUDE)" -p .
|
||||
|
||||
# to automatically fix a single check at a time, eg modernize-redundant-void-arg
|
||||
# % run-clang-tidy-4.0.py -fix -j4 -checks=-\*,modernize-redundant-void-arg -p .
|
||||
clang-tidy-fix: px4_sitl_default-clang
|
||||
@cd "$(SRC_DIR)"/build/px4_sitl_default-clang && "$(SRC_DIR)"/Tools/run-clang-tidy.py -header-filter=".*\.hpp" -j$(j_clang_tidy) -fix -p .
|
||||
@cd "$(SRC_DIR)"/build/px4_sitl_default-clang && "$(SRC_DIR)"/Tools/run-clang-tidy.py -header-filter=".*\.hpp" -j$(j_clang_tidy) -exclude="$(CLANG_TIDY_EXCLUDE)" -fix -p .
|
||||
|
||||
# TODO: Fix cppcheck errors then try --enable=warning,performance,portability,style,unusedFunction or --enable=all
|
||||
cppcheck: px4_sitl_default
|
||||
@@ -598,3 +615,10 @@ failsafe_web:
|
||||
run_failsafe_web_server: failsafe_web
|
||||
@cd build/px4_sitl_default_failsafe_web && \
|
||||
python3 -m http.server
|
||||
|
||||
# Generate reference documentation for uORB messages
|
||||
.PHONY: msg_docs
|
||||
msg_docs:
|
||||
$(call colorecho,'Generating uORB message reference docs')
|
||||
@mkdir -p build/msg_docs
|
||||
@./Tools/msg/generate_msg_docs.py -d build/msg_docs
|
||||
|
||||
@@ -1,62 +1,109 @@
|
||||
# PX4 Drone Autopilot
|
||||
<p align="center">
|
||||
<a href="https://px4.io">
|
||||
<img src="docs/assets/site/px4_logo.svg" alt="PX4 Autopilot" width="240">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
[](https://github.com/PX4/PX4-Autopilot/releases) [](https://zenodo.org/badge/latestdoi/22634/PX4/PX4-Autopilot)
|
||||
<p align="center">
|
||||
<em>The autopilot stack the industry builds on.</em>
|
||||
</p>
|
||||
|
||||
[](https://github.com/PX4/PX4-Autopilot/actions/workflows/build_all_targets.yml) [](https://github.com/PX4/PX4-Autopilot/actions?query=workflow%3A%22SITL+Tests%22)
|
||||
<p align="center">
|
||||
<a href="https://github.com/PX4/PX4-Autopilot/releases"><img src="https://img.shields.io/github/release/PX4/PX4-Autopilot.svg" alt="Releases"></a>
|
||||
<a href="https://zenodo.org/badge/latestdoi/22634/PX4/PX4-Autopilot"><img src="https://zenodo.org/badge/22634/PX4/PX4-Autopilot.svg" alt="DOI"></a>
|
||||
<a href="https://github.com/PX4/PX4-Autopilot/actions/workflows/build_all_targets.yml"><img src="https://github.com/PX4/PX4-Autopilot/actions/workflows/build_all_targets.yml/badge.svg?branch=main" alt="Build Targets"></a>
|
||||
<a href="https://discord.gg/dronecode"><img src="https://discordapp.com/api/guilds/1022170275984457759/widget.png?style=shield" alt="Discord"></a>
|
||||
</p>
|
||||
|
||||
[](https://discord.gg/dronecode)
|
||||
---
|
||||
|
||||
This repository holds the [PX4](http://px4.io) flight control solution for drones, with the main applications located in the [src/modules](https://github.com/PX4/PX4-Autopilot/tree/main/src/modules) directory. It also contains the PX4 Drone Middleware Platform, which provides drivers and middleware to run drones.
|
||||
## About
|
||||
|
||||
PX4 is highly portable, OS-independent and supports Linux, NuttX and MacOS out of the box.
|
||||
PX4 is an open-source autopilot stack for drones and unmanned vehicles. It supports multirotors, fixed-wing, VTOL, rovers, and many more experimental platforms from racing quads to industrial survey aircraft. It runs on [NuttX](https://nuttx.apache.org/), Linux, and macOS. Licensed under [BSD 3-Clause](LICENSE).
|
||||
|
||||
* Official Website: http://px4.io (License: BSD 3-clause, [LICENSE](https://github.com/PX4/PX4-Autopilot/blob/main/LICENSE))
|
||||
* [Supported airframes](https://docs.px4.io/main/en/airframes/airframe_reference.html) ([portfolio](https://px4.io/ecosystem/commercial-systems/)):
|
||||
* [Multicopters](https://docs.px4.io/main/en/frames_multicopter/)
|
||||
* [Fixed wing](https://docs.px4.io/main/en/frames_plane/)
|
||||
* [VTOL](https://docs.px4.io/main/en/frames_vtol/)
|
||||
* [Autogyro](https://docs.px4.io/main/en/frames_autogyro/)
|
||||
* [Rover](https://docs.px4.io/main/en/frames_rover/)
|
||||
* many more experimental types (Blimps, Boats, Submarines, High Altitude Balloons, Spacecraft, etc)
|
||||
* Releases: [Downloads](https://github.com/PX4/PX4-Autopilot/releases)
|
||||
## Why PX4
|
||||
|
||||
## Releases
|
||||
**Modular architecture.** PX4 is built around [uORB](https://docs.px4.io/main/en/middleware/uorb.html), a [DDS](https://docs.px4.io/main/en/middleware/uxrce_dds.html)-compatible publish/subscribe middleware. Modules are fully parallelized and thread safe. You can build custom configurations and trim what you don't need.
|
||||
|
||||
Release notes and supporting information for PX4 releases can be found on the [Developer Guide](https://docs.px4.io/main/en/releases/).
|
||||
**Wide hardware support.** PX4 runs on a wide range of [autopilot boards](https://docs.px4.io/main/en/flight_controller/) and supports an extensive set of sensors, telemetry radios, and actuators through the [Pixhawk](https://pixhawk.org/) ecosystem.
|
||||
|
||||
## Building a PX4 based drone, rover, boat or robot
|
||||
**Developer friendly.** First-class support for [MAVLink](https://mavlink.io/) and [DDS / ROS 2](https://docs.px4.io/main/en/ros2/) integration. Comprehensive [SITL simulation](https://docs.px4.io/main/en/simulation/), hardware-in-the-loop testing, and [log analysis](https://docs.px4.io/main/en/log/flight_log_analysis.html) tools. An active developer community on [Discord](https://discord.gg/dronecode) and the [weekly dev call](https://docs.px4.io/main/en/contribute/).
|
||||
|
||||
The [PX4 User Guide](https://docs.px4.io/main/en/) explains how to assemble [supported vehicles](https://docs.px4.io/main/en/airframes/airframe_reference.html) and fly drones with PX4. See the [forum and chat](https://docs.px4.io/main/en/#getting-help) if you need help!
|
||||
**Vendor neutral governance.** PX4 is hosted under the [Dronecode Foundation](https://www.dronecode.org/), part of the Linux Foundation. Business-friendly BSD-3 license. No single vendor controls the roadmap.
|
||||
|
||||
## Supported Vehicles
|
||||
|
||||
## Changing Code and Contributing
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://docs.px4.io/main/en/frames_multicopter/">
|
||||
<img src="docs/assets/airframes/types/QuadRotorX.svg" width="50" alt="Multicopter"><br>
|
||||
<sub>Multicopter</sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://docs.px4.io/main/en/frames_plane/">
|
||||
<img src="docs/assets/airframes/types/Plane.svg" width="50" alt="Fixed Wing"><br>
|
||||
<sub>Fixed Wing</sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://docs.px4.io/main/en/frames_vtol/">
|
||||
<img src="docs/assets/airframes/types/VTOLPlane.svg" width="50" alt="VTOL"><br>
|
||||
<sub>VTOL</sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://docs.px4.io/main/en/frames_rover/">
|
||||
<img src="docs/assets/airframes/types/Rover.svg" width="50" alt="Rover"><br>
|
||||
<sub>Rover</sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
This [Developer Guide](https://docs.px4.io/main/en/development/development.html) is for software developers who want to modify the flight stack and middleware (e.g. to add new flight modes), hardware integrators who want to support new flight controller boards and peripherals, and anyone who wants to get PX4 working on a new (unsupported) airframe/vehicle.
|
||||
<sub>…and many more: helicopters, autogyros, airships, submarines, boats, and other experimental platforms. These frames have basic support but are not part of the regular flight-test program. See the <a href="https://docs.px4.io/main/en/airframes/airframe_reference.html">full airframe reference</a>.</sub>
|
||||
|
||||
Developers should read the [Guide for Contributions](https://docs.px4.io/main/en/contribute/).
|
||||
See the [forum and chat](https://docs.px4.io/main/en/#getting-help) if you need help!
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
git clone https://github.com/PX4/PX4-Autopilot.git --recursive
|
||||
cd PX4-Autopilot
|
||||
make px4_sitl
|
||||
```
|
||||
|
||||
## Weekly Dev Call
|
||||
> [!NOTE]
|
||||
> See the [Development Guide](https://docs.px4.io/main/en/development/development.html) for toolchain setup and build options.
|
||||
|
||||
The PX4 Dev Team syncs up on a [weekly dev call](https://docs.px4.io/main/en/contribute/).
|
||||
## Documentation & Resources
|
||||
|
||||
> **Note** The dev call is open to all interested developers (not just the core dev team). This is a great opportunity to meet the team and contribute to the ongoing development of the platform. It includes a QA session for newcomers. All regular calls are listed in the [Dronecode calendar](https://www.dronecode.org/calendar/).
|
||||
| Resource | Description |
|
||||
| --- | --- |
|
||||
| [User Guide](https://docs.px4.io/main/en/) | Build, configure, and fly with PX4 |
|
||||
| [Developer Guide](https://docs.px4.io/main/en/development/development.html) | Modify the flight stack, add peripherals, port to new hardware |
|
||||
| [Airframe Reference](https://docs.px4.io/main/en/airframes/airframe_reference.html) | Full list of supported frames |
|
||||
| [Autopilot Hardware](https://docs.px4.io/main/en/flight_controller/) | Compatible flight controllers |
|
||||
| [Release Notes](https://docs.px4.io/main/en/releases/) | What's new in each release |
|
||||
| [Contribution Guide](https://docs.px4.io/main/en/contribute/) | How to contribute to PX4 |
|
||||
|
||||
## Community
|
||||
|
||||
## Maintenance Team
|
||||
- **Weekly Dev Call** — open to all developers ([Dronecode calendar](https://www.dronecode.org/calendar/))
|
||||
- **Discord** — [Join the Dronecode server](https://discord.gg/dronecode)
|
||||
- **Discussion Forum** — [PX4 Discuss](https://discuss.px4.io/)
|
||||
- **Maintainers** — see [`MAINTAINERS.md`](MAINTAINERS.md)
|
||||
- **Contributor Stats** — [LFX Insights](https://insights.lfx.linuxfoundation.org/foundation/dronecode)
|
||||
|
||||
See the latest list of maintainers on [MAINTAINERS](MAINTAINERS.md) file at the root of the project.
|
||||
## Contributing
|
||||
|
||||
For the latest stats on contributors please see the latest stats for the Dronecode ecosystem in our project dashboard under [LFX Insights](https://insights.lfx.linuxfoundation.org/foundation/dronecode). For information on how to update your profile and affiliations please see the following support link on how to [Complete Your LFX Profile](https://docs.linuxfoundation.org/lfx/my-profile/complete-your-lfx-profile). Dronecode publishes a yearly snapshot of contributions and achievements on its [website under the Reports section](https://dronecode.org).
|
||||
We welcome contributions of all kinds — bug reports, documentation, new features, and code reviews. Please read the [Contribution Guide](https://docs.px4.io/main/en/contribute/) to get started.
|
||||
|
||||
## Supported Hardware
|
||||
## Governance
|
||||
|
||||
For the most up to date information, please visit [PX4 User Guide > Autopilot Hardware](https://docs.px4.io/main/en/flight_controller/).
|
||||
The PX4 Autopilot project is hosted by the [Dronecode Foundation](https://www.dronecode.org/), a [Linux Foundation](https://www.linuxfoundation.org/) Collaborative Project. Dronecode holds all PX4 trademarks and serves as the project's legal guardian, ensuring vendor-neutral stewardship — no single company owns the name or controls the roadmap. The source code is licensed under the [BSD 3-Clause](LICENSE) license, so you are free to use, modify, and distribute it in your own projects.
|
||||
|
||||
## Project Governance
|
||||
|
||||
The PX4 Autopilot project including all of its trademarks is hosted under [Dronecode](https://www.dronecode.org/), part of the Linux Foundation.
|
||||
|
||||
<a href="https://www.dronecode.org/" style="padding:20px" ><img src="https://dronecode.org/wp-content/uploads/sites/24/2020/08/dronecode_logo_default-1.png" alt="Dronecode Logo" width="110px"/></a>
|
||||
<div style="padding:10px"> </div>
|
||||
<p align="center">
|
||||
<a href="https://www.dronecode.org/">
|
||||
<img src="docs/assets/site/dronecode_logo.svg" alt="Dronecode Logo" width="180">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@@ -101,6 +101,7 @@ param set-default NAV_ACC_RAD 5
|
||||
param set-default NAV_DLL_ACT 2
|
||||
|
||||
param set-default VT_FWD_THRUST_EN 4
|
||||
param set-default VT_F_TRANS_THR 0.3
|
||||
param set-default VT_PITCH_MIN -5
|
||||
param set-default VT_F_TRANS_THR 1
|
||||
param set-default VT_TYPE 2
|
||||
param set-default FD_ESCS_EN 0
|
||||
|
||||
@@ -42,23 +42,21 @@ param set-default FD_FAIL_R 0
|
||||
param set-default CA_ROTOR_COUNT 8
|
||||
param set-default CA_R_REV 255
|
||||
|
||||
param set-default CA_ROTOR0_AX -1
|
||||
param set-default CA_ROTOR0_AY 1
|
||||
param set-default CA_ROTOR0_AX 1
|
||||
param set-default CA_ROTOR0_AY -1
|
||||
param set-default CA_ROTOR0_AZ 0
|
||||
param set-default CA_ROTOR0_KM 0
|
||||
param set-default CA_ROTOR0_PX 0.14
|
||||
param set-default CA_ROTOR0_PY 0.10
|
||||
param set-default CA_ROTOR0_PZ 0.06
|
||||
#param set-default CA_ROTOR0_PZ 0.0
|
||||
|
||||
param set-default CA_ROTOR1_AX -1
|
||||
param set-default CA_ROTOR1_AY -1
|
||||
param set-default CA_ROTOR1_AX 1
|
||||
param set-default CA_ROTOR1_AY 1
|
||||
param set-default CA_ROTOR1_AZ 0
|
||||
param set-default CA_ROTOR1_KM 0
|
||||
param set-default CA_ROTOR1_PX 0.14
|
||||
param set-default CA_ROTOR1_PY -0.10
|
||||
param set-default CA_ROTOR1_PZ 0.06
|
||||
#param set-default CA_ROTOR1_PZ 0.0
|
||||
|
||||
param set-default CA_ROTOR2_AX 1
|
||||
param set-default CA_ROTOR2_AY 1
|
||||
@@ -67,7 +65,6 @@ param set-default CA_ROTOR2_KM 0
|
||||
param set-default CA_ROTOR2_PX -0.14
|
||||
param set-default CA_ROTOR2_PY 0.10
|
||||
param set-default CA_ROTOR2_PZ 0.06
|
||||
#param set-default CA_ROTOR2_PZ 0.0
|
||||
|
||||
param set-default CA_ROTOR3_AX 1
|
||||
param set-default CA_ROTOR3_AY -1
|
||||
@@ -79,7 +76,7 @@ param set-default CA_ROTOR3_PZ 0.06
|
||||
|
||||
param set-default CA_ROTOR4_AX 0
|
||||
param set-default CA_ROTOR4_AY 0
|
||||
param set-default CA_ROTOR4_AZ 1
|
||||
param set-default CA_ROTOR4_AZ -1
|
||||
param set-default CA_ROTOR4_KM 0
|
||||
param set-default CA_ROTOR4_PX 0.12
|
||||
param set-default CA_ROTOR4_PY 0.22
|
||||
@@ -103,7 +100,7 @@ param set-default CA_ROTOR6_PZ 0
|
||||
|
||||
param set-default CA_ROTOR7_AX 0
|
||||
param set-default CA_ROTOR7_AY 0
|
||||
param set-default CA_ROTOR7_AZ 1
|
||||
param set-default CA_ROTOR7_AZ -1
|
||||
param set-default CA_ROTOR7_KM 0
|
||||
param set-default CA_ROTOR7_PX -0.12
|
||||
param set-default CA_ROTOR7_PY -0.22
|
||||
|
||||
@@ -123,3 +123,9 @@ if(CONFIG_MODULES_TEMPERATURE_COMPENSATION)
|
||||
rc.thermal_cal
|
||||
)
|
||||
endif()
|
||||
|
||||
if(CONFIG_DRIVERS_VTXTABLE)
|
||||
px4_add_romfs_files(
|
||||
rc.vtxtable
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# @name HolyBro QAV250
|
||||
#
|
||||
# @url https://docs.px4.io/main/en/frames_multicopter/holybro_qav250_pixhawk4_mini.html
|
||||
# @url https://docs.px4.io/main/en/frames_multicopter/holybro_qav250_pixhawk4_mini
|
||||
#
|
||||
# @type Quadrotor x
|
||||
# @class Copter
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# @name Aion Robotics R1 UGV
|
||||
#
|
||||
# @url https://docs.px4.io/main/en/complete_vehicles_rover/aion_r1.html
|
||||
# @url https://docs.px4.io/main/en/complete_vehicles_rover/aion_r1
|
||||
#
|
||||
# @type Rover
|
||||
# @class Rover
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
|
||||
. ${R}etc/init.d/rc.uuv_defaults
|
||||
|
||||
# Overwrite DDS AG IP to `192.168.0.1`
|
||||
param set-default UXRCE_DDS_AG_IP -1062731775
|
||||
|
||||
# param set-default MAV_1_CONFIG 102
|
||||
|
||||
param set-default BAT1_A_PER_V 37.8798
|
||||
|
||||
@@ -10,9 +10,6 @@ set VEHICLE_TYPE uuv
|
||||
# MAV_TYPE_SUBMARINE 12
|
||||
param set-default MAV_TYPE 12
|
||||
|
||||
# Set micro-dds-client to use ethernet and IP-address 192.168.0.1
|
||||
param set-default UXRCE_DDS_AG_IP -1062731775
|
||||
|
||||
# Disable preflight disarm to not interfere with external launching
|
||||
param set-default COM_DISARM_PRFLT -1
|
||||
param set-default CBRK_SUPPLY_CHK 894281
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# VTX table loading script.
|
||||
#
|
||||
# NOTE: Script variables are declared/initialized/unset in the rcS script.
|
||||
#
|
||||
|
||||
vtxtable load
|
||||
@@ -479,6 +479,19 @@ else
|
||||
pwm_out start
|
||||
fi
|
||||
|
||||
#
|
||||
# Optional UAVCAN/DroneCAN or Cyphal
|
||||
#
|
||||
if param greater -s UAVCAN_ENABLE 0
|
||||
then
|
||||
uavcan start
|
||||
else
|
||||
if param greater -s CYPHAL_ENABLE 0
|
||||
then
|
||||
cyphal start
|
||||
fi
|
||||
fi
|
||||
|
||||
#
|
||||
# Configure vehicle type specific parameters.
|
||||
# Note: rc.vehicle_setup is the entry point for all vehicle type specific setup.
|
||||
@@ -507,6 +520,11 @@ else
|
||||
#
|
||||
. ${R}etc/init.d/rc.serial
|
||||
|
||||
if param greater -s ZENOH_ENABLE 0
|
||||
then
|
||||
zenoh start
|
||||
fi
|
||||
|
||||
# Must be started after the serial config is read
|
||||
rc_input start $RC_INPUT_ARGS
|
||||
|
||||
@@ -612,6 +630,16 @@ else
|
||||
fi
|
||||
unset RC_LOGGING
|
||||
|
||||
#
|
||||
# Start the VTX services.
|
||||
#
|
||||
set RC_VTXTABLE ${R}etc/init.d/rc.vtxtable
|
||||
if [ -f ${RC_VTXTABLE} ]
|
||||
then
|
||||
. ${RC_VTXTABLE}
|
||||
fi
|
||||
unset RC_VTXTABLE
|
||||
|
||||
#
|
||||
# Set additional parameters and env variables for selected AUTOSTART.
|
||||
#
|
||||
@@ -628,27 +656,6 @@ else
|
||||
fi
|
||||
unset BOARD_BOOTLOADER_UPGRADE
|
||||
|
||||
#
|
||||
# Check if UAVCAN is enabled, default to it for ESCs.
|
||||
#
|
||||
if param greater -s UAVCAN_ENABLE 0
|
||||
then
|
||||
# Start core UAVCAN module.
|
||||
if ! uavcan start
|
||||
then
|
||||
tune_control play error
|
||||
fi
|
||||
else
|
||||
if param greater -s CYPHAL_ENABLE 0
|
||||
then
|
||||
cyphal start
|
||||
fi
|
||||
fi
|
||||
if param greater -s ZENOH_ENABLE 0
|
||||
then
|
||||
zenoh start
|
||||
fi
|
||||
|
||||
#
|
||||
# End of autostart.
|
||||
#
|
||||
|
||||
@@ -39,6 +39,8 @@ exec find boards msg src platforms test \
|
||||
-path src/lib/cdrstream/rosidl -prune -o \
|
||||
-path src/modules/zenoh/zenoh-pico -prune -o \
|
||||
-path boards/modalai/voxl2/libfc-sensor-api -prune -o \
|
||||
-path boards/modalai/voxl2/src/lib/mpa/libmodal-json -prune -o \
|
||||
-path boards/modalai/voxl2/src/lib/mpa/libmodal-pipe -prune -o \
|
||||
-path src/drivers/actuators/vertiq_io/iq-module-communication-cpp -prune -o \
|
||||
-path src/lib/tensorflow_lite_micro/tflite_micro -prune -o \
|
||||
-path src/drivers/ins/sbgecom/sbgECom -prune -o \
|
||||
|
||||
Executable
+331
@@ -0,0 +1,331 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Validate commit messages in a PR against conventional commits format.
|
||||
|
||||
Reads a JSON array of GitHub commit objects from stdin (as returned by the
|
||||
GitHub API's /pulls/{n}/commits endpoint) and checks each message for
|
||||
blocking errors and advisory warnings.
|
||||
|
||||
With --markdown, outputs a formatted PR comment body instead of plain text.
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
|
||||
from conventional_commits import (
|
||||
EXEMPT_PREFIXES,
|
||||
parse_header,
|
||||
)
|
||||
|
||||
# Blocking: prefixes that indicate unsquashed fixup commits
|
||||
FIXUP_PREFIXES = ('fixup!', 'squash!', 'amend!')
|
||||
|
||||
# Blocking: single-word throwaway messages (case-insensitive exact match)
|
||||
THROWAWAY_WORDS = frozenset({
|
||||
'fix', 'fixed', 'fixes',
|
||||
'update', 'updated', 'updates',
|
||||
'test', 'tests', 'testing',
|
||||
'tmp', 'temp',
|
||||
'oops', 'wip',
|
||||
'debug', 'cleanup',
|
||||
})
|
||||
|
||||
# Blocking: debug session leftovers
|
||||
DEBUG_KEYWORDS = ('tmate',)
|
||||
|
||||
# Warning: review-response messages (case-insensitive substring match)
|
||||
REVIEW_RESPONSE_PATTERNS = (
|
||||
'address review',
|
||||
'apply suggestions from code review',
|
||||
'code review',
|
||||
)
|
||||
|
||||
# Warning: formatter-only commits
|
||||
FORMATTER_PATTERNS = (
|
||||
'do make format',
|
||||
'make format',
|
||||
'run formatter',
|
||||
'apply format',
|
||||
)
|
||||
|
||||
MIN_MESSAGE_LENGTH = 5
|
||||
|
||||
|
||||
def check_commit(message: str) -> tuple[list[str], list[str]]:
|
||||
"""Return (errors, warnings) for a single commit message."""
|
||||
errors: list[str] = []
|
||||
warnings: list[str] = []
|
||||
|
||||
first_line = message.split('\n', 1)[0].strip()
|
||||
lower = first_line.lower()
|
||||
|
||||
# --- Blocking checks ---
|
||||
|
||||
for prefix in FIXUP_PREFIXES:
|
||||
if lower.startswith(prefix):
|
||||
errors.append(f'Unsquashed commit: starts with "{prefix}"')
|
||||
|
||||
if lower == 'wip' or lower.startswith('wip ') or lower.startswith('wip:'):
|
||||
errors.append('WIP commit should not be merged')
|
||||
|
||||
if len(first_line) < MIN_MESSAGE_LENGTH:
|
||||
errors.append(f'Message too short ({len(first_line)} chars, minimum {MIN_MESSAGE_LENGTH})')
|
||||
|
||||
if first_line.strip() and first_line.strip().lower() in THROWAWAY_WORDS:
|
||||
errors.append(f'Single-word throwaway message: "{first_line.strip()}"')
|
||||
|
||||
for kw in DEBUG_KEYWORDS:
|
||||
if kw in lower:
|
||||
errors.append(f'Debug session leftover: contains "{kw}"')
|
||||
|
||||
# --- Warning checks ---
|
||||
|
||||
for pattern in REVIEW_RESPONSE_PATTERNS:
|
||||
if pattern in lower:
|
||||
warnings.append('Review-response commit')
|
||||
break
|
||||
|
||||
for pattern in FORMATTER_PATTERNS:
|
||||
if pattern in lower:
|
||||
warnings.append('Formatter-only commit')
|
||||
break
|
||||
|
||||
if not parse_header(first_line):
|
||||
# Exempt merge commits
|
||||
for prefix in EXEMPT_PREFIXES:
|
||||
if first_line.startswith(prefix):
|
||||
break
|
||||
else:
|
||||
warnings.append(
|
||||
'Missing conventional commit format '
|
||||
'(e.g. "feat(ekf2): add something")'
|
||||
)
|
||||
|
||||
return errors, warnings
|
||||
|
||||
|
||||
def suggest_commit(message: str) -> str | None:
|
||||
"""Suggest how to fix a bad commit message."""
|
||||
first_line = message.split('\n', 1)[0].strip()
|
||||
lower = first_line.lower()
|
||||
|
||||
for prefix in FIXUP_PREFIXES:
|
||||
if lower.startswith(prefix):
|
||||
return 'Squash this into the commit it fixes'
|
||||
|
||||
if lower == 'wip' or lower.startswith('wip ') or lower.startswith('wip:'):
|
||||
return 'Reword with a descriptive message (e.g. "feat(scope): what changed")'
|
||||
|
||||
if len(first_line) < MIN_MESSAGE_LENGTH:
|
||||
return 'Reword with a descriptive message (e.g. "feat(ekf2): what changed")'
|
||||
|
||||
if first_line.strip().lower() in THROWAWAY_WORDS:
|
||||
return 'Reword with a descriptive message (e.g. "fix(scope): what changed")'
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def format_plain(data: list) -> tuple[bool, bool]:
|
||||
"""Print plain text output. Returns (has_blocking, has_warnings)."""
|
||||
has_blocking = False
|
||||
has_warnings = False
|
||||
|
||||
for commit in data:
|
||||
sha = commit.get('sha', '?')[:10]
|
||||
message = commit.get('commit', {}).get('message', '')
|
||||
first_line = message.split('\n', 1)[0].strip()
|
||||
|
||||
errors, warnings = check_commit(message)
|
||||
|
||||
if errors or warnings:
|
||||
print(f"\n {sha} {first_line}")
|
||||
|
||||
for err in errors:
|
||||
print(f" ERROR: {err}")
|
||||
has_blocking = True
|
||||
|
||||
for warn in warnings:
|
||||
print(f" WARNING: {warn}")
|
||||
has_warnings = True
|
||||
|
||||
if has_blocking:
|
||||
print(
|
||||
"\n"
|
||||
"ERROR = must fix before merging (CI will block the PR)\n"
|
||||
"WARNING = advisory, not blocking, but recommended to fix\n"
|
||||
"\n"
|
||||
"See the contributing guide for details:\n"
|
||||
" https://github.com/PX4/PX4-Autopilot/blob/main/CONTRIBUTING.md#commit-message-convention\n",
|
||||
)
|
||||
|
||||
elif has_warnings:
|
||||
print(
|
||||
"\n"
|
||||
"WARNING = advisory, not blocking, but recommended to fix\n"
|
||||
"\n"
|
||||
"See the contributing guide for details:\n"
|
||||
" https://github.com/PX4/PX4-Autopilot/blob/main/CONTRIBUTING.md#commit-message-convention\n",
|
||||
)
|
||||
|
||||
return has_blocking, has_warnings
|
||||
|
||||
|
||||
def format_markdown_blocking(data: list) -> str:
|
||||
"""Format a blocking error markdown comment."""
|
||||
error_groups: dict[str, list[str]] = {}
|
||||
unique_commits: list[tuple[str, str, list[str], str]] = []
|
||||
|
||||
for commit in data:
|
||||
sha = commit.get('sha', '?')[:10]
|
||||
message = commit.get('commit', {}).get('message', '')
|
||||
first_line = message.split('\n', 1)[0].strip()
|
||||
|
||||
errors, _ = check_commit(message)
|
||||
if not errors:
|
||||
continue
|
||||
|
||||
suggestion = suggest_commit(message) or ''
|
||||
unique_commits.append((sha, first_line, errors, suggestion))
|
||||
|
||||
for err in errors:
|
||||
error_groups.setdefault(err, []).append(sha)
|
||||
|
||||
lines = [
|
||||
"## \u274c Commit messages need attention before merging",
|
||||
"",
|
||||
]
|
||||
|
||||
has_large_group = any(len(shas) > 3 for shas in error_groups.values())
|
||||
|
||||
if has_large_group:
|
||||
lines.extend([
|
||||
"**Issues found:**",
|
||||
"",
|
||||
])
|
||||
for err_msg, shas in error_groups.items():
|
||||
if len(shas) > 3:
|
||||
lines.append(f"- **{len(shas)} commits**: {err_msg} "
|
||||
f"(`{shas[0]}`, `{shas[1]}`, ... `{shas[-1]}`)")
|
||||
else:
|
||||
sha_list = ', '.join(f'`{s}`' for s in shas)
|
||||
lines.append(f"- {err_msg}: {sha_list}")
|
||||
|
||||
distinct_messages = {msg for _, msg, _, _ in unique_commits}
|
||||
if len(distinct_messages) <= 5:
|
||||
lines.extend(["", "**Affected commits:**", ""])
|
||||
for sha, msg, errors, suggestion in unique_commits:
|
||||
safe_msg = msg.replace('|', '\\|')
|
||||
lines.append(f"- `{sha}` {safe_msg}")
|
||||
else:
|
||||
lines.extend([
|
||||
"| Commit | Message | Issue | Suggested fix |",
|
||||
"|--------|---------|-------|---------------|",
|
||||
])
|
||||
for sha, msg, errors, suggestion in unique_commits:
|
||||
issues = '; '.join(errors)
|
||||
safe_msg = msg.replace('|', '\\|')
|
||||
lines.append(f"| `{sha}` | {safe_msg} | {issues} | {suggestion} |")
|
||||
|
||||
lines.extend([
|
||||
"",
|
||||
"See [CONTRIBUTING.md](https://github.com/PX4/PX4-Autopilot/blob/main/CONTRIBUTING.md#commit-message-convention) "
|
||||
"for how to clean up commits.",
|
||||
"",
|
||||
"---",
|
||||
"*This comment will be automatically removed once the issues are resolved.*",
|
||||
])
|
||||
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
def format_markdown_advisory(data: list) -> str:
|
||||
"""Format an advisory warning markdown comment."""
|
||||
lines = [
|
||||
"## \U0001f4a1 Commit messages could be improved",
|
||||
"",
|
||||
"Not blocking, but these commit messages could use some cleanup.",
|
||||
"",
|
||||
"| Commit | Message | Suggestion |",
|
||||
"|--------|---------|------------|",
|
||||
]
|
||||
|
||||
for commit in data:
|
||||
sha = commit.get('sha', '?')[:10]
|
||||
message = commit.get('commit', {}).get('message', '')
|
||||
first_line = message.split('\n', 1)[0].strip()
|
||||
|
||||
_, warnings = check_commit(message)
|
||||
if not warnings:
|
||||
continue
|
||||
|
||||
suggestion = '; '.join(warnings)
|
||||
safe_msg = first_line.replace('|', '\\|')
|
||||
lines.append(f"| `{sha}` | {safe_msg} | {suggestion} |")
|
||||
|
||||
lines.extend([
|
||||
"",
|
||||
"See the [commit message convention](https://github.com/PX4/PX4-Autopilot/blob/main/CONTRIBUTING.md#commit-message-convention) "
|
||||
"for details.",
|
||||
"",
|
||||
"---",
|
||||
"*This comment will be automatically removed once the issues are resolved.*",
|
||||
])
|
||||
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
markdown_stdout = '--markdown' in sys.argv
|
||||
markdown_file = None
|
||||
for i, a in enumerate(sys.argv):
|
||||
if a == '--markdown-file' and i + 1 < len(sys.argv):
|
||||
markdown_file = sys.argv[i + 1]
|
||||
elif a.startswith('--markdown-file='):
|
||||
markdown_file = a.split('=', 1)[1]
|
||||
|
||||
try:
|
||||
data = json.load(sys.stdin)
|
||||
except json.JSONDecodeError as exc:
|
||||
print(f"Failed to parse JSON input: {exc}", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
|
||||
if not isinstance(data, list):
|
||||
print("Expected a JSON array of commit objects.", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
|
||||
# Always compute blocking/warning state
|
||||
has_blocking = False
|
||||
has_warnings = False
|
||||
for commit in data:
|
||||
message = commit.get('commit', {}).get('message', '')
|
||||
errors, warnings = check_commit(message)
|
||||
if errors:
|
||||
has_blocking = True
|
||||
if warnings:
|
||||
has_warnings = True
|
||||
|
||||
# Generate markdown if needed
|
||||
md = None
|
||||
if has_blocking:
|
||||
md = format_markdown_blocking(data)
|
||||
elif has_warnings:
|
||||
md = format_markdown_advisory(data)
|
||||
|
||||
if md:
|
||||
if markdown_stdout:
|
||||
print(md)
|
||||
if markdown_file:
|
||||
with open(markdown_file, 'w') as f:
|
||||
f.write(md + '\n')
|
||||
elif markdown_file:
|
||||
with open(markdown_file, 'w') as f:
|
||||
pass
|
||||
|
||||
# Plain text output to stderr for CI logs (always, unless --markdown only)
|
||||
if not markdown_stdout:
|
||||
has_blocking, _ = format_plain(data)
|
||||
|
||||
sys.exit(1 if has_blocking else 0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Executable
+163
@@ -0,0 +1,163 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Validate that a PR title follows conventional commits format.
|
||||
|
||||
Format: type(scope): description
|
||||
|
||||
Can output plain text for CI logs or markdown for PR comments.
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
from conventional_commits import (
|
||||
CONVENTIONAL_TYPES,
|
||||
EXEMPT_PREFIXES,
|
||||
parse_header,
|
||||
suggest_scope,
|
||||
suggest_type,
|
||||
)
|
||||
|
||||
|
||||
def suggest_title(title: str) -> str | None:
|
||||
"""Try to suggest a corrected title in conventional commits format."""
|
||||
stripped = title.strip()
|
||||
|
||||
# Remove common bracket prefixes like [docs], [CI], etc.
|
||||
bracket_match = re.match(r'^\[([^\]]+)\]\s*(.+)', stripped)
|
||||
if bracket_match:
|
||||
prefix = bracket_match.group(1).strip().lower()
|
||||
rest = bracket_match.group(2).strip()
|
||||
rest = re.sub(r'^[\-:]\s*', '', rest).strip()
|
||||
if len(rest) >= 5:
|
||||
# Try to map bracket content to a type
|
||||
commit_type = prefix if prefix in CONVENTIONAL_TYPES else suggest_type(rest)
|
||||
scope = suggest_scope(rest)
|
||||
if scope:
|
||||
return f"{commit_type}({scope}): {rest}"
|
||||
|
||||
# Already has old-style "subsystem: description" format - convert it
|
||||
colon_match = re.match(r'^([a-zA-Z][a-zA-Z0-9_/\-\. ]*): (.+)$', stripped)
|
||||
if colon_match:
|
||||
old_subsystem = colon_match.group(1).strip()
|
||||
desc = colon_match.group(2).strip()
|
||||
if len(desc) >= 5:
|
||||
commit_type = suggest_type(desc)
|
||||
# Use the old subsystem as scope (clean it up)
|
||||
scope = old_subsystem.lower().replace(' ', '_')
|
||||
return f"{commit_type}({scope}): {desc}"
|
||||
|
||||
# No format at all - try to guess both type and scope
|
||||
commit_type = suggest_type(stripped)
|
||||
scope = suggest_scope(stripped)
|
||||
if scope:
|
||||
desc = stripped[0].lower() + stripped[1:] if stripped else stripped
|
||||
return f"{commit_type}({scope}): {desc}"
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def check_title(title: str) -> bool:
|
||||
title = title.strip()
|
||||
|
||||
if not title:
|
||||
print("PR title is empty.", file=sys.stderr)
|
||||
return False
|
||||
|
||||
for prefix in EXEMPT_PREFIXES:
|
||||
if title.startswith(prefix):
|
||||
return True
|
||||
|
||||
if parse_header(title):
|
||||
return True
|
||||
|
||||
types_str = ', '.join(f'`{t}`' for t in CONVENTIONAL_TYPES.keys())
|
||||
print(
|
||||
f"PR title does not match conventional commits format.\n"
|
||||
f"\n"
|
||||
f" Title: {title}\n"
|
||||
f"\n"
|
||||
f"Expected format: type(scope): description\n"
|
||||
f"\n"
|
||||
f"Valid types: {types_str}\n"
|
||||
f"\n"
|
||||
f"Good examples:\n"
|
||||
f" feat(ekf2): add height fusion timeout\n"
|
||||
f" fix(mavlink): correct BATTERY_STATUS_V2 parsing\n"
|
||||
f" ci(workflows): migrate to reusable workflows\n"
|
||||
f" feat(boards/px4_fmu-v6x)!: remove deprecated driver API\n"
|
||||
f"\n"
|
||||
f"Bad examples:\n"
|
||||
f" fix stuff\n"
|
||||
f" Update file\n"
|
||||
f" ekf2: fix something (missing type prefix)\n"
|
||||
f"\n"
|
||||
f"See the contributing guide for details:\n"
|
||||
f" https://github.com/PX4/PX4-Autopilot/blob/main/CONTRIBUTING.md#commit-message-convention\n",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
def format_markdown(title: str) -> str:
|
||||
"""Format a markdown PR comment body for a bad title."""
|
||||
lines = [
|
||||
"## \u274c PR title needs conventional commit format",
|
||||
"",
|
||||
"Expected format: `type(scope): description` "
|
||||
"([conventional commits](https://www.conventionalcommits.org/)).",
|
||||
"",
|
||||
"**Your title:**",
|
||||
f"> {title}",
|
||||
"",
|
||||
]
|
||||
|
||||
suggestion = suggest_title(title)
|
||||
if suggestion:
|
||||
lines.extend([
|
||||
"**Suggested fix:**",
|
||||
f"> {suggestion}",
|
||||
"",
|
||||
])
|
||||
|
||||
lines.extend([
|
||||
"**To fix this:** click the ✏️ next to the PR title at the top "
|
||||
"of this page and update it.",
|
||||
"",
|
||||
"See [CONTRIBUTING.md](https://github.com/PX4/PX4-Autopilot/blob/main/CONTRIBUTING.md#commit-message-convention) "
|
||||
"for details.",
|
||||
"",
|
||||
"---",
|
||||
"*This comment will be automatically removed once the issue is resolved.*",
|
||||
])
|
||||
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(description='Check PR title format')
|
||||
parser.add_argument('title', help='The PR title to validate')
|
||||
parser.add_argument('--markdown', action='store_true',
|
||||
help='Output markdown to stdout on failure')
|
||||
parser.add_argument('--markdown-file', metavar='FILE',
|
||||
help='Write markdown to FILE on failure')
|
||||
args = parser.parse_args()
|
||||
|
||||
passed = check_title(args.title)
|
||||
|
||||
if not passed:
|
||||
md = format_markdown(args.title)
|
||||
if args.markdown:
|
||||
print(md)
|
||||
if args.markdown_file:
|
||||
with open(args.markdown_file, 'w') as f:
|
||||
f.write(md + '\n')
|
||||
elif args.markdown_file:
|
||||
with open(args.markdown_file, 'w') as f:
|
||||
pass
|
||||
|
||||
sys.exit(0 if passed else 1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,146 @@
|
||||
"""Shared constants and helpers for conventional commit validation.
|
||||
|
||||
Format: type(scope): description
|
||||
Optional breaking change marker: type(scope)!: description
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
CONVENTIONAL_TYPES = {
|
||||
'feat': 'A new feature',
|
||||
'fix': 'A bug fix',
|
||||
'docs': 'Documentation only changes',
|
||||
'style': 'Formatting, whitespace, no code change',
|
||||
'refactor': 'Code change that neither fixes a bug nor adds a feature',
|
||||
'perf': 'Performance improvement',
|
||||
'test': 'Adding or correcting tests',
|
||||
'build': 'Build system or external dependencies',
|
||||
'ci': 'CI configuration files and scripts',
|
||||
'chore': 'Other changes that don\'t modify src or test files',
|
||||
'revert': 'Reverts a previous commit',
|
||||
}
|
||||
|
||||
# type(scope)[!]: description
|
||||
# - type: one of CONVENTIONAL_TYPES keys
|
||||
# - scope: required, alphanumeric with _/-/.
|
||||
# - !: optional breaking change marker
|
||||
# - description: at least 5 chars
|
||||
HEADER_PATTERN = re.compile(
|
||||
r'^(' + '|'.join(CONVENTIONAL_TYPES.keys()) + r')'
|
||||
r'\(([a-zA-Z0-9_/\-\.]+)\)'
|
||||
r'(!)?'
|
||||
r': (.{5,})$'
|
||||
)
|
||||
|
||||
EXEMPT_PREFIXES = ('Merge ',)
|
||||
|
||||
# Common PX4 subsystem scopes for suggestions
|
||||
KNOWN_SCOPES = [
|
||||
'ekf2', 'mavlink', 'commander', 'navigator', 'sensors',
|
||||
'mc_att_control', 'mc_pos_control', 'mc_rate_control',
|
||||
'fw_att_control', 'fw_pos_control', 'fw_rate_control',
|
||||
'vtol', 'actuators', 'battery', 'param', 'logger',
|
||||
'uorb', 'drivers', 'boards', 'simulation', 'sitl',
|
||||
'gps', 'rc', 'safety', 'can', 'serial',
|
||||
'ci', 'docs', 'build', 'cmake', 'tools',
|
||||
'mixer', 'land_detector', 'airspeed', 'gyroscope',
|
||||
'accelerometer', 'magnetometer', 'barometer',
|
||||
]
|
||||
|
||||
# Keyword patterns to suggest scopes from description text
|
||||
KEYWORD_SCOPES = [
|
||||
(r'\b(ekf|estimator|height|fusion|imu|baro)\b', 'ekf2'),
|
||||
(r'\b(mavlink|MAVLink|MAVLINK|command_int|heartbeat)\b', 'mavlink'),
|
||||
(r'\b(uorb|orb|pub|sub|topic)\b', 'uorb'),
|
||||
(r'\b(board|fmu|nuttx|stm32)\b', 'boards'),
|
||||
(r'\b(mixer|actuator|motor|servo|pwm|dshot)\b', 'actuators'),
|
||||
(r'\b(battery|power)\b', 'battery'),
|
||||
(r'\b(param|parameter)\b', 'param'),
|
||||
(r'\b(log|logger|sdlog)\b', 'logger'),
|
||||
(r'\b(sensor|accel|gyro)\b', 'sensors'),
|
||||
(r'\b(land|takeoff|rtl|mission|navigator|geofence)\b', 'navigator'),
|
||||
(r'\b(position|velocity|attitude|rate)\s*(control|ctrl)\b', 'mc_att_control'),
|
||||
(r'\b(mc|multicopter|quad)\b', 'mc_att_control'),
|
||||
(r'\b(fw|fixedwing|fixed.wing|plane)\b', 'fw_att_control'),
|
||||
(r'\b(vtol|transition)\b', 'vtol'),
|
||||
(r'\b(ci|workflow|github.action|pipeline)\b', 'ci'),
|
||||
(r'\b(doc|docs|documentation|readme)\b', 'docs'),
|
||||
(r'\b(cmake|make|toolchain|compiler)\b', 'build'),
|
||||
(r'\b(sitl|simulation|gazebo|jmavsim|sih)\b', 'simulation'),
|
||||
(r'\b(can|uavcan|cyphal|dronecan)\b', 'can'),
|
||||
(r'\b(serial|uart|spi|i2c)\b', 'serial'),
|
||||
(r'\b(safety|failsafe|arm|disarm|kill)\b', 'safety'),
|
||||
(r'\b(rc|radio|sbus|crsf|elrs|dsm)\b', 'rc'),
|
||||
(r'\b(gps|gnss|rtk|ubx)\b', 'gps'),
|
||||
(r'\b(optical.flow|flow|rangefinder|lidar|distance)\b', 'sensors'),
|
||||
(r'\b(orbit|follow|offboard)\b', 'commander'),
|
||||
(r'\b(driver)\b', 'drivers'),
|
||||
]
|
||||
|
||||
# Verb patterns to suggest conventional commit type
|
||||
VERB_TYPE_MAP = [
|
||||
(r'^fix(e[ds])?[\s:]', 'fix'),
|
||||
(r'^bug[\s:]', 'fix'),
|
||||
(r'^add(s|ed|ing)?[\s:]', 'feat'),
|
||||
(r'^implement', 'feat'),
|
||||
(r'^introduce', 'feat'),
|
||||
(r'^support', 'feat'),
|
||||
(r'^enable', 'feat'),
|
||||
(r'^update[ds]?[\s:]', 'feat'),
|
||||
(r'^improv(e[ds]?|ing)', 'perf'),
|
||||
(r'^optimi[zs](e[ds]?|ing)', 'perf'),
|
||||
(r'^refactor', 'refactor'),
|
||||
(r'^clean\s*up', 'refactor'),
|
||||
(r'^restructure', 'refactor'),
|
||||
(r'^simplif(y|ied)', 'refactor'),
|
||||
(r'^remov(e[ds]?|ing)', 'refactor'),
|
||||
(r'^delet(e[ds]?|ing)', 'refactor'),
|
||||
(r'^deprecat', 'refactor'),
|
||||
(r'^replac(e[ds]?|ing)', 'refactor'),
|
||||
(r'^renam(e[ds]?|ing)', 'refactor'),
|
||||
(r'^migrat', 'refactor'),
|
||||
(r'^revert', 'revert'),
|
||||
(r'^doc(s|ument)', 'docs'),
|
||||
(r'^test', 'test'),
|
||||
(r'^format', 'style'),
|
||||
(r'^lint', 'style'),
|
||||
(r'^whitespace', 'style'),
|
||||
(r'^build', 'build'),
|
||||
(r'^ci[\s:]', 'ci'),
|
||||
]
|
||||
|
||||
|
||||
def parse_header(text: str) -> dict | None:
|
||||
"""Parse a conventional commit header into components.
|
||||
|
||||
Returns dict with keys {type, scope, breaking, subject} or None if
|
||||
the text doesn't match conventional commits format.
|
||||
"""
|
||||
text = text.strip()
|
||||
m = HEADER_PATTERN.match(text)
|
||||
if not m:
|
||||
return None
|
||||
return {
|
||||
'type': m.group(1),
|
||||
'scope': m.group(2),
|
||||
'breaking': m.group(3) == '!',
|
||||
'subject': m.group(4),
|
||||
}
|
||||
|
||||
|
||||
def suggest_type(text: str) -> str:
|
||||
"""Infer a conventional commit type from description text."""
|
||||
lower = text.strip().lower()
|
||||
for pattern, commit_type in VERB_TYPE_MAP:
|
||||
if re.search(pattern, lower):
|
||||
return commit_type
|
||||
return 'feat'
|
||||
|
||||
|
||||
def suggest_scope(text: str) -> str | None:
|
||||
"""Infer a scope from keywords in the text."""
|
||||
lower = text.strip().lower()
|
||||
for pattern, scope in KEYWORD_SCOPES:
|
||||
if re.search(pattern, lower, re.IGNORECASE):
|
||||
return scope
|
||||
return None
|
||||
@@ -36,11 +36,20 @@ if args.filter:
|
||||
target_filter.append(target)
|
||||
|
||||
default_container = 'ghcr.io/px4/px4-dev:v1.16.0-rc1-258-g0369abd556'
|
||||
voxl2_container = 'ghcr.io/px4/px4-dev-voxl2:v1.5'
|
||||
build_configs = []
|
||||
grouped_targets = {}
|
||||
excluded_boards = ['modalai_voxl2', 'px4_ros2', 'espressif_esp32'] # TODO: fix and enable
|
||||
excluded_boards = ['px4_ros2', 'espressif_esp32'] # TODO: fix and enable
|
||||
excluded_manufacturers = ['atlflight']
|
||||
excluded_platforms = ['qurt']
|
||||
excluded_platforms = []
|
||||
|
||||
# Container overrides for platforms/boards that need a non-default container
|
||||
platform_container_overrides = {
|
||||
'qurt': voxl2_container,
|
||||
}
|
||||
board_container_overrides = {
|
||||
'modalai_voxl2': voxl2_container,
|
||||
}
|
||||
excluded_labels = [
|
||||
'stackcheck',
|
||||
'nolockstep', 'replay', 'test',
|
||||
@@ -88,7 +97,20 @@ def process_target(px4board_file, target_name):
|
||||
|
||||
if platform not in excluded_platforms:
|
||||
container = default_container
|
||||
if platform == 'posix':
|
||||
|
||||
# Extract board name (manufacturer_board) from target name
|
||||
board_name = '_'.join(target_name.split('_')[:2])
|
||||
|
||||
# Apply container overrides for specific platforms or boards
|
||||
if platform in platform_container_overrides:
|
||||
container = platform_container_overrides[platform]
|
||||
if board_name in board_container_overrides:
|
||||
container = board_container_overrides[board_name]
|
||||
|
||||
# Boards with container overrides get their own group
|
||||
if board_name in board_container_overrides or platform in platform_container_overrides:
|
||||
group = 'voxl2'
|
||||
elif platform == 'posix':
|
||||
group = 'base'
|
||||
if toolchain:
|
||||
if toolchain.startswith('aarch64'):
|
||||
@@ -126,18 +148,18 @@ grouped_targets['base']['manufacturers'] = {}
|
||||
grouped_targets['base']['manufacturers']['px4'] = []
|
||||
grouped_targets['base']['manufacturers']['px4'] += metadata_targets
|
||||
|
||||
for manufacturer in os.scandir(os.path.join(source_dir, '../boards')):
|
||||
for manufacturer in sorted(os.scandir(os.path.join(source_dir, '../boards')), key=lambda e: e.name):
|
||||
if not manufacturer.is_dir():
|
||||
continue
|
||||
if manufacturer.name in excluded_manufacturers:
|
||||
if verbose: print(f'excluding manufacturer {manufacturer.name}')
|
||||
continue
|
||||
|
||||
for board in os.scandir(manufacturer.path):
|
||||
for board in sorted(os.scandir(manufacturer.path), key=lambda e: e.name):
|
||||
if not board.is_dir():
|
||||
continue
|
||||
|
||||
for files in os.scandir(board.path):
|
||||
for files in sorted(os.scandir(board.path), key=lambda e: e.name):
|
||||
if files.is_file() and files.name.endswith('.px4board'):
|
||||
|
||||
board_name = manufacturer.name + '_' + board.name
|
||||
@@ -203,7 +225,7 @@ if (args.group):
|
||||
if(verbose):
|
||||
print(f'=:Architectures: [{grouped_targets.keys()}]')
|
||||
for arch in grouped_targets:
|
||||
runner = 'x64' if arch == 'nuttx' else 'arm64'
|
||||
runner = 'x64' if arch in ('nuttx', 'voxl2') else 'arm64'
|
||||
if(verbose):
|
||||
print(f'=:Processing: [{arch}]')
|
||||
temp_group = []
|
||||
|
||||
Executable
+431
@@ -0,0 +1,431 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# metadata_sync.sh - Unified metadata generation and synchronization for PX4 docs
|
||||
#
|
||||
# Usage:
|
||||
# Tools/ci/metadata_sync.sh [OPTIONS] [TYPES...]
|
||||
#
|
||||
# Types:
|
||||
# parameters - Parameter reference (docs/en/advanced_config/parameter_reference.md)
|
||||
# airframes - Airframe reference (docs/en/airframes/airframe_reference.md)
|
||||
# modules - Module documentation (docs/en/modules/*.md)
|
||||
# msg_docs - uORB message docs (docs/en/msg_docs/*.md + docs/en/middleware/dds_topics.md)
|
||||
# uorb_graphs - uORB graph JSONs (docs/public/middleware/*.json)
|
||||
# failsafe_web - Failsafe simulator (docs/public/config/failsafe/*.{js,wasm,json})
|
||||
# all - All of the above (default)
|
||||
#
|
||||
# Options:
|
||||
# --generate Build the make targets to generate fresh metadata
|
||||
# --sync Copy generated files to docs/
|
||||
# --verbose Show detailed output
|
||||
# --help Show this help
|
||||
#
|
||||
# Exit codes:
|
||||
# 0 - Success (files synced or already up-to-date)
|
||||
# 1 - Error (build failed, missing files, etc.)
|
||||
#
|
||||
# Examples:
|
||||
# # Full regeneration and sync (orchestrator use case)
|
||||
# Tools/ci/metadata_sync.sh --generate --sync all
|
||||
#
|
||||
# # Just sync specific type (assumes already built)
|
||||
# Tools/ci/metadata_sync.sh --sync parameters
|
||||
#
|
||||
# # Generate only, don't copy
|
||||
# Tools/ci/metadata_sync.sh --generate uorb_graphs
|
||||
#
|
||||
set -euo pipefail
|
||||
shopt -s nullglob
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# Configuration
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
EMSCRIPTEN_VERSION="3.1.64"
|
||||
EMSDK_DIR="${EMSDK_DIR:-_emscripten_sdk}"
|
||||
|
||||
# All available metadata types
|
||||
ALL_TYPES=(parameters airframes modules msg_docs uorb_graphs failsafe_web)
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# Logging
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
VERBOSE=false
|
||||
|
||||
log() {
|
||||
echo "[metadata_sync] $*"
|
||||
}
|
||||
|
||||
log_verbose() {
|
||||
if [[ "$VERBOSE" == "true" ]]; then
|
||||
echo "[metadata_sync] $*"
|
||||
fi
|
||||
}
|
||||
|
||||
die() {
|
||||
echo "[metadata_sync] ERROR: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# Help
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
show_help() {
|
||||
head -n 35 "$0" | tail -n +2 | sed 's/^# \?//'
|
||||
exit 0
|
||||
}
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# Emscripten Setup
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
ensure_emscripten() {
|
||||
if command -v emcc >/dev/null 2>&1; then
|
||||
log_verbose "Emscripten already available: $(emcc --version | head -1)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "Setting up Emscripten ${EMSCRIPTEN_VERSION}..."
|
||||
|
||||
if [[ ! -d "$EMSDK_DIR" ]]; then
|
||||
log_verbose "Cloning emsdk to $EMSDK_DIR"
|
||||
if [[ "$VERBOSE" == "true" ]]; then
|
||||
git clone https://github.com/emscripten-core/emsdk.git "$EMSDK_DIR"
|
||||
else
|
||||
git clone https://github.com/emscripten-core/emsdk.git "$EMSDK_DIR" >/dev/null 2>&1
|
||||
fi
|
||||
fi
|
||||
|
||||
pushd "$EMSDK_DIR" >/dev/null
|
||||
if [[ "$VERBOSE" == "true" ]]; then
|
||||
./emsdk install "$EMSCRIPTEN_VERSION"
|
||||
./emsdk activate "$EMSCRIPTEN_VERSION"
|
||||
else
|
||||
./emsdk install "$EMSCRIPTEN_VERSION" >/dev/null 2>&1
|
||||
./emsdk activate "$EMSCRIPTEN_VERSION" >/dev/null 2>&1
|
||||
fi
|
||||
popd >/dev/null
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
source "${EMSDK_DIR}/emsdk_env.sh" >/dev/null 2>&1
|
||||
|
||||
log_verbose "Emscripten ready: $(emcc --version | head -1)"
|
||||
}
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# Generation Functions
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
generate_parameters() {
|
||||
log "Generating parameters metadata..."
|
||||
if [[ "$VERBOSE" == "true" ]]; then
|
||||
make parameters_metadata
|
||||
else
|
||||
make parameters_metadata >/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
generate_airframes() {
|
||||
log "Generating airframes metadata..."
|
||||
if [[ "$VERBOSE" == "true" ]]; then
|
||||
make airframe_metadata
|
||||
else
|
||||
make airframe_metadata >/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
generate_modules() {
|
||||
log "Generating modules documentation..."
|
||||
if [[ "$VERBOSE" == "true" ]]; then
|
||||
make module_documentation
|
||||
else
|
||||
make module_documentation >/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
generate_msg_docs() {
|
||||
log "Generating message documentation..."
|
||||
if [[ "$VERBOSE" == "true" ]]; then
|
||||
make msg_docs
|
||||
else
|
||||
make msg_docs >/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
generate_uorb_graphs() {
|
||||
log "Generating uORB graphs..."
|
||||
if [[ "$VERBOSE" == "true" ]]; then
|
||||
make uorb_graphs
|
||||
else
|
||||
make uorb_graphs >/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
generate_failsafe_web() {
|
||||
ensure_emscripten
|
||||
log "Generating failsafe web..."
|
||||
if [[ "$VERBOSE" == "true" ]]; then
|
||||
make failsafe_web
|
||||
else
|
||||
make failsafe_web >/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# Sync Functions
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
sync_parameters() {
|
||||
local src="build/px4_sitl_default/docs/parameters.md"
|
||||
local dest="docs/en/advanced_config/parameter_reference.md"
|
||||
|
||||
log "Syncing parameters..."
|
||||
|
||||
if [[ ! -f "$src" ]]; then
|
||||
die "Source file not found: $src (did you run --generate?)"
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$dest")"
|
||||
cp "$src" "$dest"
|
||||
log_verbose " $src -> $dest"
|
||||
}
|
||||
|
||||
sync_airframes() {
|
||||
local src="build/px4_sitl_default/docs/airframes.md"
|
||||
local dest="docs/en/airframes/airframe_reference.md"
|
||||
|
||||
log "Syncing airframes..."
|
||||
|
||||
if [[ ! -f "$src" ]]; then
|
||||
die "Source file not found: $src (did you run --generate?)"
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$dest")"
|
||||
cp "$src" "$dest"
|
||||
log_verbose " $src -> $dest"
|
||||
}
|
||||
|
||||
sync_modules() {
|
||||
local src_dir="build/px4_sitl_default/docs/modules"
|
||||
local dest_dir="docs/en/modules"
|
||||
|
||||
log "Syncing modules..."
|
||||
|
||||
if [[ ! -d "$src_dir" ]]; then
|
||||
die "Source directory not found: $src_dir (did you run --generate?)"
|
||||
fi
|
||||
|
||||
local src_files=("$src_dir"/*.md)
|
||||
if [[ ${#src_files[@]} -eq 0 ]]; then
|
||||
die "No .md files found in $src_dir"
|
||||
fi
|
||||
|
||||
mkdir -p "$dest_dir"
|
||||
|
||||
for src in "${src_files[@]}"; do
|
||||
local name
|
||||
name=$(basename "$src")
|
||||
cp "$src" "$dest_dir/$name"
|
||||
log_verbose " $src -> $dest_dir/$name"
|
||||
done
|
||||
}
|
||||
|
||||
sync_msg_docs() {
|
||||
local src_dir="build/msg_docs"
|
||||
local dest_dir="docs/en/msg_docs"
|
||||
local middleware_dir="docs/en/middleware"
|
||||
|
||||
log "Syncing message docs..."
|
||||
|
||||
if [[ ! -d "$src_dir" ]]; then
|
||||
die "Source directory not found: $src_dir (did you run --generate?)"
|
||||
fi
|
||||
|
||||
local src_files=("$src_dir"/*.md)
|
||||
if [[ ${#src_files[@]} -eq 0 ]]; then
|
||||
die "No .md files found in $src_dir"
|
||||
fi
|
||||
|
||||
mkdir -p "$dest_dir"
|
||||
mkdir -p "$middleware_dir"
|
||||
|
||||
for src in "${src_files[@]}"; do
|
||||
local name
|
||||
name=$(basename "$src")
|
||||
|
||||
# dds_topics.md goes to middleware dir
|
||||
if [[ "$name" == "dds_topics.md" ]]; then
|
||||
cp "$src" "$middleware_dir/$name"
|
||||
log_verbose " $src -> $middleware_dir/$name"
|
||||
else
|
||||
cp "$src" "$dest_dir/$name"
|
||||
log_verbose " $src -> $dest_dir/$name"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
sync_uorb_graphs() {
|
||||
local src_dir="Tools/uorb_graph"
|
||||
local dest_dir="docs/public/middleware"
|
||||
|
||||
log "Syncing uORB graphs..."
|
||||
|
||||
local src_files=("$src_dir"/*.json)
|
||||
if [[ ${#src_files[@]} -eq 0 ]]; then
|
||||
die "No .json files found in $src_dir (did you run --generate?)"
|
||||
fi
|
||||
|
||||
mkdir -p "$dest_dir"
|
||||
|
||||
for src in "${src_files[@]}"; do
|
||||
local name
|
||||
name=$(basename "$src")
|
||||
cp "$src" "$dest_dir/$name"
|
||||
log_verbose " $src -> $dest_dir/$name"
|
||||
done
|
||||
}
|
||||
|
||||
sync_failsafe_web() {
|
||||
local src_dir="build/px4_sitl_default_failsafe_web"
|
||||
local dest_dir="docs/public/config/failsafe"
|
||||
|
||||
log "Syncing failsafe web..."
|
||||
|
||||
if [[ ! -d "$src_dir" ]]; then
|
||||
die "Source directory not found: $src_dir (did you run --generate?)"
|
||||
fi
|
||||
|
||||
# Gather js, wasm, json files
|
||||
local src_files=()
|
||||
for ext in js wasm json; do
|
||||
src_files+=("$src_dir"/*."$ext")
|
||||
done
|
||||
|
||||
if [[ ${#src_files[@]} -eq 0 ]]; then
|
||||
die "No .js/.wasm/.json files found in $src_dir"
|
||||
fi
|
||||
|
||||
mkdir -p "$dest_dir"
|
||||
|
||||
for src in "${src_files[@]}"; do
|
||||
local name
|
||||
name=$(basename "$src")
|
||||
cp "$src" "$dest_dir/$name"
|
||||
log_verbose " $src -> $dest_dir/$name"
|
||||
done
|
||||
}
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# Main Logic
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
DO_GENERATE=false
|
||||
DO_SYNC=false
|
||||
SELECTED_TYPES=()
|
||||
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--generate)
|
||||
DO_GENERATE=true
|
||||
shift
|
||||
;;
|
||||
--sync)
|
||||
DO_SYNC=true
|
||||
shift
|
||||
;;
|
||||
--verbose)
|
||||
VERBOSE=true
|
||||
shift
|
||||
;;
|
||||
--help|-h)
|
||||
show_help
|
||||
;;
|
||||
-*)
|
||||
die "Unknown option: $1"
|
||||
;;
|
||||
*)
|
||||
# It's a type
|
||||
SELECTED_TYPES+=("$1")
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Default to all types if none specified
|
||||
if [[ ${#SELECTED_TYPES[@]} -eq 0 ]]; then
|
||||
SELECTED_TYPES=("all")
|
||||
fi
|
||||
|
||||
# Expand "all" to all types
|
||||
local expanded_types=()
|
||||
for t in "${SELECTED_TYPES[@]}"; do
|
||||
if [[ "$t" == "all" ]]; then
|
||||
expanded_types+=("${ALL_TYPES[@]}")
|
||||
else
|
||||
expanded_types+=("$t")
|
||||
fi
|
||||
done
|
||||
SELECTED_TYPES=("${expanded_types[@]}")
|
||||
|
||||
# Validate types
|
||||
for t in "${SELECTED_TYPES[@]}"; do
|
||||
local valid=false
|
||||
for valid_type in "${ALL_TYPES[@]}"; do
|
||||
if [[ "$t" == "$valid_type" ]]; then
|
||||
valid=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ "$valid" == "false" ]]; then
|
||||
die "Unknown type: $t (valid: ${ALL_TYPES[*]})"
|
||||
fi
|
||||
done
|
||||
|
||||
# Must specify at least one action
|
||||
if [[ "$DO_GENERATE" == "false" && "$DO_SYNC" == "false" ]]; then
|
||||
die "Must specify at least one of: --generate, --sync"
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
parse_args "$@"
|
||||
|
||||
log "Selected types: ${SELECTED_TYPES[*]}"
|
||||
[[ "$DO_GENERATE" == "true" ]] && log "Actions: generate"
|
||||
[[ "$DO_SYNC" == "true" ]] && log "Actions: sync"
|
||||
|
||||
# Remove duplicates from SELECTED_TYPES
|
||||
local -A seen
|
||||
local unique_types=()
|
||||
for t in "${SELECTED_TYPES[@]}"; do
|
||||
if [[ -z "${seen[$t]:-}" ]]; then
|
||||
seen[$t]=1
|
||||
unique_types+=("$t")
|
||||
fi
|
||||
done
|
||||
SELECTED_TYPES=("${unique_types[@]}")
|
||||
|
||||
# Generate phase
|
||||
if [[ "$DO_GENERATE" == "true" ]]; then
|
||||
log "=== Generation Phase ==="
|
||||
for t in "${SELECTED_TYPES[@]}"; do
|
||||
"generate_$t"
|
||||
done
|
||||
fi
|
||||
|
||||
# Sync phase
|
||||
if [[ "$DO_SYNC" == "true" ]]; then
|
||||
log "=== Sync Phase ==="
|
||||
for t in "${SELECTED_TYPES[@]}"; do
|
||||
"sync_$t"
|
||||
done
|
||||
fi
|
||||
|
||||
log "Done."
|
||||
exit 0
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
mkdir artifacts
|
||||
cp **/**/*.px4 artifacts/
|
||||
cp **/**/*.elf artifacts/
|
||||
cp **/**/*.px4 artifacts/ 2>/dev/null || true
|
||||
cp **/**/*.elf artifacts/ 2>/dev/null || true
|
||||
for build_dir_path in build/*/ ; do
|
||||
build_dir_path=${build_dir_path::${#build_dir_path}-1}
|
||||
build_dir=${build_dir_path#*/}
|
||||
|
||||
Executable
+95
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Post, update, or delete a PR comment with deduplication.
|
||||
|
||||
Uses hidden HTML markers to find existing comments and avoid duplicates.
|
||||
Reads comment body from stdin when posting or updating.
|
||||
|
||||
Usage:
|
||||
echo "comment body" | python3 pr_comment.py --marker pr-title --pr 123 --result fail
|
||||
python3 pr_comment.py --marker pr-title --pr 123 --result pass
|
||||
|
||||
Results:
|
||||
fail - post/update comment with body from stdin
|
||||
warn - post/update comment with body from stdin
|
||||
pass - delete existing comment if any
|
||||
|
||||
Requires GH_TOKEN and GITHUB_REPOSITORY environment variables.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def gh_api(endpoint: str, method: str = 'GET', body: dict | None = None) -> str:
|
||||
"""Call the GitHub API via gh cli."""
|
||||
cmd = ['gh', 'api', endpoint, '-X', method]
|
||||
if body:
|
||||
for key, value in body.items():
|
||||
cmd.extend(['-f', f'{key}={value}'])
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if result.returncode != 0 and method != 'DELETE':
|
||||
print(f"gh api error: {result.stderr}", file=sys.stderr)
|
||||
return result.stdout
|
||||
|
||||
|
||||
def find_comment(repo: str, pr: int, marker: str) -> str | None:
|
||||
"""Find an existing comment by its hidden marker. Returns comment ID or None."""
|
||||
response = gh_api(f"repos/{repo}/issues/{pr}/comments?per_page=100")
|
||||
try:
|
||||
comments = json.loads(response)
|
||||
except json.JSONDecodeError:
|
||||
return None
|
||||
|
||||
if not isinstance(comments, list):
|
||||
return None
|
||||
|
||||
for comment in comments:
|
||||
if isinstance(comment, dict) and comment.get('body', '').startswith(marker):
|
||||
return str(comment['id'])
|
||||
return None
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description='Manage PR quality comments')
|
||||
parser.add_argument('--marker', required=True,
|
||||
help='Marker name (e.g. pr-title, commit-msgs, pr-body)')
|
||||
parser.add_argument('--pr', required=True, type=int,
|
||||
help='Pull request number')
|
||||
parser.add_argument('--result', required=True, choices=['pass', 'fail', 'warn'],
|
||||
help='Check result: pass deletes comment, fail/warn posts it')
|
||||
args = parser.parse_args()
|
||||
|
||||
repo = os.environ.get('GITHUB_REPOSITORY', '')
|
||||
if not repo:
|
||||
print("GITHUB_REPOSITORY not set", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
|
||||
marker = f"<!-- commit-quality-{args.marker} -->"
|
||||
existing_id = find_comment(repo, args.pr, marker)
|
||||
|
||||
if args.result == 'pass':
|
||||
if existing_id:
|
||||
gh_api(f"repos/{repo}/issues/comments/{existing_id}", method='DELETE')
|
||||
return
|
||||
|
||||
# Read comment body from stdin
|
||||
body_content = sys.stdin.read().strip()
|
||||
if not body_content:
|
||||
print("No comment body provided on stdin", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
|
||||
full_body = f"{marker}\n{body_content}"
|
||||
|
||||
if existing_id:
|
||||
gh_api(f"repos/{repo}/issues/comments/{existing_id}", method='PATCH',
|
||||
body={'body': full_body})
|
||||
else:
|
||||
gh_api(f"repos/{repo}/issues/{args.pr}/comments", method='POST',
|
||||
body={'body': full_body})
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Executable
+163
@@ -0,0 +1,163 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# test_metadata_sync.sh - Test metadata_sync.sh locally using Docker
|
||||
#
|
||||
# Usage:
|
||||
# Tools/ci/test_metadata_sync.sh [OPTIONS] [TYPES...]
|
||||
#
|
||||
# Options:
|
||||
# --shell Drop into interactive shell instead of running sync
|
||||
# --verbose Pass --verbose to metadata_sync.sh
|
||||
# --skip-build Skip SITL build (use existing build artifacts)
|
||||
# --help Show this help
|
||||
#
|
||||
# Types:
|
||||
# Same as metadata_sync.sh: parameters, airframes, modules, msg_docs, uorb_graphs, failsafe_web, all
|
||||
#
|
||||
# Examples:
|
||||
# # Test full regeneration
|
||||
# Tools/ci/test_metadata_sync.sh all
|
||||
#
|
||||
# # Test just parameters (faster)
|
||||
# Tools/ci/test_metadata_sync.sh parameters
|
||||
#
|
||||
# # Drop into shell for debugging
|
||||
# Tools/ci/test_metadata_sync.sh --shell
|
||||
#
|
||||
# # Skip build if you already have artifacts
|
||||
# Tools/ci/test_metadata_sync.sh --skip-build --verbose all
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
DOCKER_IMAGE="px4io/px4-dev:v1.17.0-alpha1"
|
||||
CONTAINER_NAME="px4-metadata-test-$$"
|
||||
|
||||
SHELL_MODE=false
|
||||
VERBOSE=""
|
||||
SKIP_BUILD=false
|
||||
TYPES=()
|
||||
|
||||
show_help() {
|
||||
head -n 28 "$0" | tail -n +2 | sed 's/^# \?//'
|
||||
exit 0
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
echo "[test] Cleaning up container..."
|
||||
docker rm -f "$CONTAINER_NAME" 2>/dev/null || true
|
||||
}
|
||||
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--shell)
|
||||
SHELL_MODE=true
|
||||
shift
|
||||
;;
|
||||
--verbose)
|
||||
VERBOSE="--verbose"
|
||||
shift
|
||||
;;
|
||||
--skip-build)
|
||||
SKIP_BUILD=true
|
||||
shift
|
||||
;;
|
||||
--help|-h)
|
||||
show_help
|
||||
;;
|
||||
-*)
|
||||
echo "Unknown option: $1" >&2
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
TYPES+=("$1")
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Default to all types
|
||||
if [[ ${#TYPES[@]} -eq 0 ]]; then
|
||||
TYPES=("all")
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
parse_args "$@"
|
||||
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
echo "[test] Using Docker image: $DOCKER_IMAGE"
|
||||
echo "[test] Repository root: $REPO_ROOT"
|
||||
|
||||
# Pull image if not present
|
||||
if ! docker image inspect "$DOCKER_IMAGE" >/dev/null 2>&1; then
|
||||
echo "[test] Pulling Docker image..."
|
||||
docker pull "$DOCKER_IMAGE"
|
||||
fi
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
# Handle git worktrees: the .git file points to the main repo's .git directory
|
||||
# We need to mount that directory too so git works inside the container
|
||||
local git_mounts=()
|
||||
if [[ -f "$REPO_ROOT/.git" ]]; then
|
||||
# It's a worktree - read the gitdir path and mount it
|
||||
local gitdir
|
||||
gitdir=$(grep '^gitdir:' "$REPO_ROOT/.git" | cut -d' ' -f2)
|
||||
if [[ -n "$gitdir" ]]; then
|
||||
# Mount the gitdir at the same path so the .git file reference works
|
||||
git_mounts+=("-v" "$gitdir:$gitdir:ro")
|
||||
# Also need the main .git directory (parent of worktrees/)
|
||||
local main_git_dir
|
||||
main_git_dir=$(dirname "$(dirname "$gitdir")")
|
||||
git_mounts+=("-v" "$main_git_dir:$main_git_dir:ro")
|
||||
echo "[test] Detected git worktree, mounting git directories"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$SHELL_MODE" == "true" ]]; then
|
||||
echo "[test] Starting interactive shell..."
|
||||
echo "[test] Run: Tools/ci/metadata_sync.sh --generate --sync all"
|
||||
docker run -it --rm \
|
||||
--name "$CONTAINER_NAME" \
|
||||
-v "$REPO_ROOT:/src" \
|
||||
"${git_mounts[@]}" \
|
||||
-w /src \
|
||||
"$DOCKER_IMAGE" \
|
||||
/bin/bash
|
||||
else
|
||||
echo "[test] Running metadata sync for: ${TYPES[*]}"
|
||||
|
||||
# Build the command
|
||||
local cmd=""
|
||||
|
||||
if [[ "$SKIP_BUILD" == "false" ]]; then
|
||||
cmd="Tools/ci/metadata_sync.sh --generate --sync $VERBOSE ${TYPES[*]}"
|
||||
else
|
||||
cmd="Tools/ci/metadata_sync.sh --sync $VERBOSE ${TYPES[*]}"
|
||||
fi
|
||||
|
||||
echo "[test] Command: $cmd"
|
||||
|
||||
docker run --rm \
|
||||
--name "$CONTAINER_NAME" \
|
||||
-v "$REPO_ROOT:/src" \
|
||||
"${git_mounts[@]}" \
|
||||
-w /src \
|
||||
"$DOCKER_IMAGE" \
|
||||
/bin/bash -c "$cmd"
|
||||
|
||||
echo ""
|
||||
echo "[test] Done! Check git status for changes:"
|
||||
echo " git status -s docs/"
|
||||
echo ""
|
||||
echo "[test] To see what changed:"
|
||||
echo " git diff docs/"
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
+4
-24
@@ -1,47 +1,27 @@
|
||||
#! /bin/bash
|
||||
|
||||
if [ -z ${PX4_DOCKER_REPO+x} ]; then
|
||||
echo "guessing PX4_DOCKER_REPO based on input";
|
||||
if [[ $@ =~ .*clang.* ]] || [[ $@ =~ .*scan-build.* ]]; then
|
||||
# clang tools
|
||||
PX4_DOCKER_REPO="px4io/px4-dev-clang:2021-02-04"
|
||||
elif [[ $@ =~ .*tests* ]]; then
|
||||
# run all tests with simulation
|
||||
PX4_DOCKER_REPO="px4io/px4-dev-simulation-bionic:2021-12-11"
|
||||
fi
|
||||
PX4_DOCKER_REPO="px4io/px4-dev:v1.17.0-beta1"
|
||||
else
|
||||
echo "PX4_DOCKER_REPO is set to '$PX4_DOCKER_REPO'";
|
||||
fi
|
||||
|
||||
# otherwise default to nuttx
|
||||
if [ -z ${PX4_DOCKER_REPO+x} ]; then
|
||||
PX4_DOCKER_REPO="px4io/px4-dev:v1.16.0-rc1-258-g0369abd556"
|
||||
fi
|
||||
|
||||
echo "PX4_DOCKER_REPO: $PX4_DOCKER_REPO";
|
||||
|
||||
PWD=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||
SRC_DIR=$PWD/../
|
||||
SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||
SRC_DIR=${SCRIPT_DIR}/../
|
||||
|
||||
CCACHE_DIR=${HOME}/.ccache
|
||||
mkdir -p "${CCACHE_DIR}"
|
||||
|
||||
docker run -it --rm -w "${SRC_DIR}" \
|
||||
--user="$(id -u):$(id -g)" \
|
||||
--env=AWS_ACCESS_KEY_ID \
|
||||
--env=AWS_SECRET_ACCESS_KEY \
|
||||
--env=BRANCH_NAME \
|
||||
--env=CCACHE_DIR="${CCACHE_DIR}" \
|
||||
--env=CI \
|
||||
--env=CODECOV_TOKEN \
|
||||
--env=COVERALLS_REPO_TOKEN \
|
||||
--env=PX4_ASAN \
|
||||
--env=PX4_MSAN \
|
||||
--env=PX4_TSAN \
|
||||
--env=PX4_UBSAN \
|
||||
--env=TRAVIS_BRANCH \
|
||||
--env=TRAVIS_BUILD_ID \
|
||||
--publish 14556:14556/udp \
|
||||
--volume=${CCACHE_DIR}:${CCACHE_DIR}:rw \
|
||||
--volume=${SRC_DIR}:${SRC_DIR}:rw \
|
||||
${PX4_DOCKER_REPO} /bin/bash -c "$1 $2 $3"
|
||||
${PX4_DOCKER_REPO} /bin/bash -c "$@"
|
||||
|
||||
@@ -234,14 +234,14 @@ def get_actuator_output(yaml_config, output_functions, timer_config_file, verbos
|
||||
param_prefix = process_param_prefix(group['param_prefix'])
|
||||
standard_params = group.get('standard_params', {})
|
||||
standard_params_array = [
|
||||
( 'function', 'Function', 'FUNC', False ),
|
||||
( 'disarmed', 'Disarmed', 'DIS', False ),
|
||||
( 'min', 'Minimum', 'MIN', False ),
|
||||
( 'max', 'Maximum', 'MAX', False ),
|
||||
( 'center', 'Center\n(for Servos)', 'CENT', False ),
|
||||
( 'failsafe', 'Failsafe', 'FAIL', True ),
|
||||
( 'function', 'Function', 'FUNC', False, True ),
|
||||
( 'disarmed', 'Disarmed', 'DIS', False, True ),
|
||||
( 'min', 'Minimum', 'MIN', False, True ),
|
||||
( 'center', 'Center\n(for Servos)', 'CENT', False, False ),
|
||||
( 'max', 'Maximum', 'MAX', False, True ),
|
||||
( 'failsafe', 'Failsafe', 'FAIL', True, True ),
|
||||
]
|
||||
for key, label, param_suffix, advanced in standard_params_array:
|
||||
for key, label, param_suffix, advanced, has_function in standard_params_array:
|
||||
show_if = None
|
||||
if key in standard_params and 'show_if' in standard_params[key]:
|
||||
show_if = standard_params[key]['show_if']
|
||||
@@ -250,13 +250,12 @@ def get_actuator_output(yaml_config, output_functions, timer_config_file, verbos
|
||||
param = {
|
||||
'label': label,
|
||||
'name': param_prefix+'_'+param_suffix+'${i}',
|
||||
'function': key,
|
||||
}
|
||||
if has_function: param['function'] = key
|
||||
if advanced: param['advanced'] = True
|
||||
if show_if: param['show-if'] = show_if
|
||||
per_channel_params.append(param)
|
||||
|
||||
|
||||
param = {
|
||||
'label': 'Rev Range\n(for Servos)',
|
||||
'name': param_prefix+'_REV',
|
||||
|
||||
@@ -312,11 +312,7 @@ When set to -1 (default), the value depends on the function (see {:}).
|
||||
if standard_params[key]['max'] >= 1<<16:
|
||||
raise Exception('maximum value for {:} expected <= {:} (got {:})'.format(key, 1<<16, standard_params[key]['max']))
|
||||
|
||||
if key == 'failsafe':
|
||||
standard_params[key]['default'] = -1
|
||||
standard_params[key]['min'] = -1
|
||||
|
||||
if key == 'center':
|
||||
if key == 'failsafe' or key == 'center':
|
||||
standard_params[key]['default'] = -1
|
||||
standard_params[key]['min'] = -1
|
||||
|
||||
|
||||
+831
-68
@@ -8,6 +8,803 @@ Also generates docs/en/middleware/dds_topics.md from dds_topics.yaml
|
||||
import os
|
||||
import argparse
|
||||
import sys
|
||||
import re
|
||||
|
||||
VALID_FIELDS = { #Note, also have to add the message types as those can be fields
|
||||
'uint64',
|
||||
'uint16',
|
||||
'uint8',
|
||||
'uint32'
|
||||
}
|
||||
|
||||
ALLOWED_UNITS = set(["m", "m/s", "m/s^2", "(m/s)^2", "deg", "deg/s", "rad", "rad/s", "rad^2", "rpm" ,"V", "A", "mA", "mAh", "W", "Wh", "dBm", "h", "minutes", "s", "ms", "us", "Ohm", "MB", "Kb/s", "degC","Pa", "%", "norm", "-"])
|
||||
invalid_units = set()
|
||||
ALLOWED_FRAMES = set(["NED", "Body", "FRD", "ENU"])
|
||||
ALLOWED_INVALID_VALUES = set(["NaN", "0", "-1"])
|
||||
ALLOWED_CONSTANTS_NOT_IN_ENUM = set(["ORB_QUEUE_LENGTH","MESSAGE_VERSION"])
|
||||
|
||||
class Error:
|
||||
def __init__(self, type, message, linenumber=None, issueString = None, field = None):
|
||||
self.type = type
|
||||
self.message = message
|
||||
self.linenumber = linenumber
|
||||
self.issueString = issueString
|
||||
self.field = field
|
||||
|
||||
def display_error(self):
|
||||
#print(f"Debug: Error: display_error")
|
||||
|
||||
|
||||
if 'trailing_whitespace' == self.type:
|
||||
if self.issueString.strip():
|
||||
print(f"NOTE: Line has trailing whitespace ({self.message}: {self.linenumber}): {self.issueString}")
|
||||
else:
|
||||
print(f"NOTE: Line has trailing whitespace ({self.message}: {self.linenumber})")
|
||||
elif 'leading_whitespace_field_or_constant' == self.type:
|
||||
print(f"NOTE: Whitespace before field or constant ({self.message}: {self.linenumber}): {self.issueString}")
|
||||
elif 'field_or_constant_has_multiple_whitepsace' == self.type:
|
||||
print(f"NOTE: Field/constant has more than one sequential whitespace character ({self.message}: {self.linenumber}): {self.issueString}")
|
||||
elif 'empty_start_line' == self.type:
|
||||
print(f"NOTE: Empty line at start of file ({self.message}: {self.linenumber})")
|
||||
elif 'internal_comment' == self.type:
|
||||
print(f"NOTE: Internal Comment ({self.message}: {self.linenumber})\n {self.issueString}")
|
||||
elif 'internal_comment_empty' == self.type:
|
||||
print(f"NOTE: Empty Internal Comment ({self.message}: {self.linenumber})")
|
||||
elif 'summary_missing' == self.type:
|
||||
print(f"WARNING: No message description ({self.message})")
|
||||
elif 'topic_error' == self.type:
|
||||
print(f"NOTE: TOPIC ISSUE: {self.issueString}")
|
||||
elif 'unknown_unit' == self.type:
|
||||
print(f"WARNING: Unknown Unit: [{self.issueString}] on `{self.field}` ({self.message}: {self.linenumber})")
|
||||
elif 'constant_not_in_assigned_enum' == self.type:
|
||||
print(f"WARNING: `{self.issueString}` constant: Prefix not in `@enum` field metadata ({self.message}: {self.linenumber})")
|
||||
elif 'unknown_invalid_value' == self.type:
|
||||
print(f"WARNING: Unknown @invalid value: [{self.issueString}] on `{self.field}` ({self.message}: {self.linenumber})")
|
||||
elif 'unknown_frame' == self.type:
|
||||
print(f"WARNING: Unknown @frame: [{self.issueString}] on `{self.field}` ({self.message}: {self.linenumber})")
|
||||
elif 'command_no_params_pipes' == self.type:
|
||||
print(f"WARNING: `{self.field}` command has no parameters (pipes): [{self.issueString}] ({self.message}: {self.linenumber})")
|
||||
elif 'command_missing_params' == self.type:
|
||||
print(f"WARNING: `{self.field}` command missing params - should be 7 params surrounded by 8 pipes: [{self.issueString}] ({self.message}: {self.linenumber})")
|
||||
elif 'command_too_many_params' == self.type:
|
||||
print(f"WARNING: `{self.field}` command too many params (should be 7). Extras: [{self.issueString}] ({self.message}: {self.linenumber})")
|
||||
|
||||
|
||||
else:
|
||||
self.display_info()
|
||||
|
||||
def display_info(self):
|
||||
"""
|
||||
Display info about an error.
|
||||
Used as a fallback if error does not have specific printout in display_error()
|
||||
"""
|
||||
#print(f"Debug: Error: display_info")
|
||||
print(f" type: {self.type}, message: {self.message}, linenumber: {self.linenumber}, issueString: {self.issueString}, field: {self.field}")
|
||||
|
||||
class Enum:
|
||||
def __init__(self, name, parentMessage):
|
||||
self.name = name
|
||||
self.parent = parentMessage
|
||||
self.enumValues = dict()
|
||||
|
||||
def display_info(self):
|
||||
"""
|
||||
Display info about an enum
|
||||
"""
|
||||
print(f"Debug: Enum: display_info")
|
||||
print(f" name: {self.name}")
|
||||
for key, value in self.enumValues.items():
|
||||
value.display_info()
|
||||
|
||||
class ConstantValue:
|
||||
def __init__(self, name, type, value, comment, line_number):
|
||||
self.name = name.strip()
|
||||
self.type = type.strip()
|
||||
self.value = value.strip()
|
||||
self.comment = comment
|
||||
self.line_number = line_number
|
||||
|
||||
if not self.value:
|
||||
print(f"Debug WARNING: NO VALUE in ConstantValue: {self.name}") ## TODO make into ERROR
|
||||
exit()
|
||||
|
||||
# TODO if value or name are empty, error
|
||||
|
||||
def display_info(self):
|
||||
print(f"Debug: ConstantValue: display_info")
|
||||
print(f" name: {self.name}, type: {self.type}, value: {self.value}, comment: {self.comment}, line: {self.line_number}")
|
||||
|
||||
|
||||
class CommandParam:
|
||||
"""
|
||||
Represents an individual param in a command constant
|
||||
Encapsulates parsing of the param to extract units etc.
|
||||
"""
|
||||
|
||||
def __init__(self, num, paramText, line_number, parentCommand):
|
||||
self.paramNum = num
|
||||
self.paramText = paramText.strip()
|
||||
self.enum = None
|
||||
self.range = None
|
||||
#self.type = type
|
||||
self.units = []
|
||||
self.enums = []
|
||||
self.minValue = None
|
||||
self.maxValue = None
|
||||
self.invalidValue = None
|
||||
self.frameValue = None
|
||||
self.lineNumber = line_number
|
||||
self.parent = parentCommand
|
||||
self.parentMessage = self.parent.parent
|
||||
|
||||
match = None
|
||||
if self.paramText:
|
||||
match = re.match(r'^((?:\[[^\]]*\]\s*)+)(.*)$', paramText)
|
||||
self.description = paramText
|
||||
bracketed_part = None
|
||||
if match:
|
||||
bracketed_part = match.group(1).strip() # .strip() removes trailing whitespace from the bracketed part
|
||||
self.description = match.group(2).strip()
|
||||
if bracketed_part:
|
||||
# get units
|
||||
bracket_content_matches = re.findall(r'\[(.*?)\]', bracketed_part)
|
||||
#print(f"DEBUG: bracket_content_matches: {bracket_content_matches}")
|
||||
for item in bracket_content_matches:
|
||||
item = item.strip()
|
||||
if item.startswith('@'): # Not a unit:
|
||||
if item.startswith('@enum'):
|
||||
item = item.split(" ")
|
||||
enum = item[1].strip()
|
||||
if enum and enum not in self.enums:
|
||||
self.enums.append(enum)
|
||||
|
||||
# Create parent enum objects for any enums created in this step
|
||||
for enumName in self.enums:
|
||||
if not enumName in self.parentMessage.enums:
|
||||
self.parentMessage.enums[enumName]=Enum(enumName,self.parentMessage)
|
||||
|
||||
elif item.startswith('@range'):
|
||||
item = item[6:].strip().split(",")
|
||||
self.range = item
|
||||
self.minValue = item[0].strip()
|
||||
self.maxValue = item[1].strip()
|
||||
elif item.startswith('@invalid'):
|
||||
self.invalidValue = item[8:].strip()
|
||||
#TODO: Do we require a description? (not currently)
|
||||
if self.invalidValue.split(" ")[0] not in ALLOWED_INVALID_VALUES:
|
||||
print(f"TODO: Command param do not support @invalid: {self.invalidValue}")
|
||||
"""
|
||||
error = Error("unknown_invalid_value", self.parent.filename, self.lineNumber, self.invalidValue, self.name)
|
||||
#error.display_error()
|
||||
if not "unknown_invalid_value" in self.parent.errors:
|
||||
self.parent.errors["unknown_invalid_value"] = []
|
||||
self.parent.errors["unknown_invalid_value"].append(error)
|
||||
"""
|
||||
|
||||
elif item.startswith('@frame'):
|
||||
self.frameValue = item[6:].strip()
|
||||
print(f"TODO: Command param do not support @frame: {self.frameValue}")
|
||||
"""
|
||||
if self.frameValue not in ALLOWED_FRAMES:
|
||||
error = Error("unknown_frame", self.parent.filename, self.lineNumber, self.frameValue, self.name)
|
||||
#error.display_error()
|
||||
if not "unknown_frame" in self.parent.errors:
|
||||
self.parent.errors["unknown_frame"] = []
|
||||
self.parent.errors["unknown_frame"].append(error)
|
||||
"""
|
||||
else:
|
||||
print(f"WARNING: Unhandled metadata in message comment: {item}")
|
||||
# TODO - report errors for different kinds of metadata
|
||||
exit()
|
||||
|
||||
else: # bracket is a unit
|
||||
unit = item.strip()
|
||||
|
||||
if item == "-":
|
||||
unit = ""
|
||||
|
||||
if unit and unit not in self.units:
|
||||
self.units.append(unit)
|
||||
|
||||
if unit not in ALLOWED_UNITS:
|
||||
invalid_units.add(unit)
|
||||
error = Error("unknown_unit", self.parentMessage.filename, self.lineNumber, unit, self.parent.name)
|
||||
#error.display_error()
|
||||
if not "unknown_unit" in self.parentMessage.errors:
|
||||
self.parentMessage.errors["unknown_unit"] = []
|
||||
self.parentMessage.errors["unknown_unit"].append(error)
|
||||
|
||||
|
||||
def display_info(self):
|
||||
print(f"Debug: CommandParam: display_info")
|
||||
print(f" id: {self.paramNum}")
|
||||
print(f" paramText: {self.paramText}\n unit: {self.units}\n enums: {self.enums}\n lineNumber: {self.lineNumber}\n range: {self.range}\n minValue: {self.minValue}\n maxValue: {self.maxValue}\n invalidValue: {self.invalidValue}\n frameValue: {self.frameValue}\n parent: {self.parent}\n ")
|
||||
|
||||
|
||||
|
||||
class CommandConstant:
|
||||
"""
|
||||
Represents a constant that is a command definition.
|
||||
Encapsulates parsing of the command format.
|
||||
The individual params are further parsed in CommandParam
|
||||
"""
|
||||
def __init__(self, name, type, value, comment, line_number, parentMessage):
|
||||
self.name = name.strip()
|
||||
self.type = type.strip()
|
||||
self.value = value.strip()
|
||||
self.comment = comment
|
||||
self.line_number = line_number
|
||||
self.parent = parentMessage
|
||||
|
||||
self.description = self.comment
|
||||
self.param1 = None
|
||||
self.param2 = None
|
||||
self.param3 = None
|
||||
self.param4 = None
|
||||
self.param5 = None
|
||||
self.param6 = None
|
||||
self.param7 = None
|
||||
|
||||
if not self.value:
|
||||
print(f"Debug WARNING: NO VALUE in CommandConstant: {self.name}") ## TODO make into ERROR
|
||||
exit()
|
||||
|
||||
if not self.comment: # This is an bug for a command
|
||||
#print(f"Debug WARNING: NO COMMENT in CommandConstant: {self.name}") ## TODO make into ERROR
|
||||
return
|
||||
|
||||
# Parse command comment to get the description and parameters.
|
||||
# print(f"Debug CommandConstant: {self.comment}")
|
||||
if not "|" in self.comment:
|
||||
# This is an error for a command constant
|
||||
error = Error("command_no_params_pipes", self.parent.filename, self.line_number, self.comment, self.name)
|
||||
#error.display_error()
|
||||
if not "command_no_params_pipes" in self.parent.errors:
|
||||
self.parent.errors["command_no_params_pipes"] = []
|
||||
self.parent.errors["command_no_params_pipes"].append(error)
|
||||
return
|
||||
|
||||
# Split on pipes
|
||||
commandSplit = self.comment.split("|")
|
||||
if len(commandSplit) < 9:
|
||||
# Should 7 pipes, so each command is fully surrounded
|
||||
error = Error("command_missing_params", self.parent.filename, self.line_number, self.comment, self.name)
|
||||
#error.display_error()
|
||||
if not "command_missing_params" in self.parent.errors:
|
||||
self.parent.errors["command_missing_params"] = []
|
||||
self.parent.errors["command_missing_params"].append(error)
|
||||
|
||||
self.description = commandSplit[0].strip()
|
||||
self.description = self.description if self.description else None
|
||||
|
||||
params_to_update = commandSplit[1:8]
|
||||
|
||||
for i, value in enumerate(params_to_update, start=1):
|
||||
if value.strip():
|
||||
# parse the param
|
||||
param = CommandParam(i, value, self.line_number, self)
|
||||
#param.display_info() # DEBUG CODE XXX
|
||||
setattr(self, f"param{i}", param)
|
||||
# parse the param
|
||||
|
||||
if len(commandSplit) > 8:
|
||||
extras = commandSplit[8:]
|
||||
error = Error("command_too_many_params", self.parent.filename, self.line_number, extras, self.name)
|
||||
if not "command_too_many_params" in self.parent.errors:
|
||||
self.parent.errors["command_too_many_params"] = []
|
||||
self.parent.errors["command_too_many_params"].append(error)
|
||||
|
||||
|
||||
# TODO if value or name are empty, error
|
||||
|
||||
def markdown_out(self):
|
||||
#print("DEBUG: CommandConstant.markdown_out")
|
||||
output = f"""### {self.name} ({self.value})
|
||||
|
||||
{self.description}
|
||||
|
||||
Param | Units | Range/Enum | Description
|
||||
--- | --- | --- | ---
|
||||
"""
|
||||
for i in range(1, 8):
|
||||
attr_name = f"param{i}"
|
||||
# getattr returns None if the attribute doesn't exist
|
||||
val = getattr(self, attr_name, None)
|
||||
|
||||
if val is not None:
|
||||
rangeVal = ""
|
||||
if val.minValue or val.maxValue:
|
||||
rangeVal = f"[{val.minValue if val.minValue else '-'} : {val.maxValue if val.maxValue else '-' }]"
|
||||
|
||||
output+=f"{i} | {", ".join(val.units)}|{', '.join(f"[{e}](#{e})" for e in val.enums)}{rangeVal} | {val.description}\n"
|
||||
else:
|
||||
output+=f"{i} | | | ?\n"
|
||||
|
||||
output+=f"\n"
|
||||
return output
|
||||
|
||||
|
||||
def display_info(self):
|
||||
print(f"Debug: CommandConstant: display_info")
|
||||
print(f" name: {self.name}, type: {self.type}, value: {self.value}, comment: {self.comment}, line: {self.line_number}")
|
||||
print(f" description: {self.description}\n param1: {self.param1}\n param2: {self.param2}\n param3: {self.param3}\n param4: {self.param4}\n param5: {self.param5}\n param6: {self.param6}\n param7: {self.param7}")
|
||||
|
||||
class MessageField:
|
||||
"""
|
||||
Represents a field.
|
||||
Encapsulates parsing of the field information.
|
||||
"""
|
||||
def __init__(self, name, type, comment, line_number, parentMessage):
|
||||
self.name = name
|
||||
self.type = type
|
||||
self.comment = comment
|
||||
self.unit = None
|
||||
self.enums = None
|
||||
self.minValue = None
|
||||
self.maxValue = None
|
||||
self.invalidValue = None
|
||||
self.frameValue = None
|
||||
self.lineNumber = line_number
|
||||
self.parent = parentMessage
|
||||
|
||||
#print(f"MessageComment: {comment}")
|
||||
match = None
|
||||
if self.comment:
|
||||
match = re.match(r'^((?:\[[^\]]*\]\s*)+)(.*)$', comment)
|
||||
self.description = comment
|
||||
bracketed_part = None
|
||||
if match:
|
||||
bracketed_part = match.group(1).strip() # .strip() removes trailing whitespace from the bracketed part
|
||||
self.description = match.group(2).strip()
|
||||
if bracketed_part:
|
||||
# get units
|
||||
bracket_content_matches = re.findall(r'\[(.*?)\]', bracketed_part)
|
||||
#print(f"bracket_content_matches: {bracket_content_matches}")
|
||||
for item in bracket_content_matches:
|
||||
item = item.strip()
|
||||
if item.startswith('@'): # Not a unit:
|
||||
if item.startswith('@enum'):
|
||||
item = item.split(" ")
|
||||
self.enums = item[1:]
|
||||
# Create parent enum objects
|
||||
for enumName in self.enums:
|
||||
if not enumName in parentMessage.enums:
|
||||
parentMessage.enums[enumName]=Enum(enumName,parentMessage)
|
||||
elif item.startswith('@range'):
|
||||
item = item[6:].strip().split(",")
|
||||
self.minValue = item[0].strip()
|
||||
self.maxValue = item[1].strip()
|
||||
elif item.startswith('@invalid'):
|
||||
self.invalidValue = item[8:].strip()
|
||||
#TODO: Do we require a description? (not currently)
|
||||
if self.invalidValue.split(" ")[0] not in ALLOWED_INVALID_VALUES:
|
||||
error = Error("unknown_invalid_value", self.parent.filename, self.lineNumber, self.invalidValue, self.name)
|
||||
#error.display_error()
|
||||
if not "unknown_invalid_value" in self.parent.errors:
|
||||
self.parent.errors["unknown_invalid_value"] = []
|
||||
self.parent.errors["unknown_invalid_value"].append(error)
|
||||
elif item.startswith('@frame'):
|
||||
self.frameValue = item[6:].strip()
|
||||
if self.frameValue not in ALLOWED_FRAMES:
|
||||
error = Error("unknown_frame", self.parent.filename, self.lineNumber, self.frameValue, self.name)
|
||||
#error.display_error()
|
||||
if not "unknown_frame" in self.parent.errors:
|
||||
self.parent.errors["unknown_frame"] = []
|
||||
self.parent.errors["unknown_frame"].append(error)
|
||||
else:
|
||||
print(f"WARNING: Unhandled metadata in message comment: {item}")
|
||||
# TODO - report errors for different kinds of metadata
|
||||
exit()
|
||||
|
||||
else: # bracket is a unit
|
||||
self.unit = item
|
||||
|
||||
if self.unit not in ALLOWED_UNITS:
|
||||
invalid_units.add(self.unit)
|
||||
error = Error("unknown_unit", self.parent.filename, self.lineNumber, self.unit, self.name)
|
||||
#error.display_error()
|
||||
if not "unknown_unit" in self.parent.errors:
|
||||
self.parent.errors["unknown_unit"] = []
|
||||
self.parent.errors["unknown_unit"].append(error)
|
||||
|
||||
if item == "-":
|
||||
self.unit = ""
|
||||
|
||||
|
||||
def display_info(self):
|
||||
print(f"Debug: MessageField: display_info")
|
||||
print(f" name: {self.name}, type: {self.type}, description: {self.description}, enums: {self.enums}, minValue: {self.minValue}, maxValue: {self.maxValue}, invalidValue: {self.invalidValue}, frameValue: {self.frameValue}")
|
||||
|
||||
|
||||
class UORBMessage:
|
||||
"""
|
||||
Represents a whole message, including fields, enums, commands, constants.
|
||||
The parser function delegates the parsing of each part of the message to
|
||||
more appropriate classes, once the specific type of line has been identified.
|
||||
"""
|
||||
|
||||
def __init__(self, filename):
|
||||
|
||||
self.filename = filename
|
||||
msg_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"../../msg")
|
||||
self.msg_filename = os.path.join(msg_path, self.filename)
|
||||
self.name = os.path.splitext(os.path.basename(msg_file))[0]
|
||||
self.shortDescription = ""
|
||||
self.longDescription = ""
|
||||
self.fields = []
|
||||
self.constantFields = dict()
|
||||
self.commandConstants = dict()
|
||||
self.enums = dict()
|
||||
self.output_file = os.path.join(output_dir, f"{self.name}.md")
|
||||
self.topics = []
|
||||
self.errors = dict()
|
||||
|
||||
self.parseFile()
|
||||
|
||||
if args.errors:
|
||||
#print(f"DEBUG: args.errors: {args.errors}")
|
||||
if args.error_messages:
|
||||
messages = args.error_messages.split(" ")
|
||||
#print(f"DEBUG: args.errors: {messages},self.name: {self.name}")
|
||||
if self.name in messages:
|
||||
self.reportErrors()
|
||||
#print(f"Debug: {self.name} in {messages}")
|
||||
else:
|
||||
self.reportErrors()
|
||||
|
||||
def reportErrors(self):
|
||||
#print(f"Debug: UORBMessage: reportErrors()")
|
||||
for errorType, errors in self.errors.items():
|
||||
for error in errors:
|
||||
error.display_error()
|
||||
|
||||
def markdown_out(self):
|
||||
#print(f"Debug: UORBMessage: markdown_out()")
|
||||
|
||||
# Add page header (forces wide pages)
|
||||
markdown = f"""---
|
||||
pageClass: is-wide-page
|
||||
---
|
||||
|
||||
# {self.name} (UORB message)
|
||||
|
||||
"""
|
||||
## Append description info if present
|
||||
markdown += f"{self.shortDescription}\n\n" if self.shortDescription else ""
|
||||
markdown += f"{self.longDescription}\n\n" if self.longDescription else ""
|
||||
|
||||
topicList = " ".join(self.topics)
|
||||
markdown += f"**TOPICS:** {topicList}\n\n"
|
||||
|
||||
# Generate field docs
|
||||
markdown += f"## Fields\n\n"
|
||||
markdown += "Name | Type | Unit [Frame] | Range/Enum | Description\n"
|
||||
markdown += "--- | --- | --- | --- | ---\n"
|
||||
for field in self.fields:
|
||||
unit = f"{field.unit}" if field.unit else ""
|
||||
frame = f"[{field.frameValue}]" if field.frameValue else ""
|
||||
unit = f"{unit} {frame}"
|
||||
unit.strip()
|
||||
unit = f" {unit}"
|
||||
|
||||
value = " "
|
||||
if field.enums:
|
||||
value = ""
|
||||
for enum in field.enums:
|
||||
value += f"[{enum}](#{enum})"
|
||||
value = value.strip()
|
||||
value = f"{value}"
|
||||
elif field.minValue or field.maxValue:
|
||||
value = f"[{field.minValue if field.minValue else '-'} : {field.maxValue if field.maxValue else '-' }]"
|
||||
|
||||
description = f" {field.description}" if field.description else ""
|
||||
invalid = f" (Invalid: {field.invalidValue}) " if field.invalidValue else ""
|
||||
markdown += f"{field.name} | `{field.type}` |{unit}|{value}|{description}{invalid}\n"
|
||||
|
||||
# Generate table for command docs
|
||||
if len(self.commandConstants) > 0:
|
||||
#print("DEBUGCOMMAND")
|
||||
markdown += f"\n## Commands\n\n"
|
||||
|
||||
"""
|
||||
markdown += "Name | Type | Value | Description\n"
|
||||
markdown += "--- | --- | --- |---\n"
|
||||
for name, command in self.commandConstants.items():
|
||||
description = f" {command.comment} " if enum.comment else " "
|
||||
markdown += f'<a id="#{name}"></a> {name} | `{command.type}` | {command.value} |{description}\n'
|
||||
"""
|
||||
for commandConstant in self.commandConstants.values():
|
||||
#print(commandConstant)
|
||||
markdown += commandConstant.markdown_out()
|
||||
|
||||
# Generate enum docs
|
||||
if len(self.enums) > 0:
|
||||
markdown += f"\n## Enums\n"
|
||||
|
||||
for name, enum in self.enums.items():
|
||||
markdown += f"\n### {name} {{#{name}}}\n\n"
|
||||
|
||||
markdown += "Name | Type | Value | Description\n"
|
||||
markdown += "--- | --- | --- | ---\n"
|
||||
|
||||
for enumValueName, enumValue in enum.enumValues.items():
|
||||
description = f" {enumValue.comment} " if enumValue.comment else " "
|
||||
markdown += f'<a id="#{enumValueName}"></a> {enumValueName} | `{enumValue.type}` | {enumValue.value} |{description}\n'
|
||||
|
||||
# Generate table for constants docs
|
||||
if len(self.constantFields) > 0:
|
||||
markdown += f"\n## Constants\n\n"
|
||||
markdown += "Name | Type | Value | Description\n"
|
||||
markdown += "--- | --- | --- |---\n"
|
||||
for name, enum in self.constantFields.items():
|
||||
description = f" {enum.comment} " if enum.comment else " "
|
||||
markdown += f'<a id="#{name}"></a> {name} | `{enum.type}` | {enum.value} |{description}\n'
|
||||
|
||||
|
||||
|
||||
# Append msg contents to the end
|
||||
with open(self.msg_filename, 'r') as source_file:
|
||||
msg_contents = source_file.read()
|
||||
msg_contents = msg_contents.strip()
|
||||
|
||||
#Format markdown using msg name, comment, url, contents.
|
||||
markdown += f"""
|
||||
|
||||
## Source Message
|
||||
|
||||
[Source file (GitHub)](https://github.com/PX4/PX4-Autopilot/blob/main/msg/{self.filename})
|
||||
|
||||
::: details Click here to see original file
|
||||
|
||||
```c
|
||||
{msg_contents}
|
||||
```
|
||||
|
||||
:::
|
||||
"""
|
||||
|
||||
with open(self.output_file, 'w') as content_file:
|
||||
content_file.write(markdown)
|
||||
|
||||
#exit()
|
||||
|
||||
|
||||
def display_info(self):
|
||||
print(f"UORBMessage: display_info")
|
||||
print(f" name: {self.name}")
|
||||
print(f" filename: {self.filename}, ")
|
||||
print(f" msg_filename: {self.msg_filename}, ")
|
||||
print(f"self.shortDescription: {self.shortDescription}")
|
||||
print(f"self.longDescription: {self.longDescription}")
|
||||
print(f"self.enums: {self.enums}")
|
||||
|
||||
for enum, enumObject in self.enums.items():
|
||||
enumObject.display_info()
|
||||
|
||||
# Output our data so far
|
||||
for field in self.fields:
|
||||
field.display_info()
|
||||
|
||||
for enumvalue in self.constantFields:
|
||||
print(enumvalue)
|
||||
self.constantFields[enumvalue].display_info()
|
||||
|
||||
def handleField(self, line, line_number, parentMessage):
|
||||
#print(f"debug: handleField: (line): \n {line}")
|
||||
# Note, here we know we don't have a comment or a topic.
|
||||
# We expect it to be a field.
|
||||
|
||||
# Check field doesn't have leading whitespace (trailing spaces already checked)
|
||||
if line[:1].isspace(): # Returns True for ' ', '\t', '\n', '\r', etc.
|
||||
#print("First character is whitespace")
|
||||
error = Error("leading_whitespace_field_or_constant", self.filename, line_number, line)
|
||||
if not "leading_whitespace_field_or_constant" in self.errors:
|
||||
self.errors["leading_whitespace_field_or_constant"] = []
|
||||
self.errors["leading_whitespace_field_or_constant"].append(error)
|
||||
|
||||
# Now we can parse the stripped line
|
||||
fieldOrConstant = line.strip()
|
||||
|
||||
# Check that the field or constant has only single whitespace separators
|
||||
stripped_fieldOrConstant = re.sub(r'\s+', ' ', fieldOrConstant) # Collapse all spaces to a single space (LHS already stripped).
|
||||
if stripped_fieldOrConstant != fieldOrConstant:
|
||||
#print("Field/Constant has multiple whitespace characters") # Since the collapsed version shows them.
|
||||
error = Error("field_or_constant_has_multiple_whitepsace", self.filename, line_number, line)
|
||||
if not "field_or_constant_has_multiple_whitepsace" in self.errors:
|
||||
self.errors["field_or_constant_has_multiple_whitepsace"] = []
|
||||
self.errors["field_or_constant_has_multiple_whitepsace"].append(error)
|
||||
|
||||
fieldOrConstant = stripped_fieldOrConstant
|
||||
|
||||
|
||||
|
||||
comment = None
|
||||
if "#" in line:
|
||||
commentExtract = line.split("#", 1) # Split once on left-most '#'
|
||||
fieldOrConstant = commentExtract[0].strip()
|
||||
comment = commentExtract[-1].strip()
|
||||
|
||||
if "=" not in fieldOrConstant:
|
||||
# Is a field:
|
||||
field = fieldOrConstant.split(" ")
|
||||
type = field[0].strip()
|
||||
name = field[1].strip()
|
||||
field = MessageField(name, type, comment, line_number, parentMessage)
|
||||
self.fields.append(field)
|
||||
else:
|
||||
temp = fieldOrConstant.split("=")
|
||||
value = temp[-1]
|
||||
typeAndName = temp[0].split(" ")
|
||||
type = typeAndName[0].strip()
|
||||
name = typeAndName[1].strip()
|
||||
if name.startswith("VEHICLE_CMD_") and parentMessage.name == 'VehicleCommand': #it's a command.
|
||||
#print(f"DEBUG: startswith VEHICLE_CMD_ {name}")
|
||||
commandConstant = CommandConstant(name, type, value, comment, line_number, parentMessage)
|
||||
#commandConstant.display_info()
|
||||
self.commandConstants[name]=commandConstant
|
||||
else: #it's a constant (or part of an enum)
|
||||
constantField = ConstantValue(name, type, value, comment, line_number)
|
||||
self.constantFields[name]=constantField
|
||||
|
||||
|
||||
def parseFile(self):
|
||||
initial_block_lines = []
|
||||
#stopping_token = None
|
||||
found_first_relevant_content = False
|
||||
gettingInitialComments = False
|
||||
gettingFields = False
|
||||
|
||||
with open(self.msg_filename, 'r', encoding='utf-8') as uorbfile:
|
||||
lines = uorbfile.read().splitlines()
|
||||
for line_number, line in enumerate(lines, 1):
|
||||
|
||||
if line != line.rstrip():
|
||||
#print(f"[{self.filename}] Trailing whitespace on line {line_number}: XX{line}YY")
|
||||
error = Error("trailing_whitespace", self.filename, line_number, line)
|
||||
if not "trailing_whitespace" in self.errors:
|
||||
self.errors["trailing_whitespace"] = []
|
||||
self.errors["trailing_whitespace"].append(error)
|
||||
|
||||
#print(f"line: {line}")
|
||||
stripped_line = re.sub(r'\s+', ' ', line).strip() # Collapse all spaces to a single space and strip stuff off end.
|
||||
#print(f"stripped_line: {stripped_line}")
|
||||
# TODO? Perhaps report whitespace if the size of those two is different and it is empty
|
||||
# Or perhaps we just fix it on request
|
||||
|
||||
isEmptyLine = False if line.strip() else True
|
||||
if not found_first_relevant_content and isEmptyLine: #Empty line
|
||||
#print(f"{self.filename}: Empty line at start of file: [{line_number}]\n {line}")
|
||||
error = Error("empty_start_line", self.filename, line_number, line)
|
||||
if not "empty_start_line" in self.errors:
|
||||
self.errors["empty_start_line"] = []
|
||||
self.errors["empty_start_line"].append(error)
|
||||
#error.display_error()
|
||||
continue
|
||||
if not found_first_relevant_content and not isEmptyLine:
|
||||
found_first_relevant_content = True
|
||||
|
||||
if stripped_line.startswith("#"):
|
||||
gettingInitialComments = True
|
||||
else:
|
||||
gettingInitialComments = False
|
||||
gettingFields = True
|
||||
|
||||
if gettingInitialComments and stripped_line.startswith("#"):
|
||||
stripped_line=stripped_line[1:].strip()
|
||||
#print(f"DEBUG: gettingInitialComments: comment line: {stripped_line}")
|
||||
initial_block_lines.append(stripped_line)
|
||||
else:
|
||||
gettingInitialComments = False
|
||||
gettingFields = True #Getting fields and constants
|
||||
if gettingFields:
|
||||
if isEmptyLine:
|
||||
continue # empty line
|
||||
if stripped_line.startswith("# TOPICS "):
|
||||
stripped_line = stripped_line[9:]
|
||||
stripped_line = stripped_line.split(" ")
|
||||
self.topics+= stripped_line
|
||||
# Note, default topic and topic errors handled after all lines parsed
|
||||
continue
|
||||
if stripped_line.startswith("#"):
|
||||
# Its an internal comment
|
||||
stripped_line=stripped_line[1:].strip()
|
||||
|
||||
if stripped_line:
|
||||
#print(f"{self.filename}: Internal comment: [{line_number}]\n {line}")
|
||||
error = Error("internal_comment", self.filename, line_number, line)
|
||||
if not "internal_comment" in self.errors:
|
||||
self.errors["internal_comment"] = []
|
||||
self.errors["internal_comment"].append(error)
|
||||
else:
|
||||
#print(f"{self.filename}: Empty internal comment: [{line_number}]\n {line}")
|
||||
error = Error("internal_comment_empty", self.filename, line_number, line)
|
||||
if not "internal_comment_empty" in self.errors:
|
||||
self.errors["internal_comment_empty"] = []
|
||||
self.errors["internal_comment_empty"].append(error)
|
||||
#pass # Empty comment
|
||||
continue
|
||||
|
||||
# Must be a field or a comment.
|
||||
self.handleField(line, line_number, parentMessage=self)
|
||||
|
||||
# Fix up topics if the topic is empty
|
||||
def camel_to_snake(name):
|
||||
# Insert underscore between lowercase/digit and uppercase letter
|
||||
s1 = re.sub('([a-z0-9])([A-Z])', r'\1_\2', name)
|
||||
# Insert underscore between consecutive uppercase and uppercase+lowercase
|
||||
return re.sub('([A-Z]+)([A-Z][a-z])', r'\1_\2', s1).lower()
|
||||
|
||||
defaultTopic = camel_to_snake(self.name)
|
||||
if len(self.topics) == 0:
|
||||
# We have no topic declared, so set the default topic
|
||||
self.topics.append(defaultTopic)
|
||||
elif len(self.topics) == 1:
|
||||
# We have 1 topic declared - either it is default or there is some issue.
|
||||
if defaultTopic in self.topics:
|
||||
# Declared topic is default topic
|
||||
error = Error("topic_error", self.filename, "", f"WARNING: TOPIC {defaultTopic} unnecessarily declared for {self.name}")
|
||||
else:
|
||||
# Declared topic is not default topic
|
||||
error = Error("topic_error", self.filename, "", f"NOTE: TOPIC {self.topics[0]}: Only Declared topic is not default topic {defaultTopic} for {self.name}")
|
||||
if not "topic_error" in self.errors:
|
||||
self.errors["topic_error"] = []
|
||||
self.errors["topic_error"].append(error)
|
||||
elif len(self.topics) > 1:
|
||||
if defaultTopic not in self.topics:
|
||||
error = Error("topic_error", self.filename, "", f"NOTE: TOPIC - Default topic {defaultTopic} for {self.name} not in {self.topics}")
|
||||
|
||||
# Parse our short and long description
|
||||
#print(f"DEBUG: initial_block_lines: {initial_block_lines}")
|
||||
doingLongDescription = False
|
||||
for summaryline in initial_block_lines:
|
||||
if not self.shortDescription and summaryline.strip() == '':
|
||||
continue
|
||||
if not doingLongDescription and not summaryline.strip() == '':
|
||||
self.shortDescription += f" {summaryline}"
|
||||
self.shortDescription = self.shortDescription.strip()
|
||||
if not self.shortDescription[-1:] == ".": # Add terminating fullstop if not present.
|
||||
self.shortDescription += "."
|
||||
if not doingLongDescription and summaryline.strip() == '':
|
||||
doingLongDescription = True
|
||||
continue
|
||||
if doingLongDescription:
|
||||
self.longDescription += f"{summaryline}\n"
|
||||
|
||||
if self.longDescription:
|
||||
self.longDescription.strip()
|
||||
|
||||
if not self.shortDescription:
|
||||
# Summary has not been defined
|
||||
error = Error("summary_missing", self.filename)
|
||||
if not "summary_missing" in self.errors:
|
||||
self.errors["summary_missing"] = []
|
||||
self.errors["summary_missing"].append(error)
|
||||
|
||||
|
||||
# TODO Parse our constantValues into enums, leaving only constants
|
||||
constantValuesToRemove = []
|
||||
#print(f"DEBUG: Self.enums: {self.enums}")
|
||||
for enumName, enumObject in self.enums.items():
|
||||
for enumValueName, enumValueObject in self.constantFields.items():
|
||||
if enumValueName.startswith(enumName):
|
||||
# Copy this value into the object (cant be duplicate because parent is dict)
|
||||
enumObject.enumValues[enumValueName]=enumValueObject
|
||||
constantValuesToRemove.append(enumValueName)
|
||||
# Now delete the original enumvalues
|
||||
for enumValName in constantValuesToRemove:
|
||||
del self.constantFields[enumValName]
|
||||
constantsNotAssignedToEnums = len(self.constantFields)
|
||||
if constantsNotAssignedToEnums > 0:
|
||||
#print(f"Debug: WARNING constantsNotAssignedToEnums: {constantsNotAssignedToEnums}")
|
||||
for enumValueName, enumValue in self.constantFields.items():
|
||||
if enumValueName in ALLOWED_CONSTANTS_NOT_IN_ENUM: # Ignore constants
|
||||
pass
|
||||
else:
|
||||
error = Error("constant_not_in_assigned_enum", self.filename, enumValue.line_number, enumValueName)
|
||||
if not "constant_not_in_assigned_enum" in self.errors:
|
||||
self.errors["constant_not_in_assigned_enum"] = []
|
||||
self.errors["constant_not_in_assigned_enum"].append(error)
|
||||
# TODO Maybe present as list of possible enums.
|
||||
|
||||
|
||||
import yaml
|
||||
@@ -36,11 +833,9 @@ def generate_dds_yaml_doc(allMessageFiles, output_file = 'dds_topics.md'):
|
||||
for message in data["subscriptions"]:
|
||||
all_message_types.add(message['type'].split("::")[-1])
|
||||
all_topics.add(message['topic'].split('/')[-1])
|
||||
if data["subscriptions_multi"]: # There is none now
|
||||
dds_markdown += "None\n"
|
||||
for message in data["subscriptions_multi"]:
|
||||
all_message_types.add(message['type'].split("::")[-1])
|
||||
all_topics.add(message['topic'].split('/')[-1])
|
||||
for message in (data.get("subscriptions_multi") or []):
|
||||
all_message_types.add(message['type'].split("::")[-1])
|
||||
all_topics.add(message['topic'].split('/')[-1])
|
||||
for message in allMessageFiles:
|
||||
all_messages_in_source.add(message.split('/')[-1].split('.')[0])
|
||||
messagesNotExported = all_messages_in_source - all_message_types
|
||||
@@ -77,13 +872,17 @@ Topic | Type| Rate Limit
|
||||
|
||||
dds_markdown += "\n## Subscriptions Multi\n\n"
|
||||
|
||||
if not data["subscriptions_multi"]: # There is none now
|
||||
subscriptions_multi = data.get("subscriptions_multi") or []
|
||||
if not subscriptions_multi:
|
||||
dds_markdown += "None\n"
|
||||
else:
|
||||
print("Warning - we now have subscription_multi data - check format")
|
||||
dds_markdown += "Topic | Type\n--- | ---\n"
|
||||
for message in data["subscriptions_multi"]:
|
||||
dds_markdown += f"{message['topic']} | {message['type']}\n"
|
||||
dds_markdown += "Topic | Type | Route Field | Max Instances\n--- | --- | --- | ---\n"
|
||||
for message in subscriptions_multi:
|
||||
type = message['type']
|
||||
px4Type = type.split("::")[-1]
|
||||
route_field = f"`{message['route_field']}`" if 'route_field' in message else "-"
|
||||
max_instances = message.get('max_instances', '-')
|
||||
dds_markdown += f"{message['topic']} | [{type}](../msg_docs/{px4Type}.md) | {route_field} | {max_instances}\n"
|
||||
|
||||
if messagesNotExported:
|
||||
# Print the topics that are not exported to DDS
|
||||
@@ -127,83 +926,47 @@ if __name__ == "__main__":
|
||||
|
||||
parser = argparse.ArgumentParser(description='Generate docs from .msg files')
|
||||
parser.add_argument('-d', dest='dir', help='output directory', required=True)
|
||||
parser.add_argument('-e', dest='errors', action='store_true', help='Report errors')
|
||||
parser.add_argument('-m', dest='error_messages', help='Message to report errors against (by default all)')
|
||||
args = parser.parse_args()
|
||||
|
||||
output_dir = args.dir
|
||||
if not os.path.isdir(output_dir):
|
||||
print(f"making output_dir {output_dir}")
|
||||
os.mkdir(output_dir)
|
||||
|
||||
msg_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"../../msg")
|
||||
msg_files = get_msgs_list(msg_path)
|
||||
|
||||
msg_files.sort()
|
||||
|
||||
versioned_msgs_list = ''
|
||||
unversioned_msgs_list = ''
|
||||
msgTypes = set()
|
||||
|
||||
for msg_file in msg_files:
|
||||
# Add messages to set of allowed types (compound types)
|
||||
msg_name = os.path.splitext(os.path.basename(msg_file))[0]
|
||||
output_file = os.path.join(output_dir, msg_name+'.md')
|
||||
msg_filename = os.path.join(msg_path, msg_file)
|
||||
print("{:} -> {:}".format(msg_filename, output_file))
|
||||
msgTypes.add(msg_name)
|
||||
|
||||
#Format msg url
|
||||
msg_url="[source file](https://github.com/PX4/PX4-Autopilot/blob/main/msg/%s)" % msg_file
|
||||
|
||||
msg_description = ""
|
||||
summary_description = ""
|
||||
|
||||
#Get msg description (first non-empty comment line from top of msg)
|
||||
with open(msg_filename, 'r') as lineparser:
|
||||
line = lineparser.readline()
|
||||
while line.startswith('#') or (line.strip() == ''):
|
||||
print('DEBUG: line: %s' % line)
|
||||
line=line[1:].strip()+'\n'
|
||||
stripped_line=line.strip()
|
||||
if msg_description and not summary_description and stripped_line=='':
|
||||
summary_description = msg_description.strip()
|
||||
|
||||
msg_description+=line
|
||||
line = lineparser.readline()
|
||||
msg_description=msg_description.strip()
|
||||
if not summary_description and msg_description:
|
||||
summary_description = msg_description
|
||||
print('msg_description: Z%sZ' % msg_description)
|
||||
print('summary_description: Z%sZ' % summary_description)
|
||||
summary_description
|
||||
msg_contents = ""
|
||||
#Get msg contents (read the file)
|
||||
with open(msg_filename, 'r') as source_file:
|
||||
msg_contents = source_file.read()
|
||||
|
||||
#Format markdown using msg name, comment, url, contents.
|
||||
markdown_output="""# %s (UORB message)
|
||||
|
||||
%s
|
||||
|
||||
%s
|
||||
|
||||
```c
|
||||
%s
|
||||
```
|
||||
""" % (msg_name, msg_description, msg_url, msg_contents)
|
||||
|
||||
with open(output_file, 'w') as content_file:
|
||||
content_file.write(markdown_output)
|
||||
for msg_file in msg_files:
|
||||
message = UORBMessage(msg_file)
|
||||
# Any additional tests that can't be in UORBMessage parser go here.
|
||||
message.markdown_out()
|
||||
|
||||
# Categorize as versioned or unversioned
|
||||
if "versioned" in msg_file:
|
||||
versioned_msgs_list += '- [%s](%s.md)' % (msg_name, msg_name)
|
||||
if summary_description:
|
||||
versioned_msgs_list += " — %s" % summary_description
|
||||
versioned_msgs_list += f"- [{message.name}]({message.name}.md)"
|
||||
if message.shortDescription:
|
||||
versioned_msgs_list += f" — {message.shortDescription}"
|
||||
versioned_msgs_list += "\n"
|
||||
else:
|
||||
unversioned_msgs_list += '- [%s](%s.md)' % (msg_name, msg_name)
|
||||
if summary_description:
|
||||
unversioned_msgs_list += " — %s" % summary_description
|
||||
unversioned_msgs_list += f"- [{message.name}]({message.name}.md)"
|
||||
if message.shortDescription:
|
||||
unversioned_msgs_list += f" — {message.shortDescription}"
|
||||
unversioned_msgs_list += "\n"
|
||||
|
||||
# Write out the index.md file
|
||||
index_text="""# uORB Message Reference
|
||||
index_text=f"""# uORB Message Reference
|
||||
|
||||
::: info
|
||||
This list is [auto-generated](https://github.com/PX4/PX4-Autopilot/blob/main/Tools/msg/generate_msg_docs.py) from the source code.
|
||||
@@ -218,14 +981,14 @@ Graphs showing how these are used [can be found here](../middleware/uorb_graph.m
|
||||
|
||||
## Versioned Messages
|
||||
|
||||
%s
|
||||
{versioned_msgs_list}
|
||||
|
||||
## Unversioned Messages
|
||||
|
||||
%s
|
||||
""" % (versioned_msgs_list, unversioned_msgs_list)
|
||||
{unversioned_msgs_list}
|
||||
"""
|
||||
index_file = os.path.join(output_dir, 'index.md')
|
||||
with open(index_file, 'w') as content_file:
|
||||
with open(index_file, 'w', encoding='utf-8') as content_file:
|
||||
content_file.write(index_text)
|
||||
|
||||
generate_dds_yaml_doc(msg_files)
|
||||
|
||||
@@ -1138,6 +1138,8 @@ if num_mags >= 1:
|
||||
|
||||
if not math.isnan(sensor_mag_0['temperature'][0]):
|
||||
|
||||
mag_0_params['TC_M0_ID'] = int(np.median(sensor_mag_0['device_id']))
|
||||
|
||||
# find the min, max and reference temperature
|
||||
mag_0_params['TC_M0_TMIN'] = np.amin(sensor_mag_0['temperature'])
|
||||
mag_0_params['TC_M0_TMAX'] = np.amax(sensor_mag_0['temperature'])
|
||||
|
||||
Executable
+2113
File diff suppressed because it is too large
Load Diff
+8
-8
@@ -42,6 +42,7 @@
|
||||
import argparse
|
||||
import json
|
||||
import base64
|
||||
import os
|
||||
import zlib
|
||||
import time
|
||||
import subprocess
|
||||
@@ -99,14 +100,13 @@ if args.summary != None:
|
||||
if args.description != None:
|
||||
desc['description'] = str(args.description)
|
||||
if args.git_identity != None:
|
||||
cmd = "git --git-dir '{:}/.git' describe --exclude ext/* --always --tags".format(args.git_identity)
|
||||
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout
|
||||
desc['git_identity'] = p.read().strip().decode('utf-8')
|
||||
p.close()
|
||||
cmd = "git --git-dir '{:}/.git' rev-parse --verify HEAD".format(args.git_identity)
|
||||
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE).stdout
|
||||
desc['git_hash'] = p.read().strip().decode('utf-8')
|
||||
p.close()
|
||||
git_dir = os.path.join(args.git_identity, '.git')
|
||||
p = subprocess.run(["git", "--git-dir", git_dir, "describe", "--exclude", "ext/*", "--always", "--tags"],
|
||||
stdout=subprocess.PIPE, text=True)
|
||||
desc['git_identity'] = p.stdout.strip()
|
||||
p = subprocess.run(["git", "--git-dir", git_dir, "rev-parse", "--verify", "HEAD"],
|
||||
stdout=subprocess.PIPE, text=True)
|
||||
desc['git_hash'] = p.stdout.strip()
|
||||
if args.parameter_xml != None:
|
||||
f = open(args.parameter_xml, "rb")
|
||||
bytes = f.read()
|
||||
|
||||
@@ -1,947 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
############################################################################
|
||||
#
|
||||
# Copyright (c) 2012-2024 PX4 Development Team. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# 3. Neither the name PX4 nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
#
|
||||
# Serial firmware uploader for the PX4FMU bootloader
|
||||
#
|
||||
# The PX4 firmware file is a JSON-encoded Python object, containing
|
||||
# metadata fields and a zlib-compressed base64-encoded firmware image.
|
||||
#
|
||||
# The uploader uses the following fields from the firmware file:
|
||||
#
|
||||
# image
|
||||
# The firmware that will be uploaded.
|
||||
# image_size
|
||||
# The size of the firmware in bytes.
|
||||
# board_id
|
||||
# The board for which the firmware is intended.
|
||||
# board_revision
|
||||
# Currently only used for informational purposes.
|
||||
#
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
import binascii
|
||||
import socket
|
||||
import struct
|
||||
import json
|
||||
import zlib
|
||||
import base64
|
||||
import time
|
||||
import array
|
||||
import os
|
||||
|
||||
from sys import platform as _platform
|
||||
|
||||
try:
|
||||
import serial
|
||||
except ImportError as e:
|
||||
print(f"Failed to import serial: {e}")
|
||||
print("")
|
||||
print("You may need to install it using:")
|
||||
print(" python -m pip install pyserial")
|
||||
print("")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# Detect python version
|
||||
if sys.version_info[0] < 3:
|
||||
raise RuntimeError("Python 2 is not supported. Please try again using Python 3.")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# Use monotonic time where available
|
||||
def _time():
|
||||
try:
|
||||
return time.monotonic()
|
||||
except Exception:
|
||||
return time.time()
|
||||
|
||||
class FirmwareNotSuitableException(Exception):
|
||||
def __init__(self, message):
|
||||
super(FirmwareNotSuitableException, self).__init__(message)
|
||||
|
||||
|
||||
class firmware(object):
|
||||
'''Loads a firmware file'''
|
||||
|
||||
desc = {}
|
||||
image = bytes()
|
||||
|
||||
def __init__(self, path):
|
||||
|
||||
# read the file
|
||||
f = open(path, "r")
|
||||
self.desc = json.load(f)
|
||||
f.close()
|
||||
|
||||
self.image = bytearray(zlib.decompress(base64.b64decode(self.desc['image'])))
|
||||
|
||||
# pad image to 4-byte length
|
||||
while ((len(self.image) % 4) != 0):
|
||||
self.image.extend(b'\xff')
|
||||
|
||||
def property(self, propname):
|
||||
return self.desc[propname]
|
||||
|
||||
def crc(self, padlen):
|
||||
state = 0xFFFFFFFF
|
||||
state = zlib.crc32(self.image, state)
|
||||
padding_length = padlen - len(self.image)
|
||||
if padding_length > 0:
|
||||
padding = b'\xff' * padding_length
|
||||
state = zlib.crc32(padding, state)
|
||||
|
||||
return (state ^ 0xFFFFFFFF) & 0xFFFFFFFF
|
||||
|
||||
|
||||
class uploader:
|
||||
'''Uploads a firmware file to the PX4 bootloader'''
|
||||
|
||||
# protocol bytes
|
||||
INSYNC = b'\x12'
|
||||
EOC = b'\x20'
|
||||
|
||||
# reply bytes
|
||||
OK = b'\x10'
|
||||
FAILED = b'\x11'
|
||||
INVALID = b'\x13' # rev3+
|
||||
BAD_SILICON_REV = b'\x14' # rev5+
|
||||
|
||||
# command bytes
|
||||
NOP = b'\x00' # guaranteed to be discarded by the bootloader
|
||||
GET_SYNC = b'\x21'
|
||||
GET_DEVICE = b'\x22'
|
||||
CHIP_ERASE = b'\x23'
|
||||
CHIP_VERIFY = b'\x24' # rev2 only
|
||||
PROG_MULTI = b'\x27'
|
||||
READ_MULTI = b'\x28' # rev2 only
|
||||
GET_CRC = b'\x29' # rev3+
|
||||
GET_OTP = b'\x2a' # rev4+ , get a word from OTP area
|
||||
GET_SN = b'\x2b' # rev4+ , get a word from SN area
|
||||
GET_CHIP = b'\x2c' # rev5+ , get chip version
|
||||
SET_BOOT_DELAY = b'\x2d' # rev5+ , set boot delay
|
||||
GET_CHIP_DES = b'\x2e' # rev5+ , get chip description in ASCII
|
||||
GET_VERSION = b'\x2f' # rev5+ , get bootloader version in ASCII
|
||||
CHIP_FULL_ERASE = b'\x40' # full erase of flash, rev6+
|
||||
MAX_DES_LENGTH = 20
|
||||
|
||||
REBOOT = b'\x30'
|
||||
|
||||
INFO_BL_REV = b'\x01' # bootloader protocol revision
|
||||
BL_REV_MIN = 2 # minimum supported bootloader protocol
|
||||
BL_REV_MAX = 5 # maximum supported bootloader protocol
|
||||
INFO_BOARD_ID = b'\x02' # board type
|
||||
INFO_BOARD_REV = b'\x03' # board revision
|
||||
INFO_FLASH_SIZE = b'\x04' # max firmware size in bytes
|
||||
|
||||
PROG_MULTI_MAX = 252 # protocol max is 255, must be multiple of 4
|
||||
READ_MULTI_MAX = 252 # protocol max is 255
|
||||
|
||||
NSH_INIT = bytearray(b'\x0d\x0d\x0d')
|
||||
NSH_REBOOT_BL = b"reboot -b\n"
|
||||
NSH_REBOOT = b"reboot\n"
|
||||
MAVLINK_REBOOT_ID1 = bytearray(b'\xfe\x21\x72\xff\x00\x4c\x00\x00\x40\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x00\x01\x00\x00\x53\x6b')
|
||||
MAVLINK_REBOOT_ID0 = bytearray(b'\xfe\x21\x45\xff\x00\x4c\x00\x00\x40\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf6\x00\x00\x00\x00\xcc\x37')
|
||||
|
||||
MAX_FLASH_PRGRAM_TIME = 0.001 # Time on an F7 to send SYNC, RESULT from last data in multi RXed
|
||||
|
||||
def __init__(self, portname, baudrate_bootloader, baudrate_flightstack):
|
||||
# Open the port, keep the default timeout short so we can poll quickly.
|
||||
# On some systems writes can suddenly get stuck without having a
|
||||
# write_timeout > 0 set.
|
||||
# chartime 8n1 * bit rate is us
|
||||
self.chartime = 10 * (1.0 / baudrate_bootloader)
|
||||
|
||||
# we use a window approche to SYNC,<result> gathring
|
||||
self.window = 0
|
||||
self.window_max = 256
|
||||
self.window_per = 2 # Sync,<result>
|
||||
self.ackWindowedMode = False # Assume Non Widowed mode for all USB CDC
|
||||
self.port = serial.Serial(portname, baudrate_bootloader, timeout=0.5, write_timeout=0)
|
||||
self.otp = b''
|
||||
self.sn = b''
|
||||
self.baudrate_bootloader = baudrate_bootloader
|
||||
self.baudrate_flightstack = baudrate_flightstack
|
||||
self.baudrate_flightstack_idx = -1
|
||||
self.force_erase = False
|
||||
|
||||
def close(self):
|
||||
if self.port is not None:
|
||||
self.port.close()
|
||||
|
||||
def open(self):
|
||||
# upload timeout
|
||||
timeout = _time() + 0.2
|
||||
|
||||
# attempt to open the port while it exists and until timeout occurs
|
||||
while self.port is not None:
|
||||
portopen = True
|
||||
try:
|
||||
portopen = self.port.is_open
|
||||
except AttributeError:
|
||||
portopen = self.port.isOpen()
|
||||
|
||||
if not portopen and _time() < timeout:
|
||||
try:
|
||||
self.port.open()
|
||||
except OSError:
|
||||
# wait for the port to be ready
|
||||
time.sleep(0.04)
|
||||
except serial.SerialException:
|
||||
# if open fails, try again later
|
||||
time.sleep(0.04)
|
||||
else:
|
||||
break
|
||||
|
||||
# debugging code
|
||||
def __probe(self, state):
|
||||
# self.port.setRTS(state)
|
||||
return
|
||||
|
||||
def __send(self, c):
|
||||
# print("send " + binascii.hexlify(c))
|
||||
self.port.write(c)
|
||||
|
||||
def __recv(self, count=1):
|
||||
c = self.port.read(count)
|
||||
if len(c) < 1:
|
||||
raise RuntimeError("timeout waiting for data (%u bytes)" % count)
|
||||
# print("recv " + binascii.hexlify(c))
|
||||
return c
|
||||
|
||||
def __recv_int(self):
|
||||
raw = self.__recv(4)
|
||||
val = struct.unpack("<I", raw)
|
||||
return val[0]
|
||||
|
||||
def __getSync(self, doFlush=True):
|
||||
if (doFlush):
|
||||
self.port.flush()
|
||||
c = bytes(self.__recv())
|
||||
if c != self.INSYNC:
|
||||
raise RuntimeError("unexpected %s instead of INSYNC" % c)
|
||||
c = self.__recv()
|
||||
if c == self.INVALID:
|
||||
raise RuntimeError("bootloader reports INVALID OPERATION")
|
||||
if c == self.FAILED:
|
||||
raise RuntimeError("bootloader reports OPERATION FAILED")
|
||||
if c != self.OK:
|
||||
raise RuntimeError("unexpected response 0x%x instead of OK" % ord(c))
|
||||
|
||||
# The control flow for receiving Sync is on the order of 16 Ms per Sync
|
||||
# This will validate all the SYNC,<results> for a window of programing
|
||||
# in about 13.81 Ms for 256 blocks written
|
||||
def __ackSyncWindow(self, count):
|
||||
if (count > 0):
|
||||
data = bytearray(bytes(self.__recv(count)))
|
||||
if (len(data) != count):
|
||||
raise RuntimeError("Ack Window %i not %i " % (len(data), count))
|
||||
for i in range(0, len(data), 2):
|
||||
if bytes([data[i]]) != self.INSYNC:
|
||||
raise RuntimeError("unexpected %s instead of INSYNC" % data[i])
|
||||
if bytes([data[i+1]]) == self.INVALID:
|
||||
raise RuntimeError("bootloader reports INVALID OPERATION")
|
||||
if bytes([data[i+1]]) == self.FAILED:
|
||||
raise RuntimeError("bootloader reports OPERATION FAILED")
|
||||
if bytes([data[i+1]]) != self.OK:
|
||||
raise RuntimeError("unexpected response 0x%x instead of OK" % ord(data[i+1]))
|
||||
|
||||
# attempt to get back into sync with the bootloader
|
||||
def __sync(self):
|
||||
# send a stream of ignored bytes longer than the longest possible conversation
|
||||
# that we might still have in progress
|
||||
# self.__send(uploader.NOP * (uploader.PROG_MULTI_MAX + 2))
|
||||
self.port.flushInput()
|
||||
self.__send(uploader.GET_SYNC +
|
||||
uploader.EOC)
|
||||
self.__getSync()
|
||||
|
||||
def __trySync(self):
|
||||
try:
|
||||
self.port.flush()
|
||||
if (self.__recv() != self.INSYNC):
|
||||
# print("unexpected 0x%x instead of INSYNC" % ord(c))
|
||||
return False
|
||||
c = self.__recv()
|
||||
if (c == self.BAD_SILICON_REV):
|
||||
raise NotImplementedError()
|
||||
if (c != self.OK):
|
||||
# print("unexpected 0x%x instead of OK" % ord(c))
|
||||
return False
|
||||
return True
|
||||
|
||||
except NotImplementedError:
|
||||
raise RuntimeError("Programing not supported for this version of silicon!\n"
|
||||
"See https://docs.px4.io/main/en/flight_controller/silicon_errata.html")
|
||||
except RuntimeError:
|
||||
# timeout, no response yet
|
||||
return False
|
||||
|
||||
# attempt to determins if the device is CDCACM or A FTDI
|
||||
def __determineInterface(self):
|
||||
self.port.flushInput()
|
||||
# Set a baudrate that can not work on a real serial port
|
||||
# in that it is 233% off.
|
||||
try:
|
||||
self.port.baudrate = self.baudrate_bootloader * 2.33
|
||||
except NotImplementedError as e:
|
||||
# This error can occur because pySerial on Windows does not support odd baudrates
|
||||
print(f"{e} -> could not check for FTDI device, assuming USB connection")
|
||||
return
|
||||
|
||||
self.__send(uploader.GET_SYNC +
|
||||
uploader.EOC)
|
||||
try:
|
||||
self.__getSync(False)
|
||||
except RuntimeError:
|
||||
# if it fails we are on a real serial port - only leave this enabled on Windows
|
||||
if sys.platform.startswith('win'):
|
||||
self.ackWindowedMode = True
|
||||
finally:
|
||||
try:
|
||||
self.port.baudrate = self.baudrate_bootloader
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# send the GET_DEVICE command and wait for an info parameter
|
||||
def __getInfo(self, param):
|
||||
self.__send(uploader.GET_DEVICE + param + uploader.EOC)
|
||||
value = self.__recv_int()
|
||||
self.__getSync()
|
||||
return value
|
||||
|
||||
# send the GET_OTP command and wait for an info parameter
|
||||
def __getOTP(self, param):
|
||||
t = struct.pack("I", param) # int param as 32bit ( 4 byte ) char array.
|
||||
self.__send(uploader.GET_OTP + t + uploader.EOC)
|
||||
value = self.__recv(4)
|
||||
self.__getSync()
|
||||
return value
|
||||
|
||||
# send the GET_SN command and wait for an info parameter
|
||||
def __getSN(self, param):
|
||||
t = struct.pack("I", param) # int param as 32bit ( 4 byte ) char array.
|
||||
self.__send(uploader.GET_SN + t + uploader.EOC)
|
||||
value = self.__recv(4)
|
||||
self.__getSync()
|
||||
return value
|
||||
|
||||
# send the GET_CHIP command
|
||||
def __getCHIP(self):
|
||||
self.__send(uploader.GET_CHIP + uploader.EOC)
|
||||
value = self.__recv_int()
|
||||
self.__getSync()
|
||||
return value
|
||||
|
||||
# send the GET_CHIP command
|
||||
def __getCHIPDes(self):
|
||||
self.__send(uploader.GET_CHIP_DES + uploader.EOC)
|
||||
length = self.__recv_int()
|
||||
value = self.__recv(length)
|
||||
self.__getSync()
|
||||
pieces = value.split(b",")
|
||||
return pieces
|
||||
|
||||
def __getVersion(self):
|
||||
self.__send(uploader.GET_VERSION + uploader.EOC)
|
||||
try:
|
||||
length = self.__recv_int()
|
||||
value = self.__recv(length)
|
||||
self.__getSync()
|
||||
except RuntimeError:
|
||||
# Bootloader doesn't support version call
|
||||
return "unknown"
|
||||
return value.decode()
|
||||
|
||||
def __drawProgressBar(self, label, progress, maxVal):
|
||||
if maxVal < progress:
|
||||
progress = maxVal
|
||||
|
||||
percent = (float(progress) / float(maxVal)) * 100.0
|
||||
|
||||
redraw = "\r" if sys.stdout.isatty() else "\n"
|
||||
sys.stdout.write("%s%s: [%-20s] %.1f%%" % (redraw, label, '='*int(percent/5.0), percent))
|
||||
sys.stdout.flush()
|
||||
|
||||
# send the CHIP_ERASE command and wait for the bootloader to become ready
|
||||
def __erase(self, label):
|
||||
print(f"Windowed mode: {self.ackWindowedMode}")
|
||||
print("\n", end='')
|
||||
|
||||
if self.force_erase:
|
||||
print("Trying force erase of full chip...\n")
|
||||
self.__send(uploader.CHIP_FULL_ERASE +
|
||||
uploader.EOC)
|
||||
else:
|
||||
self.__send(uploader.CHIP_ERASE +
|
||||
uploader.EOC)
|
||||
|
||||
# erase is very slow, give it 30s
|
||||
deadline = _time() + 30.0
|
||||
while _time() < deadline:
|
||||
|
||||
usualEraseDuration = 15.0
|
||||
estimatedTimeRemaining = deadline-_time()
|
||||
if estimatedTimeRemaining >= usualEraseDuration:
|
||||
self.__drawProgressBar(label, 30.0-estimatedTimeRemaining, usualEraseDuration)
|
||||
else:
|
||||
self.__drawProgressBar(label, 10.0, 10.0)
|
||||
sys.stdout.write(" (timeout: %d seconds) " % int(deadline-_time()))
|
||||
sys.stdout.flush()
|
||||
|
||||
if self.__trySync():
|
||||
self.__drawProgressBar(label, 10.0, 10.0)
|
||||
if self.force_erase:
|
||||
print("\nForce erase done.\n")
|
||||
return
|
||||
|
||||
if self.force_erase:
|
||||
raise RuntimeError("timed out waiting for erase, force erase is likely not supported by bootloader!")
|
||||
else:
|
||||
raise RuntimeError("timed out waiting for erase")
|
||||
|
||||
# send a PROG_MULTI command to write a collection of bytes
|
||||
def __program_multi(self, data, windowMode):
|
||||
|
||||
length = len(data).to_bytes(1, byteorder='big')
|
||||
|
||||
self.__send(uploader.PROG_MULTI)
|
||||
self.__send(length)
|
||||
self.__send(data)
|
||||
self.__send(uploader.EOC)
|
||||
if (not windowMode):
|
||||
self.__getSync(False)
|
||||
else:
|
||||
# The following is done to have minimum delay on the transmission
|
||||
# of the ne fw. The per block cost of __getSync was about 16 mS per.
|
||||
# Passively wait on Sync and Result using board rates and
|
||||
# N.B. attempts to activly wait on InWating still carried 8 mS of overhead
|
||||
self.__probe(False)
|
||||
self.__probe(True)
|
||||
time.sleep((ord(length) * self.chartime) + uploader.MAX_FLASH_PRGRAM_TIME)
|
||||
self.__probe(False)
|
||||
|
||||
# verify multiple bytes in flash
|
||||
def __verify_multi(self, data):
|
||||
|
||||
length = len(data).to_bytes(1, byteorder='big')
|
||||
|
||||
self.__send(uploader.READ_MULTI)
|
||||
self.__send(length)
|
||||
self.__send(uploader.EOC)
|
||||
self.port.flush()
|
||||
programmed = self.__recv(len(data))
|
||||
if programmed != data:
|
||||
print("got " + binascii.hexlify(programmed))
|
||||
print("expect " + binascii.hexlify(data))
|
||||
return False
|
||||
self.__getSync()
|
||||
return True
|
||||
|
||||
# send the reboot command
|
||||
def __reboot(self):
|
||||
self.__send(uploader.REBOOT +
|
||||
uploader.EOC)
|
||||
self.port.flush()
|
||||
|
||||
# v3+ can report failure if the first word flash fails
|
||||
if self.bl_rev >= 3:
|
||||
self.__getSync()
|
||||
|
||||
# split a sequence into a list of size-constrained pieces
|
||||
def __split_len(self, seq, length):
|
||||
return [seq[i:i+length] for i in range(0, len(seq), length)]
|
||||
|
||||
# upload code
|
||||
def __program(self, label, fw):
|
||||
self.__probe(False)
|
||||
print("\n", end='')
|
||||
code = fw.image
|
||||
groups = self.__split_len(code, uploader.PROG_MULTI_MAX)
|
||||
# Give imedate feedback
|
||||
self.__drawProgressBar(label, 0, len(groups))
|
||||
uploadProgress = 0
|
||||
for bytes in groups:
|
||||
self.__program_multi(bytes, self.ackWindowedMode)
|
||||
# If in Window mode, extend the window size for the __ackSyncWindow
|
||||
if self.ackWindowedMode:
|
||||
self.window += self.window_per
|
||||
|
||||
# Print upload progress (throttled, so it does not delay upload progress)
|
||||
uploadProgress += 1
|
||||
if uploadProgress % 256 == 0:
|
||||
self.__probe(True)
|
||||
self.__probe(False)
|
||||
self.__probe(True)
|
||||
self.__ackSyncWindow(self.window)
|
||||
self.__probe(False)
|
||||
self.window = 0
|
||||
self.__drawProgressBar(label, uploadProgress, len(groups))
|
||||
|
||||
# Do any remaining fragment
|
||||
self.__ackSyncWindow(self.window)
|
||||
self.window = 0
|
||||
self.__drawProgressBar(label, 100, 100)
|
||||
|
||||
# verify code
|
||||
def __verify_v2(self, label, fw):
|
||||
print("\n", end='')
|
||||
self.__send(uploader.CHIP_VERIFY +
|
||||
uploader.EOC)
|
||||
self.__getSync()
|
||||
code = fw.image
|
||||
groups = self.__split_len(code, uploader.READ_MULTI_MAX)
|
||||
verifyProgress = 0
|
||||
for bytes in groups:
|
||||
verifyProgress += 1
|
||||
if verifyProgress % 256 == 0:
|
||||
self.__drawProgressBar(label, verifyProgress, len(groups))
|
||||
if (not self.__verify_multi(bytes)):
|
||||
raise RuntimeError("Verification failed")
|
||||
self.__drawProgressBar(label, 100, 100)
|
||||
|
||||
def __verify_v3(self, label, fw):
|
||||
print("\n", end='')
|
||||
self.__drawProgressBar(label, 1, 100)
|
||||
expect_crc = fw.crc(self.fw_maxsize)
|
||||
self.__send(uploader.GET_CRC + uploader.EOC)
|
||||
time.sleep(0.5)
|
||||
report_crc = self.__recv_int()
|
||||
self.__getSync()
|
||||
if report_crc != expect_crc:
|
||||
print("Expected 0x%x" % expect_crc)
|
||||
print("Got 0x%x" % report_crc)
|
||||
raise RuntimeError("Program CRC failed")
|
||||
self.__drawProgressBar(label, 100, 100)
|
||||
|
||||
def __set_boot_delay(self, boot_delay):
|
||||
self.__send(uploader.SET_BOOT_DELAY +
|
||||
struct.pack("b", boot_delay) +
|
||||
uploader.EOC)
|
||||
self.__getSync()
|
||||
|
||||
# get basic data about the board
|
||||
def identify(self):
|
||||
self.__determineInterface()
|
||||
# make sure we are in sync before starting
|
||||
self.__sync()
|
||||
|
||||
# get the bootloader protocol ID first
|
||||
self.bl_rev = self.__getInfo(uploader.INFO_BL_REV)
|
||||
if (self.bl_rev < uploader.BL_REV_MIN) or (self.bl_rev > uploader.BL_REV_MAX):
|
||||
print("Unsupported bootloader protocol %d" % uploader.INFO_BL_REV)
|
||||
raise RuntimeError("Bootloader protocol mismatch")
|
||||
|
||||
self.board_type = self.__getInfo(uploader.INFO_BOARD_ID)
|
||||
self.board_rev = self.__getInfo(uploader.INFO_BOARD_REV)
|
||||
self.fw_maxsize = self.__getInfo(uploader.INFO_FLASH_SIZE)
|
||||
|
||||
self.version = self.__getVersion()
|
||||
|
||||
# upload the firmware
|
||||
def upload(self, fw_list, force=False, boot_delay=None, boot_check=False, force_erase=False):
|
||||
self.force_erase = force_erase
|
||||
# select correct binary
|
||||
found_suitable_firmware = False
|
||||
for file in fw_list:
|
||||
fw = firmware(file)
|
||||
if self.board_type == fw.property('board_id'):
|
||||
if len(fw_list) > 1: print("using firmware binary {}".format(file))
|
||||
found_suitable_firmware = True
|
||||
break
|
||||
|
||||
if not found_suitable_firmware:
|
||||
msg = "Firmware not suitable for this board (Firmware board_type=%u board_id=%u)" % (
|
||||
self.board_type, fw.property('board_id'))
|
||||
print("WARNING: %s" % msg)
|
||||
if force:
|
||||
if len(fw_list) > 1:
|
||||
raise FirmwareNotSuitableException("force flashing failed, more than one file provided, none suitable")
|
||||
print("FORCED WRITE, FLASHING ANYWAY!")
|
||||
else:
|
||||
raise FirmwareNotSuitableException(msg)
|
||||
|
||||
percent = fw.property('image_size') / fw.property('image_maxsize')
|
||||
binary_size = float(fw.property('image_size'))
|
||||
binary_max_size = float(fw.property('image_maxsize'))
|
||||
percent = (binary_size / binary_max_size) * 100
|
||||
|
||||
print("Loaded firmware for board id: %s,%s size: %d bytes (%.2f%%) " % (fw.property('board_id'), fw.property('board_revision'), fw.property('image_size'), percent))
|
||||
print()
|
||||
|
||||
print(f"Bootloader version: {self.version}")
|
||||
|
||||
# Make sure we are doing the right thing
|
||||
start = _time()
|
||||
if self.board_type != fw.property('board_id'):
|
||||
msg = "Firmware not suitable for this board (Firmware board_type=%u board_id=%u)" % (
|
||||
self.board_type, fw.property('board_id'))
|
||||
print("WARNING: %s" % msg)
|
||||
if force:
|
||||
print("FORCED WRITE, FLASHING ANYWAY!")
|
||||
else:
|
||||
raise FirmwareNotSuitableException(msg)
|
||||
|
||||
# Prevent uploads where the image would overflow the flash
|
||||
if self.fw_maxsize < fw.property('image_size'):
|
||||
raise RuntimeError("Firmware image is too large for this board")
|
||||
|
||||
# OTP added in v4:
|
||||
if self.bl_rev >= 4:
|
||||
for byte in range(0, 32*6, 4):
|
||||
x = self.__getOTP(byte)
|
||||
self.otp = self.otp + x
|
||||
# print(binascii.hexlify(x).decode('Latin-1') + ' ', end='')
|
||||
# see src/modules/systemlib/otp.h in px4 code:
|
||||
self.otp_id = self.otp[0:4]
|
||||
self.otp_idtype = self.otp[4:5]
|
||||
self.otp_vid = self.otp[8:4:-1]
|
||||
self.otp_pid = self.otp[12:8:-1]
|
||||
self.otp_coa = self.otp[32:160]
|
||||
# show user:
|
||||
try:
|
||||
print("Sn: ", end='')
|
||||
for byte in range(0, 12, 4):
|
||||
x = self.__getSN(byte)
|
||||
x = x[::-1] # reverse the bytes
|
||||
self.sn = self.sn + x
|
||||
print(binascii.hexlify(x).decode('Latin-1'), end='') # show user
|
||||
print('')
|
||||
print("Chip: %08x" % self.__getCHIP())
|
||||
|
||||
otp_id = self.otp_id.decode('Latin-1')
|
||||
if ("PX4" in otp_id):
|
||||
print("OTP id: " + otp_id)
|
||||
print("OTP idtype: " + binascii.b2a_qp(self.otp_idtype).decode('Latin-1'))
|
||||
print("OTP vid: " + binascii.hexlify(self.otp_vid).decode('Latin-1'))
|
||||
print("OTP pid: " + binascii.hexlify(self.otp_pid).decode('Latin-1'))
|
||||
print("OTP coa: " + binascii.b2a_base64(self.otp_coa).decode('Latin-1'))
|
||||
|
||||
except Exception as e:
|
||||
# ignore bad character encodings
|
||||
print(f"Exception ignored: {e}")
|
||||
pass
|
||||
|
||||
# Silicon errata check was added in v5
|
||||
if (self.bl_rev >= 5):
|
||||
des = self.__getCHIPDes()
|
||||
if (len(des) == 2):
|
||||
family, revision = des
|
||||
print(f"Family: {family.decode()}")
|
||||
print(f"Revision: {revision.decode()}")
|
||||
print(f"Flash: {self.fw_maxsize} bytes")
|
||||
|
||||
# Prevent uploads where the maximum image size of the board config is smaller than the flash
|
||||
# of the board. This is a hint the user chose the wrong config and will lack features
|
||||
# for this particular board.
|
||||
|
||||
# This check should also check if the revision is an unaffected revision
|
||||
# and thus can support the full flash, see
|
||||
# https://github.com/PX4/Firmware/blob/master/src/drivers/boards/common/stm32/board_mcu_version.c#L125-L144
|
||||
|
||||
if self.fw_maxsize > fw.property('image_maxsize') and not force:
|
||||
print(f"WARNING: Board can accept larger flash images ({self.fw_maxsize} bytes) than board config ({fw.property('image_maxsize')} bytes)")
|
||||
else:
|
||||
# If we're still on bootloader v4 on a Pixhawk, we don't know if we
|
||||
# have the silicon errata and therefore need to flash px4_fmu-v2
|
||||
# with 1MB flash or if it supports px4_fmu-v3 with 2MB flash.
|
||||
if fw.property('board_id') == 9 \
|
||||
and fw.property('image_size') > 1032192 \
|
||||
and not force:
|
||||
raise RuntimeError("\nThe Board uses bootloader revision 4 and can therefore not determine\n"
|
||||
"if flashing more than 1 MB (px4_fmu-v3_default) is safe, chances are\n"
|
||||
"high that it is not safe! If unsure, use px4_fmu-v2_default.\n"
|
||||
"\n"
|
||||
"If you know you that the board does not have the silicon errata, use\n"
|
||||
"this script with --force, or update the bootloader. If you are invoking\n"
|
||||
"upload using make, you can use force-upload target to force the upload.\n")
|
||||
self.__erase("Erase ")
|
||||
self.__program("Program", fw)
|
||||
|
||||
if self.bl_rev == 2:
|
||||
self.__verify_v2("Verify ", fw)
|
||||
else:
|
||||
self.__verify_v3("Verify ", fw)
|
||||
|
||||
if boot_delay is not None:
|
||||
self.__set_boot_delay(boot_delay)
|
||||
|
||||
print("\nRebooting.", end='')
|
||||
self.__reboot()
|
||||
self.port.close()
|
||||
print(" Elapsed Time %3.3f\n" % (_time() - start))
|
||||
|
||||
def __next_baud_flightstack(self):
|
||||
if self.baudrate_flightstack_idx + 1 >= len(self.baudrate_flightstack):
|
||||
return False
|
||||
try:
|
||||
self.port.baudrate = self.baudrate_flightstack[self.baudrate_flightstack_idx + 1]
|
||||
self.baudrate_flightstack_idx = self.baudrate_flightstack_idx + 1
|
||||
except serial.SerialException:
|
||||
# Sometimes _configure_port fails
|
||||
time.sleep(0.04)
|
||||
|
||||
return True
|
||||
|
||||
def send_protocol_splitter_format(self, data):
|
||||
# Header Structure:
|
||||
# bits: 1 2 3 4 5 6 7 8
|
||||
# header[0] - | Magic | (='S')
|
||||
# header[1] - |T| LenH | (T - 0: mavlink; 1: rtps)
|
||||
# header[2] - | LenL |
|
||||
# header[3] - | Checksum |
|
||||
|
||||
MAGIC = 83
|
||||
|
||||
len_bytes = len(data).to_bytes(2, "big")
|
||||
LEN_H = len_bytes[0] & 127
|
||||
LEN_L = len_bytes[1] & 255
|
||||
CHECKSUM = MAGIC ^ LEN_H ^ LEN_L
|
||||
|
||||
header_ints = [MAGIC, LEN_H, LEN_L, CHECKSUM]
|
||||
header_bytes = struct.pack("{}B".format(len(header_ints)), *header_ints)
|
||||
|
||||
self.__send(header_bytes)
|
||||
self.__send(data)
|
||||
|
||||
def send_reboot(self, use_protocol_splitter_format=False):
|
||||
if (not self.__next_baud_flightstack()):
|
||||
return False
|
||||
|
||||
print("Attempting reboot on %s with baudrate=%d..." % (self.port.port, self.port.baudrate), file=sys.stderr)
|
||||
if "ttyS" in self.port.port:
|
||||
print("If the board does not respond, check the connection to the Flight Controller")
|
||||
else:
|
||||
print("If the board does not respond, unplug and re-plug the USB connector.", file=sys.stderr)
|
||||
|
||||
try:
|
||||
send_fct = self.__send
|
||||
if use_protocol_splitter_format:
|
||||
send_fct = self.send_protocol_splitter_format
|
||||
|
||||
# try MAVLINK command first
|
||||
self.port.flush()
|
||||
send_fct(uploader.MAVLINK_REBOOT_ID1)
|
||||
send_fct(uploader.MAVLINK_REBOOT_ID0)
|
||||
# then try reboot via NSH
|
||||
send_fct(uploader.NSH_INIT)
|
||||
send_fct(uploader.NSH_REBOOT_BL)
|
||||
send_fct(uploader.NSH_INIT)
|
||||
send_fct(uploader.NSH_REBOOT)
|
||||
self.port.flush()
|
||||
self.port.baudrate = self.baudrate_bootloader
|
||||
except Exception:
|
||||
try:
|
||||
self.port.flush()
|
||||
self.port.baudrate = self.baudrate_bootloader
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
# Parse commandline arguments
|
||||
parser = argparse.ArgumentParser(description="Firmware uploader for the PX autopilot system.")
|
||||
parser.add_argument('--port', action="store", required=True, help="Comma-separated list of serial port(s) to which the FMU may be attached")
|
||||
parser.add_argument('--baud-bootloader', action="store", type=int, default=115200, help="Baud rate of the serial port (default is 115200) when communicating with bootloader, only required for true serial ports.")
|
||||
parser.add_argument('--baud-flightstack', action="store", default="57600", help="Comma-separated list of baud rate of the serial port (default is 57600) when communicating with flight stack (Mavlink or NSH), only required for true serial ports.")
|
||||
parser.add_argument('--force', action='store_true', default=False, help='Override board type check, or silicon errata checks and continue loading')
|
||||
parser.add_argument('--force-erase', action="store_true", help="Do not perform the blank check, always erase every sector of the application space")
|
||||
parser.add_argument('--boot-delay', type=int, default=None, help='minimum boot delay to store in flash')
|
||||
parser.add_argument('--use-protocol-splitter-format', action='store_true', help='use protocol splitter format for reboot')
|
||||
parser.add_argument('firmware', action="store", nargs='+', help="Firmware file(s)")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.use_protocol_splitter_format:
|
||||
print("Using protocol splitter format to reboot pixhawk!")
|
||||
|
||||
# warn people about ModemManager which interferes badly with Pixhawk
|
||||
if os.path.exists("/usr/sbin/ModemManager"):
|
||||
print("==========================================================================================================")
|
||||
print("WARNING: You should uninstall ModemManager as it conflicts with any non-modem serial device (like Pixhawk)")
|
||||
print("==========================================================================================================")
|
||||
|
||||
print("Waiting for bootloader...")
|
||||
# tell any GCS that might be connected to the autopilot to give up
|
||||
# control of the serial port
|
||||
|
||||
# send to localhost and default GCS port
|
||||
ipaddr = '127.0.0.1'
|
||||
portnum = 14550
|
||||
|
||||
# COMMAND_LONG in MAVLink 1
|
||||
heartbeatpacket = bytearray.fromhex('fe097001010000000100020c5103033c8a')
|
||||
commandpacket = bytearray.fromhex('fe210101014c00000000000000000000000000000000000000000000803f00000000f6000000008459')
|
||||
|
||||
# initialize an UDP socket
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
|
||||
# send heartbeat to initialize connection and command to free the link
|
||||
s.sendto(heartbeatpacket, (ipaddr, portnum))
|
||||
s.sendto(commandpacket, (ipaddr, portnum))
|
||||
|
||||
# close the socket
|
||||
s.close()
|
||||
|
||||
# Spin waiting for a device to show up
|
||||
try:
|
||||
while True:
|
||||
portlist = []
|
||||
patterns = args.port.split(",")
|
||||
# on unix-like platforms use glob to support wildcard ports. This allows
|
||||
# the use of /dev/serial/by-id/usb-3D_Robotics on Linux, which prevents the upload from
|
||||
# causing modem hangups etc
|
||||
if "linux" in _platform or "darwin" in _platform or "cygwin" in _platform:
|
||||
import glob
|
||||
for pattern in patterns:
|
||||
portlist += glob.glob(pattern)
|
||||
else:
|
||||
portlist = patterns
|
||||
|
||||
baud_flightstack = [int(x) for x in args.baud_flightstack.split(',')]
|
||||
|
||||
successful = False
|
||||
unsuitable_board = False
|
||||
for port in portlist:
|
||||
|
||||
# print("Trying %s" % port)
|
||||
|
||||
# create an uploader attached to the port
|
||||
try:
|
||||
if "linux" in _platform:
|
||||
# Linux, don't open Mac OS and Win ports
|
||||
if "COM" not in port and "tty.usb" not in port:
|
||||
up = uploader(port, args.baud_bootloader, baud_flightstack)
|
||||
elif "darwin" in _platform:
|
||||
# OS X, don't open Windows and Linux ports
|
||||
if "COM" not in port and "ACM" not in port:
|
||||
up = uploader(port, args.baud_bootloader, baud_flightstack)
|
||||
elif "cygwin" in _platform:
|
||||
# Cygwin, don't open native Windows COM and Linux ports
|
||||
if "COM" not in port and "ACM" not in port:
|
||||
up = uploader(port, args.baud_bootloader, baud_flightstack)
|
||||
elif "win" in _platform:
|
||||
# Windows, don't open POSIX ports
|
||||
if "/" not in port:
|
||||
up = uploader(port, args.baud_bootloader, baud_flightstack)
|
||||
except Exception as e:
|
||||
# open failed, rate-limit our attempts
|
||||
time.sleep(0.05)
|
||||
print(f"Exception ignored: {e}")
|
||||
|
||||
# and loop to the next port
|
||||
continue
|
||||
|
||||
found_bootloader = False
|
||||
while True:
|
||||
up.open()
|
||||
|
||||
# port is open, try talking to it
|
||||
try:
|
||||
# identify the bootloader
|
||||
up.identify()
|
||||
found_bootloader = True
|
||||
print()
|
||||
print(f"Found board id: {up.board_type},{up.board_rev} bootloader protocol revision {up.bl_rev} on {port}")
|
||||
break
|
||||
|
||||
except (RuntimeError, serial.SerialException):
|
||||
|
||||
if not up.send_reboot(args.use_protocol_splitter_format):
|
||||
break
|
||||
|
||||
# wait for the reboot, without we might run into Serial I/O Error 5
|
||||
time.sleep(0.25)
|
||||
|
||||
# always close the port
|
||||
up.close()
|
||||
|
||||
# wait for the close, without we might run into Serial I/O Error 6
|
||||
time.sleep(0.3)
|
||||
|
||||
if not found_bootloader:
|
||||
# Go to the next port
|
||||
continue
|
||||
|
||||
try:
|
||||
# ok, we have a bootloader, try flashing it
|
||||
up.upload(args.firmware, force=args.force, boot_delay=args.boot_delay, force_erase=args.force_erase)
|
||||
|
||||
# if we made this far without raising exceptions, the upload was successful
|
||||
successful = True
|
||||
|
||||
except RuntimeError as e:
|
||||
# print the error
|
||||
print(f"\n\nError: {e}")
|
||||
|
||||
except FirmwareNotSuitableException:
|
||||
unsuitable_board = True
|
||||
up.close()
|
||||
continue
|
||||
|
||||
except IOError:
|
||||
up.close()
|
||||
continue
|
||||
|
||||
finally:
|
||||
# always close the port
|
||||
up.close()
|
||||
|
||||
# we could loop here if we wanted to wait for more boards...
|
||||
if successful:
|
||||
sys.exit(0)
|
||||
else:
|
||||
sys.exit(1)
|
||||
|
||||
if unsuitable_board:
|
||||
# If we land here, we went through all ports, did not flash any
|
||||
# board and found at least one unsuitable board.
|
||||
# Exit with 2, so a caller can distinguish from other errors
|
||||
sys.exit(2)
|
||||
|
||||
# Delay retries to < 20 Hz to prevent spin-lock from hogging the CPU
|
||||
time.sleep(0.05)
|
||||
|
||||
# CTRL+C aborts the upload/spin-lock by interrupt mechanics
|
||||
except KeyboardInterrupt:
|
||||
print("\n Upload aborted by user.")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
|
||||
@@ -144,6 +144,8 @@ def main():
|
||||
help='number of tidy instances to be run in parallel.')
|
||||
parser.add_argument('files', nargs='*', default=['.*'],
|
||||
help='files to be processed (regex on path)')
|
||||
parser.add_argument('-exclude', dest='exclude', default=None,
|
||||
help='regular expression matching files to exclude')
|
||||
parser.add_argument('-fix', action='store_true', help='apply fix-its')
|
||||
parser.add_argument('-format', action='store_true', help='Reformat code '
|
||||
'after applying fixes')
|
||||
@@ -192,6 +194,7 @@ def main():
|
||||
|
||||
# Build up a big regexy filter from all command line arguments.
|
||||
file_name_re = re.compile('(' + ')|('.join(args.files) + ')')
|
||||
exclude_re = re.compile(args.exclude) if args.exclude else None
|
||||
|
||||
try:
|
||||
# Spin up a bunch of tidy-launching threads.
|
||||
@@ -205,6 +208,8 @@ def main():
|
||||
# Fill the queue with files.
|
||||
for name in files:
|
||||
if file_name_re.search(name):
|
||||
if exclude_re and exclude_re.search(name):
|
||||
continue
|
||||
queue.put(name)
|
||||
|
||||
# Wait for all threads to be done.
|
||||
|
||||
@@ -4,7 +4,7 @@ GREEN='\033[0;32m'
|
||||
NO_COLOR='\033[0m' # No Color
|
||||
SCRIPTID="${GREEN}[docker-entrypoint.sh]${NO_COLOR}"
|
||||
|
||||
echo -e "$SCRIPTID Starting"
|
||||
echo -e "$SCRIPTID $( uname -m ) | $(date -u +%FT%TZ)"
|
||||
|
||||
# Start virtual X server in the background
|
||||
# - DISPLAY default is :99, set in dockerfile
|
||||
@@ -22,6 +22,4 @@ if [ -n "${ROS_DISTRO}" ]; then
|
||||
source "/opt/ros/$ROS_DISTRO/setup.bash"
|
||||
fi
|
||||
|
||||
echo -e "$SCRIPTID ($( uname -m ))"
|
||||
|
||||
exec "$@"
|
||||
|
||||
@@ -74,7 +74,7 @@ python3 -m pip install --user -r ${DIR}/requirements.txt
|
||||
|
||||
# Optional, but recommended additional simulation tools:
|
||||
if [[ $INSTALL_SIM == "--sim-tools" ]]; then
|
||||
if brew ls --versions px4-sim > /dev/null; then
|
||||
if ! brew ls --versions px4-sim > /dev/null; then
|
||||
brew install px4-sim
|
||||
elif [[ $REINSTALL_FORMULAS == "--reinstall" ]]; then
|
||||
brew reinstall px4-sim
|
||||
|
||||
+21
-33
@@ -6,9 +6,9 @@ set -e
|
||||
## Can also be used in docker.
|
||||
##
|
||||
## Installs:
|
||||
## - Common dependencies and tools for nuttx, jMAVSim, Gazebo
|
||||
## - Common dependencies and tools for nuttx, Gazebo
|
||||
## - NuttX toolchain (omit with arg: --no-nuttx)
|
||||
## - jMAVSim and Gazebo9 simulator (omit with arg: --no-sim-tools)
|
||||
## - Gazebo Harmonic simulator (omit with arg: --no-sim-tools)
|
||||
##
|
||||
|
||||
INSTALL_NUTTX="true"
|
||||
@@ -176,8 +176,10 @@ if [[ $INSTALL_NUTTX == "true" ]]; then
|
||||
|
||||
echo
|
||||
echo "Fetching Xtensa compilers"
|
||||
wget -q -P $DIR https://github.com/espressif/crosstool-NG/releases/download/esp-13.2.0_20240530/xtensa-esp-elf-13.2.0_20240530-x86_64-linux-gnu.tar.xz
|
||||
sudo tar -xf $DIR/xtensa-esp-elf-13.2.0_20240530-x86_64-linux-gnu.tar.xz -C /opt
|
||||
XTENSA_FILE_NAME=xtensa-esp-elf-13.2.0_20240530-x86_64-linux-gnu.tar.xz
|
||||
wget -q -P $DIR https://github.com/espressif/crosstool-NG/releases/download/esp-13.2.0_20240530/$XTENSA_FILE_NAME
|
||||
sudo tar -xf $DIR/$XTENSA_FILE_NAME -C /opt
|
||||
rm $DIR/$XTENSA_FILE_NAME
|
||||
echo 'export PATH=$PATH:/opt/xtensa-esp-elf/bin/' >> /home/$USER/.bashrc
|
||||
fi
|
||||
|
||||
@@ -194,6 +196,11 @@ if [[ $INSTALL_NUTTX == "true" ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "${UBUNTU_RELEASE}" == "25.10" ]]; then
|
||||
echo "[ubuntu.sh] Gazebo binaries are not available for 25.10, skipping installation"
|
||||
INSTALL_SIM="false"
|
||||
fi
|
||||
|
||||
# Simulation tools
|
||||
if [[ $INSTALL_SIM == "true" ]]; then
|
||||
|
||||
@@ -205,37 +212,18 @@ if [[ $INSTALL_SIM == "true" ]]; then
|
||||
bc \
|
||||
;
|
||||
|
||||
# Gazebo / Gazebo classic installation
|
||||
if [[ "${UBUNTU_RELEASE}" == "18.04" || "${UBUNTU_RELEASE}" == "20.04" ]]; then
|
||||
sudo sh -c 'echo "deb http://packages.osrfoundation.org/gazebo/ubuntu-stable `lsb_release -cs` main" > /etc/apt/sources.list.d/gazebo-stable.list'
|
||||
wget http://packages.osrfoundation.org/gazebo.key -O - | sudo apt-key add -
|
||||
# Update list, since new gazebo-stable.list has been added
|
||||
sudo apt-get update -y --quiet
|
||||
# Gazebo Harmonic installation (Ubuntu 22.04+)
|
||||
echo "[ubuntu.sh] Gazebo (Harmonic) will be installed"
|
||||
# Add Gazebo binary repository
|
||||
sudo wget https://packages.osrfoundation.org/gazebo.gpg -O /usr/share/keyrings/pkgs-osrf-archive-keyring.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/pkgs-osrf-archive-keyring.gpg] http://packages.osrfoundation.org/gazebo/ubuntu-stable $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/gazebo-stable.list > /dev/null
|
||||
sudo apt-get update -y --quiet
|
||||
|
||||
# Install Gazebo classic
|
||||
if [[ "${UBUNTU_RELEASE}" == "18.04" ]]; then
|
||||
gazebo_classic_version=9
|
||||
gazebo_packages="gazebo$gazebo_classic_version libgazebo$gazebo_classic_version-dev"
|
||||
else
|
||||
# default and Ubuntu 20.04
|
||||
gazebo_classic_version=11
|
||||
gazebo_packages="gazebo$gazebo_classic_version libgazebo$gazebo_classic_version-dev"
|
||||
fi
|
||||
else
|
||||
# Expects Ubuntu 22.04 > by default
|
||||
echo "[ubuntu.sh] Gazebo (Harmonic) will be installed"
|
||||
echo "[ubuntu.sh] Earlier versions will be removed"
|
||||
# Add Gazebo binary repository
|
||||
sudo wget https://packages.osrfoundation.org/gazebo.gpg -O /usr/share/keyrings/pkgs-osrf-archive-keyring.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/pkgs-osrf-archive-keyring.gpg] http://packages.osrfoundation.org/gazebo/ubuntu-stable $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/gazebo-stable.list > /dev/null
|
||||
sudo apt-get update -y --quiet
|
||||
# Install Gazebo
|
||||
gazebo_packages="gz-harmonic libunwind-dev"
|
||||
|
||||
# Install Gazebo
|
||||
gazebo_packages="gz-harmonic libunwind-dev"
|
||||
|
||||
if [[ "${UBUNTU_RELEASE}" == "24.04" ]]; then
|
||||
gazebo_packages="$gazebo_packages cppzmq-dev"
|
||||
fi
|
||||
if [[ "${UBUNTU_RELEASE}" == "24.04" ]]; then
|
||||
gazebo_packages="$gazebo_packages cppzmq-dev"
|
||||
fi
|
||||
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get -y --quiet --no-install-recommends install \
|
||||
|
||||
Submodule Tools/simulation/gazebo-classic/sitl_gazebo-classic updated: ac718b6fb2...5b6966ed57
+1
-1
Submodule Tools/simulation/gz updated: fe3fe236e3...3eb05f716a
Submodule Tools/simulation/jmavsim/jMAVSim updated: 66b764ada5...665b26d62d
@@ -221,8 +221,10 @@
|
||||
/* HEATER
|
||||
* PWM in future
|
||||
*/
|
||||
#define GPIO_HEATER_OUTPUT /* PB10 T2CH3 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTB|GPIO_PIN10)
|
||||
#define HEATER_OUTPUT_EN(on_true) px4_arch_gpiowrite(GPIO_HEATER_OUTPUT, (on_true))
|
||||
#define GPIO_HEATER_OUTPUT
|
||||
#define HEATER_NUM 1
|
||||
#define GPIO_HEATER1_OUTPUT /* PB10 T2CH3 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTB|GPIO_PIN10)
|
||||
#define HEATER1_OUTPUT_EN(on_true) px4_arch_gpiowrite(GPIO_HEATER1_OUTPUT, (on_true))
|
||||
|
||||
/* PE6 is nARMED
|
||||
* The GPIO will be set as input while not armed HW will have external HW Pull UP.
|
||||
@@ -442,7 +444,7 @@
|
||||
GPIO_CAN1_RX, \
|
||||
GPIO_CAN2_TX, \
|
||||
GPIO_CAN2_RX, \
|
||||
GPIO_HEATER_OUTPUT, \
|
||||
GPIO_HEATER1_OUTPUT, \
|
||||
GPIO_nPOWER_IN_A, \
|
||||
GPIO_nPOWER_IN_B, \
|
||||
GPIO_nPOWER_IN_C, \
|
||||
|
||||
@@ -31,6 +31,7 @@ CONFIG_UAVCANNODE_ESC_RAW_COMMAND=y
|
||||
CONFIG_UAVCANNODE_ESC_STATUS=y
|
||||
CONFIG_UAVCANNODE_FLOW_MEASUREMENT=y
|
||||
CONFIG_UAVCANNODE_GNSS_FIX=y
|
||||
CONFIG_UAVCANNODE_HARDPOINT_COMMAND=y
|
||||
CONFIG_UAVCANNODE_HYGROMETER_MEASUREMENT=y
|
||||
CONFIG_UAVCANNODE_LIGHTS_COMMAND=y
|
||||
CONFIG_UAVCANNODE_MAGNETIC_FIELD_STRENGTH=y
|
||||
|
||||
@@ -28,12 +28,8 @@ CONFIG_DRIVERS_IMU_INVENSENSE_IIM42653=y
|
||||
CONFIG_DRIVERS_IMU_MURATA_SCH16T=y
|
||||
CONFIG_COMMON_LIGHT=y
|
||||
CONFIG_DRIVERS_MAGNETOMETER_BOSCH_BMM150=y
|
||||
CONFIG_DRIVERS_MAGNETOMETER_HMC5883=y
|
||||
CONFIG_DRIVERS_MAGNETOMETER_QMC5883L=y
|
||||
CONFIG_DRIVERS_MAGNETOMETER_ISENTEK_IST8308=y
|
||||
CONFIG_DRIVERS_MAGNETOMETER_ISENTEK_IST8310=y
|
||||
CONFIG_DRIVERS_MAGNETOMETER_LIS3MDL=y
|
||||
CONFIG_DRIVERS_MAGNETOMETER_LSM303AGR=y
|
||||
CONFIG_DRIVERS_MAGNETOMETER_RM3100=y
|
||||
CONFIG_DRIVERS_MAGNETOMETER_ST_IIS2MDC=y
|
||||
CONFIG_DRIVERS_POWER_MONITOR_INA226=y
|
||||
|
||||
@@ -17,21 +17,21 @@ param set-default MAV_2_UDP_PRT 14550
|
||||
param set-default SENS_EN_INA226 1
|
||||
param set-default SENS_EN_THERMAL 1
|
||||
param set-default SENS_IMU_MODE 1
|
||||
param set-default SENS_IMU_TEMP 10.0
|
||||
#param set-default SENS_IMU_TEMP_FF 0.0
|
||||
#param set-default SENS_IMU_TEMP_I 0.025
|
||||
#param set-default SENS_IMU_TEMP_P 1.0
|
||||
param set-default HEATER1_TEMP 10.0
|
||||
#param set-default HEATER1_TEMP_FF 0.0
|
||||
#param set-default HEATER1_TEMP_I 0.025
|
||||
#param set-default HEATER1_TEMP_P 1.0
|
||||
|
||||
param set-default UAVCAN_ESC_IFACE 2
|
||||
|
||||
if ver hwtypecmp ARKV6X000
|
||||
then
|
||||
param set-default SENS_TEMP_ID 2818058
|
||||
param set-default HEATER1_IMU_ID 2818058
|
||||
fi
|
||||
|
||||
if ver hwtypecmp ARKV6X001
|
||||
then
|
||||
param set-default SENS_TEMP_ID 3014666
|
||||
param set-default HEATER1_IMU_ID 3014666
|
||||
fi
|
||||
|
||||
safety_button start
|
||||
|
||||
@@ -100,7 +100,7 @@ bmp388 -I start
|
||||
# Start an external PWM generator
|
||||
if param greater PCA9685_EN_BUS 0
|
||||
then
|
||||
pca9685_pwm_out start
|
||||
pca9685_pwm_out start -X
|
||||
fi
|
||||
|
||||
unset HAVE_PM2
|
||||
|
||||
@@ -224,8 +224,10 @@
|
||||
/* HEATER
|
||||
* PWM in future
|
||||
*/
|
||||
#define GPIO_HEATER_OUTPUT /* PB10 T2CH3 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTB|GPIO_PIN10)
|
||||
#define HEATER_OUTPUT_EN(on_true) px4_arch_gpiowrite(GPIO_HEATER_OUTPUT, (on_true))
|
||||
#define GPIO_HEATER_OUTPUT
|
||||
#define HEATER_NUM 1
|
||||
#define GPIO_HEATER1_OUTPUT /* PB10 T2CH3 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTB|GPIO_PIN10)
|
||||
#define HEATER1_OUTPUT_EN(on_true) px4_arch_gpiowrite(GPIO_HEATER1_OUTPUT, (on_true))
|
||||
|
||||
/* PE6 is nARMED
|
||||
* The GPIO will be set as input while not armed HW will have external HW Pull UP.
|
||||
@@ -445,7 +447,7 @@
|
||||
GPIO_CAN1_RX, \
|
||||
GPIO_CAN2_TX, \
|
||||
GPIO_CAN2_RX, \
|
||||
GPIO_HEATER_OUTPUT, \
|
||||
GPIO_HEATER1_OUTPUT, \
|
||||
GPIO_nPOWER_IN_A, \
|
||||
GPIO_nPOWER_IN_B, \
|
||||
GPIO_nPOWER_IN_C, \
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# CONFIG_BOARD_UAVCAN_TIMER_OVERRIDE is not set
|
||||
CONFIG_BOARD_CONSTRAINED_FLASH=y
|
||||
CONFIG_BOARD_CONSTRAINED_MEMORY=y
|
||||
CONFIG_MODULES_UXRCE_DDS_CLIENT=n
|
||||
CONFIG_MODULES_ZENOH=y
|
||||
@@ -15,17 +15,19 @@ fi
|
||||
|
||||
# TODO: Tune the following parameters
|
||||
param set-default SENS_EN_THERMAL 1
|
||||
param set-default SENS_IMU_TEMP 10.0
|
||||
#param set-default SENS_IMU_TEMP_FF 0.0
|
||||
#param set-default SENS_IMU_TEMP_I 0.025
|
||||
#param set-default SENS_IMU_TEMP_P 1.0
|
||||
param set-default HEATER1_TEMP 10.0
|
||||
#param set-default HEATER1_TEMP_FF 0.0
|
||||
#param set-default HEATER1_TEMP_I 0.025
|
||||
#param set-default HEATER1_TEMP_P 1.0
|
||||
|
||||
if ver hwtypecmp ARKFPV000
|
||||
then
|
||||
param set-default SENS_TEMP_ID 3014666
|
||||
param set-default HEATER1_IMU_ID 3014666
|
||||
fi
|
||||
|
||||
param set-default BAT1_V_DIV 21.0
|
||||
param set-default BAT1_V_FILT 0.075
|
||||
param set-default BAT1_I_FILT 0.5
|
||||
|
||||
param set-default RC_CRSF_PRT_CFG 300
|
||||
param set-default RC_SBUS_PRT_CFG 0
|
||||
|
||||
@@ -20,5 +20,5 @@ bmp388 -I -b 2 start
|
||||
# Start an external PWM generator
|
||||
if param greater PCA9685_EN_BUS 0
|
||||
then
|
||||
pca9685_pwm_out start
|
||||
pca9685_pwm_out start -X
|
||||
fi
|
||||
|
||||
@@ -176,9 +176,6 @@
|
||||
|
||||
#define BOARD_BATTERY1_V_DIV (21.0f) // (20k + 1k) / 1k = 21
|
||||
|
||||
#define BOARD_BATTERY_ADC_VOLTAGE_FILTER_S 0.075f
|
||||
#define BOARD_BATTERY_ADC_CURRENT_FILTER_S 0.125f
|
||||
|
||||
#define ADC_SCALED_PAYLOAD_SENSE ADC_SCALED_12V_CHANNEL
|
||||
|
||||
/* HW has to large of R termination on ADC todo:change when HW value is chosen */
|
||||
@@ -208,8 +205,10 @@
|
||||
/* HEATER
|
||||
* PWM in future
|
||||
*/
|
||||
#define GPIO_HEATER_OUTPUT /* PB10 T2CH3 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTB|GPIO_PIN10)
|
||||
#define HEATER_OUTPUT_EN(on_true) px4_arch_gpiowrite(GPIO_HEATER_OUTPUT, (on_true))
|
||||
#define GPIO_HEATER_OUTPUT
|
||||
#define HEATER_NUM 1
|
||||
#define GPIO_HEATER1_OUTPUT /* PB10 T2CH3 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTB|GPIO_PIN10)
|
||||
#define HEATER1_OUTPUT_EN(on_true) px4_arch_gpiowrite(GPIO_HEATER1_OUTPUT, (on_true))
|
||||
|
||||
/* PE6 is nARMED
|
||||
* The GPIO will be set as input while not armed HW will have external HW Pull UP.
|
||||
@@ -331,7 +330,7 @@
|
||||
GPIO_HW_VER_REV_DRIVE, \
|
||||
GPIO_CAN1_TX, \
|
||||
GPIO_CAN1_RX, \
|
||||
GPIO_HEATER_OUTPUT, \
|
||||
GPIO_HEATER1_OUTPUT, \
|
||||
GPIO_VDD_5V_PGOOD, \
|
||||
GPIO_VDD_12V_PGOOD, \
|
||||
GPIO_VDD_12V_EN, \
|
||||
|
||||
@@ -21,17 +21,17 @@ param set-default SENS_EN_INA226 1
|
||||
|
||||
# TODO: Tune the following parameters
|
||||
param set-default SENS_EN_THERMAL 1
|
||||
param set-default SENS_IMU_TEMP 10.0
|
||||
#param set-default SENS_IMU_TEMP_FF 0.0
|
||||
#param set-default SENS_IMU_TEMP_I 0.025
|
||||
#param set-default SENS_IMU_TEMP_P 1.0
|
||||
param set-default HEATER1_TEMP 10.0
|
||||
#param set-default HEATER1_TEMP_FF 0.0
|
||||
#param set-default HEATER1_TEMP_I 0.025
|
||||
#param set-default HEATER1_TEMP_P 1.0
|
||||
|
||||
param set-default UAVCAN_ESC_IFACE 1
|
||||
|
||||
if ver hwtypecmp ARKPI6X000
|
||||
then
|
||||
# TODO: Add the correct sensor ID
|
||||
param set-default SENS_TEMP_ID 2490378
|
||||
param set-default HEATER1_IMU_ID 2490378
|
||||
fi
|
||||
|
||||
param set-default EKF2_MULTI_IMU 0
|
||||
|
||||
@@ -38,5 +38,5 @@ afbrs50 start
|
||||
# Start an external PWM generator
|
||||
if param greater PCA9685_EN_BUS 0
|
||||
then
|
||||
pca9685_pwm_out start
|
||||
pca9685_pwm_out start -X
|
||||
fi
|
||||
|
||||
@@ -188,8 +188,10 @@
|
||||
/* HEATER
|
||||
* PWM in future
|
||||
*/
|
||||
#define GPIO_HEATER_OUTPUT /* PB10 T2CH3 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTB|GPIO_PIN10)
|
||||
#define HEATER_OUTPUT_EN(on_true) px4_arch_gpiowrite(GPIO_HEATER_OUTPUT, (on_true))
|
||||
#define GPIO_HEATER_OUTPUT
|
||||
#define HEATER_NUM 1
|
||||
#define GPIO_HEATER1_OUTPUT /* PB10 T2CH3 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTB|GPIO_PIN10)
|
||||
#define HEATER1_OUTPUT_EN(on_true) px4_arch_gpiowrite(GPIO_HEATER1_OUTPUT, (on_true))
|
||||
|
||||
/* PE6 is nARMED
|
||||
* The GPIO will be set as input while not armed HW will have external HW Pull UP.
|
||||
@@ -333,7 +335,7 @@
|
||||
GPIO_HW_VER_REV_DRIVE, \
|
||||
GPIO_CAN1_TX, \
|
||||
GPIO_CAN1_RX, \
|
||||
GPIO_HEATER_OUTPUT, \
|
||||
GPIO_HEATER1_OUTPUT, \
|
||||
GPIO_VDD_5V_HIPOWER_nEN, \
|
||||
GPIO_VDD_5V_HIPOWER_nOC, \
|
||||
GPIO_VDD_3V3_SD_CARD_EN, \
|
||||
|
||||
@@ -121,7 +121,9 @@
|
||||
#define BOARD_REAR_LED_MASK (1 << 1) | (1 << 2)
|
||||
|
||||
/* HEATER */
|
||||
#define GPIO_HEATER_OUTPUT /* PA7 T14CH1 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTA|GPIO_PIN7)
|
||||
#define GPIO_HEATER_OUTPUT
|
||||
#define HEATER_NUM 1
|
||||
#define GPIO_HEATER1_OUTPUT /* PA7 T14CH1 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTA|GPIO_PIN7)
|
||||
|
||||
#define BOARD_HAS_LED_PWM 1
|
||||
#define BOARD_LED_PWM_DRIVE_ACTIVE_LOW 1
|
||||
@@ -182,7 +184,7 @@
|
||||
PX4_ADC_GPIO, \
|
||||
GPIO_HW_REV_DRIVE, \
|
||||
GPIO_HW_VER_DRIVE, \
|
||||
GPIO_HEATER_OUTPUT, \
|
||||
GPIO_HEATER1_OUTPUT, \
|
||||
GPIO_VDD_3V3_SD_CARD_EN, \
|
||||
GPIO_OTGFS_VBUS \
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ CONFIG_DRIVERS_DSHOT=y
|
||||
CONFIG_DRIVERS_GNSS_SEPTENTRIO=y
|
||||
CONFIG_DRIVERS_GPS=y
|
||||
CONFIG_DRIVERS_PPS_CAPTURE=y
|
||||
CONFIG_DRIVERS_VTX=y
|
||||
CONFIG_VTX_CRSF_MSP_SUPPORT=y
|
||||
CONFIG_DRIVERS_IMU_BOSCH_BMI088=y
|
||||
CONFIG_BMI088_ACCELEROMETER_INT2=y
|
||||
CONFIG_COMMON_LIGHT=y
|
||||
@@ -53,6 +55,7 @@ CONFIG_MODULES_HARDFAULT_STREAM=y
|
||||
CONFIG_MODULES_LAND_DETECTOR=y
|
||||
CONFIG_MODULES_LANDING_TARGET_ESTIMATOR=y
|
||||
CONFIG_MODULES_LOAD_MON=y
|
||||
CONFIG_MODULES_TASK_WATCHDOG=y
|
||||
CONFIG_MODULES_LOGGER=y
|
||||
CONFIG_LOGGER_STACK_SIZE=4100
|
||||
CONFIG_MODULES_MAG_BIAS_ESTIMATOR=y
|
||||
@@ -92,3 +95,4 @@ CONFIG_SYSTEMCMDS_TOPIC_LISTENER=y
|
||||
CONFIG_SYSTEMCMDS_UORB=y
|
||||
CONFIG_SYSTEMCMDS_VER=y
|
||||
CONFIG_SYSTEMCMDS_WORK_QUEUE=y
|
||||
CONFIG_WQ_TTY_STACKSIZE=2500
|
||||
|
||||
@@ -34,5 +34,8 @@ nshterm /dev/ttyS3 &
|
||||
# Start the time_persistor to cyclically store the RTC in FRAM
|
||||
time_persistor start
|
||||
|
||||
# Start the task_watchdog as we do not have the logger watchdog
|
||||
task_watchdog start
|
||||
|
||||
# Start the ESC telemetry
|
||||
dshot telemetry -d /dev/ttyS5 -x
|
||||
|
||||
@@ -77,5 +77,5 @@ ist8310 -X -b 1 -R 10 start
|
||||
# Start an external PWM generator
|
||||
if param greater PCA9685_EN_BUS 0
|
||||
then
|
||||
pca9685_pwm_out start
|
||||
pca9685_pwm_out start -X
|
||||
fi
|
||||
|
||||
@@ -160,7 +160,7 @@ CONFIG_NSH_BUILTIN_APPS=y
|
||||
CONFIG_NSH_CMDPARMS=y
|
||||
CONFIG_NSH_CROMFSETC=y
|
||||
CONFIG_NSH_LINELEN=128
|
||||
CONFIG_NSH_MAXARGUMENTS=15
|
||||
CONFIG_NSH_MAXARGUMENTS=24
|
||||
CONFIG_NSH_NESTDEPTH=8
|
||||
CONFIG_NSH_QUOTE=y
|
||||
CONFIG_NSH_ROMFSETC=y
|
||||
|
||||
@@ -224,8 +224,10 @@
|
||||
/* HEATER
|
||||
* PWM in future
|
||||
*/
|
||||
#define GPIO_HEATER_OUTPUT /* PB10 T2CH3 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTB|GPIO_PIN10)
|
||||
#define HEATER_OUTPUT_EN(on_true) px4_arch_gpiowrite(GPIO_HEATER_OUTPUT, (on_true))
|
||||
#define GPIO_HEATER_OUTPUT
|
||||
#define HEATER_NUM 1
|
||||
#define GPIO_HEATER1_OUTPUT /* PB10 T2CH3 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTB|GPIO_PIN10)
|
||||
#define HEATER1_OUTPUT_EN(on_true) px4_arch_gpiowrite(GPIO_HEATER1_OUTPUT, (on_true))
|
||||
|
||||
/* PE6 is nARMED
|
||||
* The GPIO will be set as input while not armed HW will have external HW Pull UP.
|
||||
@@ -443,7 +445,7 @@
|
||||
GPIO_CAN1_RX, \
|
||||
GPIO_CAN2_TX, \
|
||||
GPIO_CAN2_RX, \
|
||||
GPIO_HEATER_OUTPUT, \
|
||||
GPIO_HEATER1_OUTPUT, \
|
||||
GPIO_nPOWER_IN_A, \
|
||||
GPIO_nPOWER_IN_B, \
|
||||
GPIO_nPOWER_IN_C, \
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
# CONFIG_BOARD_UAVCAN_TIMER_OVERRIDE is not set
|
||||
CONFIG_DRIVERS_UAVCAN=n
|
||||
CONFIG_MODULES_UXRCE_DDS_CLIENT=n
|
||||
CONFIG_SYSTEMCMDS_SD_BENCH=n
|
||||
CONFIG_SYSTEMCMDS_I2CDETECT=n
|
||||
CONFIG_MODULES_ZENOH=y
|
||||
|
||||
@@ -28,7 +28,7 @@ then
|
||||
echo "ads1115 not found."
|
||||
fi
|
||||
|
||||
if ! pca9685_pwm_out start
|
||||
if ! pca9685_pwm_out start -X
|
||||
then
|
||||
echo "pca9685_pwm_out not found."
|
||||
fi
|
||||
|
||||
@@ -66,15 +66,15 @@ then
|
||||
fi
|
||||
fi
|
||||
|
||||
iim42652 -R 6 -s -C 32768 start
|
||||
bmi088 -A -R 4 -s start
|
||||
bmi088 -G -R 4 -s start
|
||||
iim42652 -R 6 -s -C 32768 start
|
||||
icm45686 -R 2 -s start
|
||||
|
||||
rm3100 -I -b 4 start
|
||||
|
||||
icp201xx -I -a 0x64 start
|
||||
bmp581 -b 2 -X -a 0x47 start
|
||||
icp201xx -I -a 0x64 start
|
||||
|
||||
# External compass on GPS1/I2C1 (the 3rd external bus): standard Holybro Pixhawk 4 or CUAV V5 GPS/compass puck (with lights, safety button, and buzzer)
|
||||
ist8310 -X -b 1 -R 10 start
|
||||
|
||||
@@ -230,8 +230,10 @@
|
||||
/* HEATER
|
||||
* PWM in future
|
||||
*/
|
||||
#define GPIO_HEATER_OUTPUT /* PB10 T2CH3 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTB|GPIO_PIN10)
|
||||
#define HEATER_OUTPUT_EN(on_true) px4_arch_gpiowrite(GPIO_HEATER_OUTPUT, (on_true))
|
||||
#define GPIO_HEATER_OUTPUT
|
||||
#define HEATER_NUM 1
|
||||
#define GPIO_HEATER1_OUTPUT /* PB10 T2CH3 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTB|GPIO_PIN10)
|
||||
#define HEATER1_OUTPUT_EN(on_true) px4_arch_gpiowrite(GPIO_HEATER1_OUTPUT, (on_true))
|
||||
|
||||
/* PE6 is nARMED
|
||||
* The GPIO will be set as input while not armed HW will have external HW Pull UP.
|
||||
@@ -453,7 +455,7 @@
|
||||
GPIO_CAN1_RX, \
|
||||
GPIO_CAN2_TX, \
|
||||
GPIO_CAN2_RX, \
|
||||
GPIO_HEATER_OUTPUT, \
|
||||
GPIO_HEATER1_OUTPUT, \
|
||||
GPIO_nPOWER_IN_A, \
|
||||
GPIO_nPOWER_IN_B, \
|
||||
GPIO_nPOWER_IN_C, \
|
||||
|
||||
@@ -104,8 +104,10 @@
|
||||
#define GPIO_CAN2_SILENT_S1 /* PH3 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTH|GPIO_PIN3)
|
||||
|
||||
/* HEATER */
|
||||
#define GPIO_HEATER_OUTPUT /* PA8 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTA|GPIO_PIN8)
|
||||
#define HEATER_OUTPUT_EN(on_true) px4_arch_gpiowrite(GPIO_HEATER_OUTPUT, (on_true))
|
||||
#define GPIO_HEATER_OUTPUT
|
||||
#define HEATER_NUM 1
|
||||
#define GPIO_HEATER1_OUTPUT /* PA8 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTA|GPIO_PIN8)
|
||||
#define HEATER1_OUTPUT_EN(on_true) px4_arch_gpiowrite(GPIO_HEATER1_OUTPUT, (on_true))
|
||||
|
||||
/* PWM */
|
||||
#define DIRECT_PWM_OUTPUT_CHANNELS 14
|
||||
@@ -127,8 +129,8 @@
|
||||
#define GPIO_VDD_5V_RC_EN /* PG5 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTG|GPIO_PIN5)
|
||||
#define GPIO_VDD_3V3_SD_CARD_EN /* PG7 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTG|GPIO_PIN7)
|
||||
|
||||
#define GPIO_VDD_5V_HIPOWER_OC /* PJ3 */ (GPIO_INPUT|GPIO_FLOAT|GPIO_PORTJ|GPIO_PIN3)
|
||||
#define GPIO_nVDD_5V_PERIPH_OC /* PJ4 */ (GPIO_INPUT|GPIO_FLOAT|GPIO_PORTJ|GPIO_PIN4)
|
||||
#define GPIO_VDD_5V_HIPOWER_OC /* PJ3 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTJ|GPIO_PIN3)
|
||||
#define GPIO_nVDD_5V_PERIPH_OC /* PJ4 */ (GPIO_INPUT|GPIO_PULLUP|GPIO_PORTJ|GPIO_PIN4)
|
||||
|
||||
/* Power switch controls ******************************************************/
|
||||
#define VDD_5V_PERIPH_EN(on_true) px4_arch_gpiowrite(GPIO_nVDD_5V_PERIPH_EN, (on_true))
|
||||
@@ -212,7 +214,7 @@
|
||||
GPIO_CAN2_RX, \
|
||||
GPIO_CAN1_SILENT_S0, \
|
||||
GPIO_CAN2_SILENT_S1, \
|
||||
GPIO_HEATER_OUTPUT, \
|
||||
GPIO_HEATER1_OUTPUT, \
|
||||
GPIO_nPOWER_IN_CAN, \
|
||||
GPIO_nPOWER_IN_ADC, \
|
||||
GPIO_nPOWER_IN_C, \
|
||||
|
||||
@@ -31,5 +31,4 @@
|
||||
#
|
||||
############################################################################
|
||||
|
||||
add_subdirectory(core_heater)
|
||||
add_subdirectory(pwm_voltage)
|
||||
|
||||
@@ -1,261 +0,0 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Copyright (c) 2025 PX4 Development Team. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name PX4 nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file core_heater.cpp
|
||||
*
|
||||
*/
|
||||
|
||||
#include "core_heater.h"
|
||||
|
||||
#include <px4_platform_common/getopt.h>
|
||||
#include <px4_platform_common/log.h>
|
||||
#include <drivers/drv_hrt.h>
|
||||
#include <drivers/drv_io_heater.h>
|
||||
|
||||
# ifndef GPIO_CORE_HEATER_OUTPUT
|
||||
# error "To use the heater driver, the board_config.h must define and initialize GPIO_CORE_HEATER_OUTPUT"
|
||||
# endif
|
||||
|
||||
Core_Heater::Core_Heater() :
|
||||
ModuleParams(nullptr),
|
||||
ScheduledWorkItem(MODULE_NAME, px4::wq_configurations::lp_default)
|
||||
{
|
||||
_heater_status_pub.advertise();
|
||||
}
|
||||
|
||||
Core_Heater::~Core_Heater()
|
||||
{
|
||||
disable_core_heater();
|
||||
}
|
||||
|
||||
int Core_Heater::custom_command(int argc, char *argv[])
|
||||
{
|
||||
// Check if the driver is running.
|
||||
if (!is_running()) {
|
||||
PX4_INFO("not running");
|
||||
return PX4_ERROR;
|
||||
}
|
||||
|
||||
return print_usage("Unrecognized command.");
|
||||
}
|
||||
|
||||
void Core_Heater::disable_core_heater()
|
||||
{
|
||||
// Reset heater to off state.
|
||||
px4_arch_unconfiggpio(GPIO_CORE_HEATER_OUTPUT);
|
||||
}
|
||||
|
||||
void Core_Heater::initialize_core_heater_io()
|
||||
{
|
||||
// Initialize heater to off state.
|
||||
px4_arch_configgpio(GPIO_CORE_HEATER_OUTPUT);
|
||||
}
|
||||
|
||||
void Core_Heater::core_heater_off()
|
||||
{
|
||||
CORE_HEATER_OUTPUT_EN(false);
|
||||
}
|
||||
|
||||
void Core_Heater::core_heater_on()
|
||||
{
|
||||
CORE_HEATER_OUTPUT_EN(true);
|
||||
}
|
||||
|
||||
bool Core_Heater::initialize_topics()
|
||||
{
|
||||
for (uint8_t i = 0; i < ORB_MULTI_MAX_INSTANCES; i++) {
|
||||
uORB::SubscriptionData<sensor_accel_s> sensor_accel_sub{ORB_ID(sensor_accel), i};
|
||||
|
||||
if (sensor_accel_sub.get().timestamp != 0 &&
|
||||
sensor_accel_sub.get().device_id != 0 &&
|
||||
PX4_ISFINITE(sensor_accel_sub.get().temperature)) {
|
||||
|
||||
// If the correct ID is found, exit the for-loop with _sensor_accel_sub pointing to the correct instance.
|
||||
if (sensor_accel_sub.get().device_id == (uint32_t)_param_core_temp_id.get()) {
|
||||
_sensor_accel_sub.ChangeInstance(i);
|
||||
_sensor_device_id = sensor_accel_sub.get().device_id;
|
||||
initialize_core_heater_io();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Core_Heater::Run()
|
||||
{
|
||||
if (should_exit()) {
|
||||
exit_and_cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
update_params();
|
||||
|
||||
if (_sensor_device_id == 0) {
|
||||
if (!initialize_topics()) {
|
||||
// if sensor still not found try again in 1 second
|
||||
ScheduleDelayed(1_s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sensor_accel_s sensor_accel;
|
||||
float temperature_delta {0.f};
|
||||
|
||||
if (_core_heater_on) {
|
||||
// Turn the heater off.
|
||||
_core_heater_on = false;
|
||||
core_heater_off();
|
||||
ScheduleDelayed(_controller_period_usec - _controller_time_on_usec);
|
||||
|
||||
} else if (_sensor_accel_sub.update(&sensor_accel)) {
|
||||
// Update the current IMU sensor temperature if valid.
|
||||
if (PX4_ISFINITE(sensor_accel.temperature)) {
|
||||
temperature_delta = _param_core_imu_temp.get() - sensor_accel.temperature;
|
||||
_temperature_last = sensor_accel.temperature;
|
||||
}
|
||||
|
||||
_proportional_value = temperature_delta * _param_core_imu_temp_p.get();
|
||||
_integrator_value += temperature_delta * _param_core_imu_temp_i.get();
|
||||
|
||||
_integrator_value = math::constrain(_integrator_value, -0.25f, 0.25f);
|
||||
|
||||
_controller_time_on_usec = static_cast<int>((_param_core_imu_temp_ff.get() + _proportional_value +
|
||||
_integrator_value) * static_cast<float>(_controller_period_usec));
|
||||
|
||||
_controller_time_on_usec = math::constrain(_controller_time_on_usec, 0, _controller_period_usec);
|
||||
|
||||
if (fabsf(temperature_delta) < TEMPERATURE_TARGET_THRESHOLD) {
|
||||
_temperature_target_met = true;
|
||||
|
||||
} else {
|
||||
|
||||
_temperature_target_met = false;
|
||||
}
|
||||
|
||||
_core_heater_on = true;
|
||||
core_heater_on();
|
||||
ScheduleDelayed(_controller_time_on_usec);
|
||||
}
|
||||
|
||||
publish_status();
|
||||
}
|
||||
|
||||
void Core_Heater::publish_status()
|
||||
{
|
||||
heater_status_s status{};
|
||||
status.device_id = _sensor_device_id;
|
||||
status.heater_on = _core_heater_on;
|
||||
status.temperature_sensor = _temperature_last;
|
||||
status.temperature_target = _param_core_imu_temp.get();
|
||||
status.temperature_target_met = _temperature_target_met;
|
||||
status.controller_period_usec = _controller_period_usec;
|
||||
status.controller_time_on_usec = _controller_time_on_usec;
|
||||
status.proportional_value = _proportional_value;
|
||||
status.integrator_value = _integrator_value;
|
||||
status.feed_forward_value = _param_core_imu_temp_ff.get();
|
||||
|
||||
status.mode = heater_status_s::MODE_GPIO;
|
||||
|
||||
status.timestamp = hrt_absolute_time();
|
||||
_heater_status_pub.publish(status);
|
||||
}
|
||||
|
||||
int Core_Heater::start()
|
||||
{
|
||||
// Exit the driver if the sensor ID does not match the desired sensor.
|
||||
if (_param_core_temp_id.get() == 0) {
|
||||
PX4_ERR("Valid CORE_TEMP_ID required");
|
||||
request_stop();
|
||||
return PX4_ERROR;
|
||||
}
|
||||
|
||||
update_params(true);
|
||||
ScheduleNow();
|
||||
return PX4_OK;
|
||||
}
|
||||
|
||||
int Core_Heater::task_spawn(int argc, char *argv[])
|
||||
{
|
||||
Core_Heater *core_heater = new Core_Heater();
|
||||
|
||||
if (!core_heater) {
|
||||
PX4_ERR("driver allocation failed");
|
||||
return PX4_ERROR;
|
||||
}
|
||||
|
||||
_object.store(core_heater);
|
||||
_task_id = task_id_is_work_queue;
|
||||
|
||||
core_heater->start();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Core_Heater::update_params(const bool force)
|
||||
{
|
||||
if (_parameter_update_sub.updated() || force) {
|
||||
// clear update
|
||||
parameter_update_s param_update;
|
||||
_parameter_update_sub.copy(¶m_update);
|
||||
|
||||
// update parameters from storage
|
||||
ModuleParams::updateParams();
|
||||
}
|
||||
}
|
||||
|
||||
int Core_Heater::print_usage(const char *reason)
|
||||
{
|
||||
if (reason) {
|
||||
printf("%s\n\n", reason);
|
||||
}
|
||||
|
||||
PRINT_MODULE_DESCRIPTION(
|
||||
R"DESCR_STR(
|
||||
### Description
|
||||
Background process running periodically on the LP work queue to regulate IMU temperature at a setpoint.
|
||||
|
||||
)DESCR_STR");
|
||||
|
||||
PRINT_MODULE_USAGE_NAME("core_heater", "system");
|
||||
PRINT_MODULE_USAGE_COMMAND("start");
|
||||
PRINT_MODULE_USAGE_DEFAULT_COMMANDS();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" __EXPORT int core_heater_main(int argc, char *argv[])
|
||||
{
|
||||
return Core_Heater::main(argc, argv);
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Copyright (c) 2025 PX4 Development Team. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name PX4 nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file core_heater.h
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <px4_platform_common/px4_config.h>
|
||||
#include <px4_platform_common/getopt.h>
|
||||
#include <px4_platform_common/module.h>
|
||||
#include <px4_platform_common/module_params.h>
|
||||
#include <px4_platform_common/px4_work_queue/ScheduledWorkItem.hpp>
|
||||
#include <uORB/Publication.hpp>
|
||||
#include <uORB/SubscriptionInterval.hpp>
|
||||
#include <uORB/topics/heater_status.h>
|
||||
#include <uORB/topics/parameter_update.h>
|
||||
#include <uORB/topics/sensor_accel.h>
|
||||
|
||||
#include <mathlib/mathlib.h>
|
||||
|
||||
using namespace time_literals;
|
||||
|
||||
#define CONTROLLER_PERIOD_DEFAULT 10000
|
||||
#define TEMPERATURE_TARGET_THRESHOLD 2.5f
|
||||
|
||||
class Core_Heater : public ModuleBase<Core_Heater>, public ModuleParams, public px4::ScheduledWorkItem
|
||||
{
|
||||
public:
|
||||
Core_Heater();
|
||||
|
||||
virtual ~Core_Heater();
|
||||
|
||||
/**
|
||||
* @see ModuleBase::custom_command().
|
||||
* @brief main Main entry point to the module that should be
|
||||
* called directly from the module's main method.
|
||||
* @param argc The input argument count.
|
||||
* @param argv Pointer to the input argument array.
|
||||
* @return Returns 0 iff successful, -1 otherwise.
|
||||
*/
|
||||
static int custom_command(int argc, char *argv[]);
|
||||
|
||||
/**
|
||||
* @see ModuleBase::print_usage().
|
||||
* @brief Prints the module usage to the nuttshell console.
|
||||
* @param reason The requested reason for printing to console.
|
||||
*/
|
||||
static int print_usage(const char *reason = nullptr);
|
||||
|
||||
/**
|
||||
* @see ModuleBase::task_spawn().
|
||||
* @brief Initializes the class in the same context as the work queue
|
||||
* and starts the background listener.
|
||||
* @param argv Pointer to the input argument array.
|
||||
* @return Returns 0 iff successful, -1 otherwise.
|
||||
*/
|
||||
static int task_spawn(int argc, char *argv[]);
|
||||
|
||||
/**
|
||||
* @brief Initiates the heater driver work queue, starts a new background task,
|
||||
* and fails if it is already running.
|
||||
* @return Returns 1 iff start was successful.
|
||||
*/
|
||||
int start();
|
||||
|
||||
private:
|
||||
|
||||
/** Disables the heater (either by GPIO). */
|
||||
void disable_core_heater();
|
||||
|
||||
/** Turns the heater on (either by GPIO). */
|
||||
void core_heater_on();
|
||||
|
||||
/** Turns the heater off (either by GPIO). */
|
||||
void core_heater_off();
|
||||
|
||||
void initialize();
|
||||
|
||||
/** Enables / configures the heater (either by GPIO). */
|
||||
void initialize_core_heater_io();
|
||||
|
||||
/** @brief Called once to initialize uORB topics. */
|
||||
bool initialize_topics();
|
||||
|
||||
void publish_status();
|
||||
|
||||
/** @brief Calculates the heater element on/off time and schedules the next cycle. */
|
||||
void Run() override;
|
||||
|
||||
/**
|
||||
* @brief Updates and checks for updated uORB parameters.
|
||||
* @param force Boolean to determine if an update check should be forced.
|
||||
*/
|
||||
void update_params(const bool force = false);
|
||||
|
||||
/** Work queue struct for the scheduler. */
|
||||
static struct work_s _work;
|
||||
|
||||
bool _core_heater_initialized = false;
|
||||
bool _core_heater_on = false;
|
||||
bool _temperature_target_met = false;
|
||||
|
||||
int _controller_period_usec = CONTROLLER_PERIOD_DEFAULT;
|
||||
int _controller_time_on_usec = 0;
|
||||
|
||||
float _integrator_value = 0.0f;
|
||||
float _proportional_value = 0.0f;
|
||||
|
||||
uORB::Publication<heater_status_s> _heater_status_pub{ORB_ID(heater_status)};
|
||||
|
||||
uORB::SubscriptionInterval _parameter_update_sub{ORB_ID(parameter_update), 1_s};
|
||||
|
||||
uORB::Subscription _sensor_accel_sub{ORB_ID(sensor_accel)};
|
||||
|
||||
uint32_t _sensor_device_id{0};
|
||||
|
||||
float _temperature_last{NAN};
|
||||
|
||||
DEFINE_PARAMETERS(
|
||||
(ParamFloat<px4::params::CORE_IMU_TEMP_FF>) _param_core_imu_temp_ff,
|
||||
(ParamFloat<px4::params::CORE_IMU_TEMP_I>) _param_core_imu_temp_i,
|
||||
(ParamFloat<px4::params::CORE_IMU_TEMP_P>) _param_core_imu_temp_p,
|
||||
(ParamFloat<px4::params::CORE_IMU_TEMP>) _param_core_imu_temp,
|
||||
(ParamInt<px4::params::CORE_TEMP_ID>) _param_core_temp_id
|
||||
)
|
||||
};
|
||||
@@ -20,15 +20,14 @@ param set-default USB_MAV_MODE 5
|
||||
param set-default UAVCAN_SUB_GPS 1
|
||||
param set-default UAVCAN_SUB_BAT 1
|
||||
|
||||
# Enable IMU thermal control
|
||||
# IMU thermal control (multi-instance heater)
|
||||
# HEATER1_IMU_ID: 2818058(IIM42652 SPI1)
|
||||
# HEATER2_IMU_ID: 3014698(IIM42653 SPI5)
|
||||
param set-default SENS_EN_THERMAL 1
|
||||
param set-default SENS_IMU_TEMP 45
|
||||
param set-default SENS_TEMP_ID 2818058
|
||||
|
||||
# CUAV core board IMU thermal control
|
||||
param set-default CORE_IMU_TEMP 45
|
||||
param set-default CORE_TEMP_ID 3014698
|
||||
core_heater start
|
||||
param set-default HEATER1_IMU_ID 2818058
|
||||
param set-default HEATER1_TEMP 45
|
||||
param set-default HEATER2_IMU_ID 3014698
|
||||
param set-default HEATER2_TEMP 45
|
||||
|
||||
# CUAV pwm voltage 3.3V/5V switch
|
||||
pwm_voltage_apply start
|
||||
|
||||
@@ -175,15 +175,14 @@
|
||||
#define GPIO_HW_REV_SENSE /* PH4 */ GPIO_ADC3_INP15
|
||||
#define GPIO_HW_VER_SENSE /* PH3 */ GPIO_ADC3_INP14
|
||||
|
||||
/* HEATER
|
||||
* PWM in future
|
||||
*/
|
||||
// IMU BOARD HEATER
|
||||
#define GPIO_HEATER_OUTPUT /* PB10 T2CH3 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTB|GPIO_PIN10)
|
||||
#define HEATER_OUTPUT_EN(on_true) px4_arch_gpiowrite(GPIO_HEATER_OUTPUT, (on_true))
|
||||
// CORE BOARD HEATER
|
||||
#define GPIO_CORE_HEATER_OUTPUT /* PE6 T15CH2 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTE|GPIO_PIN6)
|
||||
#define CORE_HEATER_OUTPUT_EN(on_true) px4_arch_gpiowrite(GPIO_CORE_HEATER_OUTPUT, (on_true))
|
||||
|
||||
/* HEATER */
|
||||
#define GPIO_HEATER_OUTPUT
|
||||
#define HEATER_NUM 2
|
||||
#define GPIO_HEATER1_OUTPUT /* PB10 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTB|GPIO_PIN10)
|
||||
#define HEATER1_OUTPUT_EN(on_true) px4_arch_gpiowrite(GPIO_HEATER1_OUTPUT, (on_true))
|
||||
#define GPIO_HEATER2_OUTPUT /* PE6 */ (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTE|GPIO_PIN6)
|
||||
#define HEATER2_OUTPUT_EN(on_true) px4_arch_gpiowrite(GPIO_HEATER2_OUTPUT, (on_true))
|
||||
|
||||
/* PE7 is nARMED
|
||||
* The GPIO will be set as input while not armed HW will have external HW Pull UP.
|
||||
@@ -403,8 +402,8 @@
|
||||
GPIO_CAN2_RX, \
|
||||
GPIO_CAN1_SILENT_S0, \
|
||||
GPIO_CAN2_SILENT_S1, \
|
||||
GPIO_HEATER_OUTPUT, \
|
||||
GPIO_CORE_HEATER_OUTPUT, \
|
||||
GPIO_HEATER1_OUTPUT, \
|
||||
GPIO_HEATER2_OUTPUT, \
|
||||
GPIO_nPOWER_IN_A, \
|
||||
GPIO_nPOWER_IN_B, \
|
||||
GPIO_nPOWER_IN_C, \
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
############################################################################
|
||||
#
|
||||
# Copyright (c) 2026 PX4 Development Team. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# 3. Neither the name PX4 nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
add_subdirectory(pwm_voltage)
|
||||
@@ -0,0 +1,3 @@
|
||||
CONFIG_BOARD_TOOLCHAIN="arm-none-eabi"
|
||||
CONFIG_BOARD_ARCHITECTURE="cortex-m7"
|
||||
CONFIG_BOARD_ROMFSROOT=""
|
||||
@@ -0,0 +1,49 @@
|
||||
############################################################################
|
||||
#
|
||||
# Copyright (c) 2025 PX4 Development Team. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# 3. Neither the name PX4 nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
|
||||
set(PX4_FW_NAME ${PX4_BINARY_DIR}/${PX4_BOARD_VENDOR}_${PX4_BOARD_MODEL}_${PX4_BOARD_LABEL}.px4)
|
||||
|
||||
add_custom_target(upload_skynode_usb
|
||||
COMMAND ${PX4_SOURCE_DIR}/Tools/auterion/upload_skynode.sh --file=${PX4_FW_NAME}
|
||||
DEPENDS ${PX4_FW_NAME}
|
||||
COMMENT "Uploading PX4"
|
||||
USES_TERMINAL
|
||||
)
|
||||
|
||||
add_custom_target(upload_skynode_wifi
|
||||
COMMAND ${PX4_SOURCE_DIR}/Tools/auterion/upload_skynode.sh --file=${PX4_FW_NAME} --wifi
|
||||
DEPENDS ${PX4_FW_NAME}
|
||||
COMMENT "Uploading PX4"
|
||||
USES_TERMINAL
|
||||
)
|
||||
@@ -0,0 +1,91 @@
|
||||
CONFIG_BOARD_TOOLCHAIN="arm-none-eabi"
|
||||
CONFIG_BOARD_ARCHITECTURE="cortex-m7"
|
||||
CONFIG_BOARD_ETHERNET=y
|
||||
CONFIG_BOARD_SERIAL_GPS1="/dev/ttyS0"
|
||||
CONFIG_BOARD_SERIAL_GPS2="/dev/ttyS1"
|
||||
CONFIG_BOARD_SERIAL_TEL1="/dev/ttyS6"
|
||||
CONFIG_BOARD_SERIAL_TEL2="/dev/ttyS4"
|
||||
CONFIG_BOARD_SERIAL_EXT2="/dev/ttyS3"
|
||||
CONFIG_DRIVERS_ADC_ADS1115=y
|
||||
CONFIG_DRIVERS_ADC_BOARD_ADC=y
|
||||
CONFIG_DRIVERS_BAROMETER_BMP581=y
|
||||
CONFIG_DRIVERS_BAROMETER_INVENSENSE_ICP201XX=y
|
||||
CONFIG_DRIVERS_CAMERA_CAPTURE=y
|
||||
CONFIG_DRIVERS_CAMERA_TRIGGER=y
|
||||
CONFIG_DRIVERS_CDCACM_AUTOSTART=y
|
||||
CONFIG_COMMON_DIFFERENTIAL_PRESSURE=y
|
||||
CONFIG_COMMON_DISTANCE_SENSOR=y
|
||||
CONFIG_DRIVERS_DSHOT=y
|
||||
CONFIG_DRIVERS_GPS=y
|
||||
CONFIG_DRIVERS_HEATER=y
|
||||
CONFIG_DRIVERS_IMU_INVENSENSE_IIM42652=y
|
||||
CONFIG_DRIVERS_IMU_INVENSENSE_IIM42653=y
|
||||
CONFIG_DRIVERS_IMU_MURATA_SCH16T=y
|
||||
CONFIG_COMMON_LIGHT=y
|
||||
CONFIG_COMMON_MAGNETOMETER=y
|
||||
CONFIG_DRIVERS_OSD_MSP_OSD=y
|
||||
CONFIG_DRIVERS_POWER_MONITOR_INA226=y
|
||||
CONFIG_DRIVERS_POWER_MONITOR_INA228=y
|
||||
CONFIG_DRIVERS_POWER_MONITOR_INA238=y
|
||||
CONFIG_DRIVERS_PWM_OUT=y
|
||||
CONFIG_DRIVERS_RC_INPUT=y
|
||||
CONFIG_DRIVERS_SAFETY_BUTTON=y
|
||||
CONFIG_DRIVERS_TONE_ALARM=y
|
||||
CONFIG_DRIVERS_UAVCAN=y
|
||||
CONFIG_BOARD_UAVCAN_TIMER_OVERRIDE=2
|
||||
CONFIG_MODULES_AIRSPEED_SELECTOR=y
|
||||
CONFIG_MODULES_BATTERY_STATUS=y
|
||||
CONFIG_MODULES_CAMERA_FEEDBACK=y
|
||||
CONFIG_MODULES_COMMANDER=y
|
||||
CONFIG_MODULES_CONTROL_ALLOCATOR=y
|
||||
CONFIG_MODULES_DATAMAN=y
|
||||
CONFIG_MODULES_EKF2=y
|
||||
CONFIG_MODULES_ESC_BATTERY=y
|
||||
CONFIG_MODULES_EVENTS=y
|
||||
CONFIG_MODULES_FLIGHT_MODE_MANAGER=y
|
||||
CONFIG_MODULES_FW_ATT_CONTROL=y
|
||||
CONFIG_MODULES_FW_AUTOTUNE_ATTITUDE_CONTROL=y
|
||||
CONFIG_MODULES_FW_RATE_CONTROL=y
|
||||
CONFIG_MODULES_GIMBAL=y
|
||||
CONFIG_MODULES_GYRO_CALIBRATION=y
|
||||
CONFIG_MODULES_LAND_DETECTOR=y
|
||||
CONFIG_MODULES_LANDING_TARGET_ESTIMATOR=y
|
||||
CONFIG_MODULES_LOAD_MON=y
|
||||
CONFIG_MODULES_LOGGER=y
|
||||
CONFIG_MODULES_MAG_BIAS_ESTIMATOR=y
|
||||
CONFIG_MODULES_MANUAL_CONTROL=y
|
||||
CONFIG_MODULES_MAVLINK=y
|
||||
CONFIG_MODULES_MC_ATT_CONTROL=y
|
||||
CONFIG_MODULES_MC_AUTOTUNE_ATTITUDE_CONTROL=y
|
||||
CONFIG_MODULES_MC_HOVER_THRUST_ESTIMATOR=y
|
||||
CONFIG_MODULES_MC_POS_CONTROL=y
|
||||
CONFIG_MODULES_MC_RATE_CONTROL=y
|
||||
CONFIG_MODULES_NAVIGATOR=y
|
||||
CONFIG_MODULES_RC_UPDATE=y
|
||||
CONFIG_MODULES_SENSORS=y
|
||||
CONFIG_MODULES_SIMULATION_SIMULATOR_SIH=y
|
||||
CONFIG_MODULES_TEMPERATURE_COMPENSATION=y
|
||||
CONFIG_MODULES_UXRCE_DDS_CLIENT=y
|
||||
CONFIG_MODULES_VTOL_ATT_CONTROL=y
|
||||
CONFIG_SYSTEMCMDS_ACTUATOR_TEST=y
|
||||
CONFIG_SYSTEMCMDS_BSONDUMP=y
|
||||
CONFIG_SYSTEMCMDS_DMESG=y
|
||||
CONFIG_SYSTEMCMDS_GPIO=y
|
||||
CONFIG_SYSTEMCMDS_HARDFAULT_LOG=y
|
||||
CONFIG_SYSTEMCMDS_I2CDETECT=y
|
||||
CONFIG_SYSTEMCMDS_LED_CONTROL=y
|
||||
CONFIG_SYSTEMCMDS_MFT=y
|
||||
CONFIG_SYSTEMCMDS_MTD=y
|
||||
CONFIG_SYSTEMCMDS_NETMAN=y
|
||||
CONFIG_SYSTEMCMDS_NSHTERM=y
|
||||
CONFIG_SYSTEMCMDS_PARAM=y
|
||||
CONFIG_SYSTEMCMDS_PERF=y
|
||||
CONFIG_SYSTEMCMDS_REBOOT=y
|
||||
CONFIG_SYSTEMCMDS_SYSTEM_TIME=y
|
||||
CONFIG_SYSTEMCMDS_TOP=y
|
||||
CONFIG_SYSTEMCMDS_TOPIC_LISTENER=y
|
||||
CONFIG_SYSTEMCMDS_TUNE_CONTROL=y
|
||||
CONFIG_SYSTEMCMDS_UORB=y
|
||||
CONFIG_SYSTEMCMDS_VER=y
|
||||
CONFIG_SYSTEMCMDS_WORK_QUEUE=y
|
||||
CONFIG_ARCH_CHIP_STM32H7=y
|
||||
Binary file not shown.
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"board_id": 7003,
|
||||
"magic": "PX4FWv1",
|
||||
"description": "Firmware for the CUAVX25SUPER board",
|
||||
"image": "",
|
||||
"build_time": 0,
|
||||
"summary": "CUAVX25SUPER",
|
||||
"version": "0.1",
|
||||
"image_size": 0,
|
||||
"image_maxsize": 1966080,
|
||||
"git_identity": "",
|
||||
"board_revision": 0
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# board specific defaults
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
Mavlink ethernet (CFG 1000)
|
||||
param set-default MAV_2_CONFIG 1000
|
||||
param set-default MAV_2_BROADCAST 1
|
||||
param set-default MAV_2_MODE 0
|
||||
param set-default MAV_2_RADIO_CTL 0
|
||||
param set-default MAV_2_RATE 100000
|
||||
param set-default MAV_2_REMOTE_PRT 14550
|
||||
param set-default MAV_2_UDP_PRT 14550
|
||||
|
||||
param set-default SENS_EN_INA238 0
|
||||
param set-default SENS_EN_INA228 0
|
||||
param set-default SENS_EN_INA226 0
|
||||
|
||||
param set-default USB_MAV_MODE 5
|
||||
|
||||
param set-default UAVCAN_SUB_GPS 1
|
||||
param set-default UAVCAN_SUB_BAT 1
|
||||
|
||||
# IMU thermal control (multi-instance heater)
|
||||
param set-default SENS_EN_THERMAL 1
|
||||
param set-default HEATER1_IMU_ID 2818066
|
||||
param set-default HEATER1_TEMP 45
|
||||
param set-default HEATER2_IMU_ID 3014698
|
||||
param set-default HEATER2_TEMP 45
|
||||
|
||||
# CUAV pwm voltage 3.3V/5V switch
|
||||
pwm_voltage_apply start
|
||||
|
||||
safety_button start
|
||||
|
||||
# Update IP config if needed
|
||||
netman update -i eth0
|
||||
@@ -0,0 +1,65 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# CUAV X25-SUPER specific board sensors init
|
||||
#------------------------------------------------------------------------------
|
||||
set HAVE_PM2 yes
|
||||
|
||||
if mft query -q -k MFT -s MFT_PM2 -v 0
|
||||
then
|
||||
set HAVE_PM2 no
|
||||
fi
|
||||
|
||||
board_adc start
|
||||
|
||||
if param compare -s ADC_ADS1115_EN 1
|
||||
then
|
||||
ads1115 start -X
|
||||
board_adc start -n
|
||||
else
|
||||
board_adc start
|
||||
fi
|
||||
|
||||
if param compare SENS_EN_INA226 1
|
||||
then
|
||||
# Start Digital power monitors
|
||||
ina226 -X -b 1 -t 1 -k start
|
||||
|
||||
if [ $HAVE_PM2 = yes ]
|
||||
then
|
||||
ina226 -X -b 2 -t 2 -k start
|
||||
fi
|
||||
fi
|
||||
|
||||
if param compare SENS_EN_INA228 1
|
||||
then
|
||||
# Start Digital power monitors
|
||||
ina228 -X -b 1 -t 1 -k start
|
||||
if [ $HAVE_PM2 = yes ]
|
||||
then
|
||||
ina228 -X -b 2 -t 2 -k start
|
||||
fi
|
||||
fi
|
||||
|
||||
if param compare SENS_EN_INA238 1
|
||||
then
|
||||
# Start Digital power monitors
|
||||
ina238 -X -b 1 -t 1 -k start
|
||||
if [ $HAVE_PM2 = yes ]
|
||||
then
|
||||
ina238 -X -b 2 -t 2 -k start
|
||||
fi
|
||||
fi
|
||||
|
||||
sch16t -s -R 2 -b 1 start
|
||||
iim42652 -s -b 2 -C 32768 start
|
||||
iim42653 -s -b 5 -R 12 start
|
||||
|
||||
rm3100 -s -b 4 start
|
||||
|
||||
bmp581 -s -b 4 start
|
||||
icp201xx -I -b 4 start
|
||||
|
||||
# External compass on GPS1/I2C1 (the 3rd external bus): standard Holybro Pixhawk 4 or CUAV V5 GPS/compass puck (with lights, safety button, and buzzer)
|
||||
ist8310 -X -b 1 -R 10 start
|
||||
|
||||
unset HAVE_PM2
|
||||
@@ -0,0 +1,17 @@
|
||||
#
|
||||
# For a description of the syntax of this configuration file,
|
||||
# see misc/tools/kconfig-language.txt.
|
||||
#
|
||||
config BOARD_HAS_PROBES
|
||||
bool "Board provides GPIO or other Hardware for signaling to timing analyze."
|
||||
default y
|
||||
---help---
|
||||
This board provides GPIO FMU-CH1-5, CAP1-6 as PROBE_1-11 to provide timing signals from selected drivers.
|
||||
|
||||
config BOARD_USE_PROBES
|
||||
bool "Enable the use the board provided FMU-CH1-5, CAP1-6 as PROBE_1-11"
|
||||
default n
|
||||
depends on BOARD_HAS_PROBES
|
||||
|
||||
---help---
|
||||
Select to use GPIO FMU-CH1-5, CAP1-6 to provide timing signals from selected drivers.
|
||||
@@ -0,0 +1,95 @@
|
||||
#
|
||||
# This file is autogenerated: PLEASE DO NOT EDIT IT.
|
||||
#
|
||||
# You can use "make menuconfig" to make any modifications to the installed .config file.
|
||||
# You can then do "make savedefconfig" to generate a new defconfig file that includes your
|
||||
# modifications.
|
||||
#
|
||||
# CONFIG_DEV_CONSOLE is not set
|
||||
# CONFIG_DISABLE_PSEUDOFS_OPERATIONS is not set
|
||||
# CONFIG_DISABLE_PTHREAD is not set
|
||||
# CONFIG_SPI_EXCHANGE is not set
|
||||
# CONFIG_STM32H7_SYSCFG is not set
|
||||
CONFIG_ARCH="arm"
|
||||
CONFIG_ARCH_BOARD_CUSTOM=y
|
||||
CONFIG_ARCH_BOARD_CUSTOM_DIR="../../../../boards/cuav/x25-super/nuttx-config"
|
||||
CONFIG_ARCH_BOARD_CUSTOM_DIR_RELPATH=y
|
||||
CONFIG_ARCH_BOARD_CUSTOM_NAME="px4"
|
||||
CONFIG_ARCH_CHIP="stm32h7"
|
||||
CONFIG_ARCH_CHIP_STM32H743XI=y
|
||||
CONFIG_ARCH_CHIP_STM32H7=y
|
||||
CONFIG_ARCH_INTERRUPTSTACK=768
|
||||
CONFIG_ARMV7M_BASEPRI_WAR=y
|
||||
CONFIG_ARMV7M_ICACHE=y
|
||||
CONFIG_ARMV7M_MEMCPY=y
|
||||
CONFIG_ARMV7M_USEBASEPRI=y
|
||||
CONFIG_BOARDCTL=y
|
||||
CONFIG_BOARDCTL_RESET=y
|
||||
CONFIG_BOARD_ASSERT_RESET_VALUE=0
|
||||
CONFIG_BOARD_INITTHREAD_PRIORITY=254
|
||||
CONFIG_BOARD_LATE_INITIALIZE=y
|
||||
CONFIG_BOARD_LOOPSPERMSEC=95150
|
||||
CONFIG_BOARD_RESET_ON_ASSERT=2
|
||||
CONFIG_CDCACM=y
|
||||
CONFIG_CDCACM_IFLOWCONTROL=y
|
||||
CONFIG_CDCACM_PRODUCTID=0x004c
|
||||
CONFIG_CDCACM_PRODUCTSTR="PX4 BL CUAV X25-SUPER"
|
||||
CONFIG_CDCACM_RXBUFSIZE=600
|
||||
CONFIG_CDCACM_TXBUFSIZE=12000
|
||||
CONFIG_CDCACM_VENDORID=0x3163
|
||||
CONFIG_CDCACM_VENDORSTR="CUAV"
|
||||
CONFIG_DEBUG_FULLOPT=y
|
||||
CONFIG_DEBUG_SYMBOLS=y
|
||||
CONFIG_DEBUG_TCBINFO=y
|
||||
CONFIG_DEFAULT_SMALL=y
|
||||
CONFIG_EXPERIMENTAL=y
|
||||
CONFIG_FDCLONE_DISABLE=y
|
||||
CONFIG_FDCLONE_STDIO=y
|
||||
CONFIG_HAVE_CXX=y
|
||||
CONFIG_HAVE_CXXINITIALIZE=y
|
||||
CONFIG_IDLETHREAD_STACKSIZE=750
|
||||
CONFIG_INIT_ENTRYPOINT="bootloader_main"
|
||||
CONFIG_INIT_STACKSIZE=3194
|
||||
CONFIG_LIBC_FLOATINGPOINT=y
|
||||
CONFIG_LIBC_LONG_LONG=y
|
||||
CONFIG_LIBC_STRERROR=y
|
||||
CONFIG_MEMSET_64BIT=y
|
||||
CONFIG_MEMSET_OPTSPEED=y
|
||||
CONFIG_PREALLOC_TIMERS=50
|
||||
CONFIG_PTHREAD_MUTEX_ROBUST=y
|
||||
CONFIG_PTHREAD_STACK_MIN=512
|
||||
CONFIG_RAM_SIZE=245760
|
||||
CONFIG_RAM_START=0x20010000
|
||||
CONFIG_RAW_BINARY=y
|
||||
CONFIG_SERIAL_TERMIOS=y
|
||||
CONFIG_SIG_DEFAULT=y
|
||||
CONFIG_SIG_SIGALRM_ACTION=y
|
||||
CONFIG_SIG_SIGUSR1_ACTION=y
|
||||
CONFIG_SIG_SIGUSR2_ACTION=y
|
||||
CONFIG_SPI=y
|
||||
CONFIG_STACK_COLORATION=y
|
||||
CONFIG_START_DAY=30
|
||||
CONFIG_START_MONTH=11
|
||||
CONFIG_STDIO_BUFFER_SIZE=32
|
||||
CONFIG_STM32H7_BKPSRAM=y
|
||||
CONFIG_STM32H7_DMA1=y
|
||||
CONFIG_STM32H7_DMA2=y
|
||||
CONFIG_STM32H7_OTGFS=y
|
||||
CONFIG_STM32H7_PROGMEM=y
|
||||
CONFIG_STM32H7_SERIAL_DISABLE_REORDERING=y
|
||||
CONFIG_STM32H7_TIM1=y
|
||||
CONFIG_STM32H7_UART5=y
|
||||
CONFIG_SYSTEMTICK_HOOK=y
|
||||
CONFIG_SYSTEM_CDCACM=y
|
||||
CONFIG_TASK_NAME_SIZE=24
|
||||
CONFIG_TTY_SIGINT=y
|
||||
CONFIG_TTY_SIGINT_CHAR=0x03
|
||||
CONFIG_TTY_SIGTSTP=y
|
||||
CONFIG_UART5_RXBUFSIZE=512
|
||||
CONFIG_UART5_RXDMA=y
|
||||
CONFIG_UART5_TXBUFSIZE=512
|
||||
CONFIG_UART5_TXDMA=y
|
||||
CONFIG_USBDEV=y
|
||||
CONFIG_USBDEV_BUSPOWERED=y
|
||||
CONFIG_USBDEV_MAXPOWER=500
|
||||
CONFIG_USEC_PER_TICK=1000
|
||||
@@ -0,0 +1,565 @@
|
||||
/************************************************************************************
|
||||
* nuttx-configs/cuav_x25-super/include/board.h
|
||||
*
|
||||
* Copyright (C) 2016-2026 Gregory Nutt. All rights reserved.
|
||||
* Authors: David Sidrane <david.sidrane@nscdg.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name NuttX nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
************************************************************************************/
|
||||
#ifndef __NUTTX_CONFIG_CUAV_X25_SUPER_INCLUDE_BOARD_H
|
||||
#define __NUTTX_CONFIG_CUAV_X25_SUPER_INCLUDE_BOARD_H
|
||||
|
||||
/************************************************************************************
|
||||
* Included Files
|
||||
************************************************************************************/
|
||||
|
||||
#include "board_dma_map.h"
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
# include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include "stm32_rcc.h"
|
||||
#include "stm32_sdmmc.h"
|
||||
|
||||
/************************************************************************************
|
||||
* Pre-processor Definitions
|
||||
************************************************************************************/
|
||||
|
||||
/* Clocking *************************************************************************/
|
||||
/* The CUAV X25-SUPER board provides the following clock sources:
|
||||
*
|
||||
* X1: 16 MHz crystal for HSE
|
||||
*
|
||||
* So we have these clock source available within the STM32
|
||||
*
|
||||
* HSI: 16 MHz RC factory-trimmed
|
||||
* HSE: 16 MHz crystal for HSE
|
||||
*/
|
||||
|
||||
#define STM32_BOARD_XTAL 16000000ul
|
||||
|
||||
#define STM32_HSI_FREQUENCY 16000000ul
|
||||
#define STM32_LSI_FREQUENCY 32000
|
||||
#define STM32_HSE_FREQUENCY STM32_BOARD_XTAL
|
||||
#define STM32_LSE_FREQUENCY 32768
|
||||
|
||||
/* Main PLL Configuration.
|
||||
*
|
||||
* PLL source is HSE = 16,000,000
|
||||
*
|
||||
* PLL_VCOx = (STM32_HSE_FREQUENCY / PLLM) * PLLN
|
||||
* Subject to:
|
||||
*
|
||||
* 1 <= PLLM <= 63
|
||||
* 4 <= PLLN <= 512
|
||||
* 150 MHz <= PLL_VCOL <= 420MHz
|
||||
* 192 MHz <= PLL_VCOH <= 836MHz
|
||||
*
|
||||
* SYSCLK = PLL_VCO / PLLP
|
||||
* CPUCLK = SYSCLK / D1CPRE
|
||||
* Subject to
|
||||
*
|
||||
* PLLP1 = {2, 4, 6, 8, ..., 128}
|
||||
* PLLP2,3 = {2, 3, 4, ..., 128}
|
||||
* CPUCLK <= 480 MHz
|
||||
*/
|
||||
|
||||
// #define STM32_BOARD_USEHSE
|
||||
|
||||
#define STM32_PLLCFG_PLLSRC RCC_PLLCKSELR_PLLSRC_HSE
|
||||
|
||||
/* PLL1, wide 4 - 8 MHz input, enable DIVP, DIVQ, DIVR
|
||||
*
|
||||
* PLL1_VCO = (16,000,000 / 1) * 60 = 960 MHz
|
||||
*
|
||||
* PLL1P = PLL1_VCO/2 = 960 MHz / 2 = 480 MHz
|
||||
* PLL1Q = PLL1_VCO/4 = 960 MHz / 4 = 240 MHz
|
||||
* PLL1R = PLL1_VCO/8 = 960 MHz / 8 = 120 MHz
|
||||
*/
|
||||
|
||||
#define STM32_PLLCFG_PLL1CFG (RCC_PLLCFGR_PLL1VCOSEL_WIDE | \
|
||||
RCC_PLLCFGR_PLL1RGE_4_8_MHZ | \
|
||||
RCC_PLLCFGR_DIVP1EN | \
|
||||
RCC_PLLCFGR_DIVQ1EN | \
|
||||
RCC_PLLCFGR_DIVR1EN)
|
||||
#define STM32_PLLCFG_PLL1M RCC_PLLCKSELR_DIVM1(1)
|
||||
#define STM32_PLLCFG_PLL1N RCC_PLL1DIVR_N1(60)
|
||||
#define STM32_PLLCFG_PLL1P RCC_PLL1DIVR_P1(2)
|
||||
#define STM32_PLLCFG_PLL1Q RCC_PLL1DIVR_Q1(4)
|
||||
#define STM32_PLLCFG_PLL1R RCC_PLL1DIVR_R1(8)
|
||||
|
||||
#define STM32_VCO1_FREQUENCY ((STM32_HSE_FREQUENCY / 1) * 60)
|
||||
#define STM32_PLL1P_FREQUENCY (STM32_VCO1_FREQUENCY / 2)
|
||||
#define STM32_PLL1Q_FREQUENCY (STM32_VCO1_FREQUENCY / 4)
|
||||
#define STM32_PLL1R_FREQUENCY (STM32_VCO1_FREQUENCY / 8)
|
||||
|
||||
/* PLL2 */
|
||||
|
||||
#define STM32_PLLCFG_PLL2CFG (RCC_PLLCFGR_PLL2VCOSEL_WIDE | \
|
||||
RCC_PLLCFGR_PLL2RGE_4_8_MHZ | \
|
||||
RCC_PLLCFGR_DIVP2EN | \
|
||||
RCC_PLLCFGR_DIVQ2EN | \
|
||||
RCC_PLLCFGR_DIVR2EN)
|
||||
#define STM32_PLLCFG_PLL2M RCC_PLLCKSELR_DIVM2(4)
|
||||
#define STM32_PLLCFG_PLL2N RCC_PLL2DIVR_N2(48)
|
||||
#define STM32_PLLCFG_PLL2P RCC_PLL2DIVR_P2(2)
|
||||
#define STM32_PLLCFG_PLL2Q RCC_PLL2DIVR_Q2(2)
|
||||
#define STM32_PLLCFG_PLL2R RCC_PLL2DIVR_R2(2)
|
||||
|
||||
#define STM32_VCO2_FREQUENCY ((STM32_HSE_FREQUENCY / 4) * 48)
|
||||
#define STM32_PLL2P_FREQUENCY (STM32_VCO2_FREQUENCY / 2)
|
||||
#define STM32_PLL2Q_FREQUENCY (STM32_VCO2_FREQUENCY / 2)
|
||||
#define STM32_PLL2R_FREQUENCY (STM32_VCO2_FREQUENCY / 2)
|
||||
|
||||
/* PLL3 */
|
||||
|
||||
#define STM32_PLLCFG_PLL3CFG (RCC_PLLCFGR_PLL3VCOSEL_WIDE | \
|
||||
RCC_PLLCFGR_PLL3RGE_4_8_MHZ | \
|
||||
RCC_PLLCFGR_DIVQ3EN)
|
||||
#define STM32_PLLCFG_PLL3M RCC_PLLCKSELR_DIVM3(4)
|
||||
#define STM32_PLLCFG_PLL3N RCC_PLL3DIVR_N3(48)
|
||||
#define STM32_PLLCFG_PLL3P RCC_PLL3DIVR_P3(2)
|
||||
#define STM32_PLLCFG_PLL3Q RCC_PLL3DIVR_Q3(4)
|
||||
#define STM32_PLLCFG_PLL3R RCC_PLL3DIVR_R3(2)
|
||||
|
||||
#define STM32_VCO3_FREQUENCY ((STM32_HSE_FREQUENCY / 4) * 48)
|
||||
#define STM32_PLL3P_FREQUENCY (STM32_VCO3_FREQUENCY / 2)
|
||||
#define STM32_PLL3Q_FREQUENCY (STM32_VCO3_FREQUENCY / 4)
|
||||
#define STM32_PLL3R_FREQUENCY (STM32_VCO3_FREQUENCY / 2)
|
||||
|
||||
/* SYSCLK = PLL1P = 480MHz
|
||||
* CPUCLK = SYSCLK / 1 = 480 MHz
|
||||
*/
|
||||
|
||||
#define STM32_RCC_D1CFGR_D1CPRE (RCC_D1CFGR_D1CPRE_SYSCLK)
|
||||
#define STM32_SYSCLK_FREQUENCY (STM32_PLL1P_FREQUENCY)
|
||||
#define STM32_CPUCLK_FREQUENCY (STM32_SYSCLK_FREQUENCY / 1)
|
||||
|
||||
/* Configure Clock Assignments */
|
||||
|
||||
/* AHB clock (HCLK) is SYSCLK/2 (240 MHz max)
|
||||
* HCLK1 = HCLK2 = HCLK3 = HCLK4 = 240
|
||||
*/
|
||||
|
||||
#define STM32_RCC_D1CFGR_HPRE RCC_D1CFGR_HPRE_SYSCLKd2 /* HCLK = SYSCLK / 2 */
|
||||
#define STM32_ACLK_FREQUENCY (STM32_CPUCLK_FREQUENCY / 2) /* ACLK in D1, HCLK3 in D1 */
|
||||
#define STM32_HCLK_FREQUENCY (STM32_CPUCLK_FREQUENCY / 2) /* HCLK in D2, HCLK4 in D3 */
|
||||
#define STM32_BOARD_HCLK STM32_HCLK_FREQUENCY /* same as above, to satisfy compiler */
|
||||
|
||||
/* APB1 clock (PCLK1) is HCLK/2 (120 MHz) */
|
||||
|
||||
#define STM32_RCC_D2CFGR_D2PPRE1 RCC_D2CFGR_D2PPRE1_HCLKd2 /* PCLK1 = HCLK / 2 */
|
||||
#define STM32_PCLK1_FREQUENCY (STM32_HCLK_FREQUENCY/2)
|
||||
|
||||
/* APB2 clock (PCLK2) is HCLK/2 (120 MHz) */
|
||||
|
||||
#define STM32_RCC_D2CFGR_D2PPRE2 RCC_D2CFGR_D2PPRE2_HCLKd2 /* PCLK2 = HCLK / 2 */
|
||||
#define STM32_PCLK2_FREQUENCY (STM32_HCLK_FREQUENCY/2)
|
||||
|
||||
/* APB3 clock (PCLK3) is HCLK/2 (120 MHz) */
|
||||
|
||||
#define STM32_RCC_D1CFGR_D1PPRE RCC_D1CFGR_D1PPRE_HCLKd2 /* PCLK3 = HCLK / 2 */
|
||||
#define STM32_PCLK3_FREQUENCY (STM32_HCLK_FREQUENCY/2)
|
||||
|
||||
/* APB4 clock (PCLK4) is HCLK/4 (120 MHz) */
|
||||
|
||||
#define STM32_RCC_D3CFGR_D3PPRE RCC_D3CFGR_D3PPRE_HCLKd2 /* PCLK4 = HCLK / 2 */
|
||||
#define STM32_PCLK4_FREQUENCY (STM32_HCLK_FREQUENCY/2)
|
||||
|
||||
/* Timer clock frequencies */
|
||||
|
||||
/* Timers driven from APB1 will be twice PCLK1 */
|
||||
|
||||
#define STM32_APB1_TIM2_CLKIN (2*STM32_PCLK1_FREQUENCY)
|
||||
#define STM32_APB1_TIM3_CLKIN (2*STM32_PCLK1_FREQUENCY)
|
||||
#define STM32_APB1_TIM4_CLKIN (2*STM32_PCLK1_FREQUENCY)
|
||||
#define STM32_APB1_TIM5_CLKIN (2*STM32_PCLK1_FREQUENCY)
|
||||
#define STM32_APB1_TIM6_CLKIN (2*STM32_PCLK1_FREQUENCY)
|
||||
#define STM32_APB1_TIM7_CLKIN (2*STM32_PCLK1_FREQUENCY)
|
||||
#define STM32_APB1_TIM12_CLKIN (2*STM32_PCLK1_FREQUENCY)
|
||||
#define STM32_APB1_TIM13_CLKIN (2*STM32_PCLK1_FREQUENCY)
|
||||
#define STM32_APB1_TIM14_CLKIN (2*STM32_PCLK1_FREQUENCY)
|
||||
|
||||
/* Timers driven from APB2 will be twice PCLK2 */
|
||||
|
||||
#define STM32_APB2_TIM1_CLKIN (2*STM32_PCLK2_FREQUENCY)
|
||||
#define STM32_APB2_TIM8_CLKIN (2*STM32_PCLK2_FREQUENCY)
|
||||
#define STM32_APB2_TIM15_CLKIN (2*STM32_PCLK2_FREQUENCY)
|
||||
#define STM32_APB2_TIM16_CLKIN (2*STM32_PCLK2_FREQUENCY)
|
||||
#define STM32_APB2_TIM17_CLKIN (2*STM32_PCLK2_FREQUENCY)
|
||||
|
||||
/* Kernel Clock Configuration
|
||||
*
|
||||
* Note: look at Table 54 in ST Manual
|
||||
*/
|
||||
|
||||
/* I2C123 clock source */
|
||||
|
||||
#define STM32_RCC_D2CCIP2R_I2C123SRC RCC_D2CCIP2R_I2C123SEL_HSI
|
||||
|
||||
/* I2C4 clock source */
|
||||
|
||||
#define STM32_RCC_D3CCIPR_I2C4SRC RCC_D3CCIPR_I2C4SEL_HSI
|
||||
|
||||
/* SPI123 clock source */
|
||||
|
||||
#define STM32_RCC_D2CCIP1R_SPI123SRC RCC_D2CCIP1R_SPI123SEL_PLL2
|
||||
|
||||
/* SPI45 clock source */
|
||||
|
||||
#define STM32_RCC_D2CCIP1R_SPI45SRC RCC_D2CCIP1R_SPI45SEL_PLL2
|
||||
|
||||
/* SPI6 clock source */
|
||||
|
||||
#define STM32_RCC_D3CCIPR_SPI6SRC RCC_D3CCIPR_SPI6SEL_PLL2
|
||||
|
||||
/* USB 1 and 2 clock source */
|
||||
|
||||
#define STM32_RCC_D2CCIP2R_USBSRC RCC_D2CCIP2R_USBSEL_PLL3
|
||||
|
||||
/* ADC 1 2 3 clock source */
|
||||
|
||||
#define STM32_RCC_D3CCIPR_ADCSRC RCC_D3CCIPR_ADCSEL_PLL2
|
||||
|
||||
/* UART clock selection */
|
||||
/* reset to default to overwrite any changes done by any bootloader */
|
||||
|
||||
#define STM32_RCC_D2CCIP2R_USART234578_SEL RCC_D2CCIP2R_USART234578SEL_RCC
|
||||
#define STM32_RCC_D2CCIP2R_USART16_SEL RCC_D2CCIP2R_USART16SEL_RCC
|
||||
|
||||
/* FDCAN 1 2 clock source */
|
||||
|
||||
#define STM32_RCC_D2CCIP1R_FDCANSEL RCC_D2CCIP1R_FDCANSEL_HSE /* FDCAN 1 2 clock source */
|
||||
|
||||
#define STM32_FDCANCLK STM32_HSE_FREQUENCY
|
||||
|
||||
/* FLASH wait states
|
||||
*
|
||||
* ------------ ---------- -----------
|
||||
* Vcore MAX ACLK WAIT STATES
|
||||
* ------------ ---------- -----------
|
||||
* 1.15-1.26 V 70 MHz 0
|
||||
* (VOS1 level) 140 MHz 1
|
||||
* 210 MHz 2
|
||||
* 1.05-1.15 V 55 MHz 0
|
||||
* (VOS2 level) 110 MHz 1
|
||||
* 165 MHz 2
|
||||
* 220 MHz 3
|
||||
* 0.95-1.05 V 45 MHz 0
|
||||
* (VOS3 level) 90 MHz 1
|
||||
* 135 MHz 2
|
||||
* 180 MHz 3
|
||||
* 225 MHz 4
|
||||
* ------------ ---------- -----------
|
||||
*/
|
||||
|
||||
#define BOARD_FLASH_WAITSTATES 2
|
||||
|
||||
/* SDMMC definitions ********************************************************/
|
||||
|
||||
/* Init 400kHz, freq = PLL1Q/(2*div) div = PLL1Q/(2*freq) */
|
||||
|
||||
#define STM32_SDMMC_INIT_CLKDIV (300 << STM32_SDMMC_CLKCR_CLKDIV_SHIFT)
|
||||
|
||||
/* 25 MHz Max for now, 25 mHZ = PLL1Q/(2*div), div = PLL1Q/(2*freq)
|
||||
* div = 4.8 = 240 / 50, So round up to 5 for default speed 24 MB/s
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_STM32H7_SDMMC_XDMA) || defined(CONFIG_STM32H7_SDMMC_IDMA)
|
||||
# define STM32_SDMMC_MMCXFR_CLKDIV (5 << STM32_SDMMC_CLKCR_CLKDIV_SHIFT)
|
||||
#else
|
||||
# define STM32_SDMMC_MMCXFR_CLKDIV (100 << STM32_SDMMC_CLKCR_CLKDIV_SHIFT)
|
||||
#endif
|
||||
#if defined(CONFIG_STM32H7_SDMMC_XDMA) || defined(CONFIG_STM32H7_SDMMC_IDMA)
|
||||
# define STM32_SDMMC_SDXFR_CLKDIV (5 << STM32_SDMMC_CLKCR_CLKDIV_SHIFT)
|
||||
#else
|
||||
# define STM32_SDMMC_SDXFR_CLKDIV (100 << STM32_SDMMC_CLKCR_CLKDIV_SHIFT)
|
||||
#endif
|
||||
|
||||
#define STM32_SDMMC_CLKCR_EDGE STM32_SDMMC_CLKCR_NEGEDGE
|
||||
|
||||
/* LED definitions ******************************************************************/
|
||||
/* The PX4 FMUV6X board has three, LED_GREEN a Green LED, LED_BLUE a Blue LED and
|
||||
* LED_RED a Red LED, that can be controlled by software.
|
||||
*
|
||||
* If CONFIG_ARCH_LEDS is not defined, then the user can control the LEDs in any way.
|
||||
* The following definitions are used to access individual LEDs.
|
||||
*/
|
||||
|
||||
/* LED index values for use with board_userled() */
|
||||
|
||||
/* LED definitions ******************************************************************/
|
||||
/* The px4_fmu-v6x board has three, LED_GREEN a Green LED, LED_BLUE a Blue LED and
|
||||
* LED_RED a Red LED, that can be controlled by software.
|
||||
*
|
||||
* If CONFIG_ARCH_LEDS is not defined, then the user can control the LEDs in any way.
|
||||
* The following definitions are used to access individual LEDs.
|
||||
*/
|
||||
|
||||
/* LED index values for use with board_userled() */
|
||||
|
||||
#define BOARD_LED1 0
|
||||
#define BOARD_LED2 1
|
||||
#define BOARD_LED3 2
|
||||
#define BOARD_NLEDS 3
|
||||
|
||||
#define BOARD_LED_RED BOARD_LED1
|
||||
#define BOARD_LED_GREEN BOARD_LED2
|
||||
#define BOARD_LED_BLUE BOARD_LED3
|
||||
|
||||
/* LED bits for use with board_userled_all() */
|
||||
|
||||
#define BOARD_LED1_BIT (1 << BOARD_LED1)
|
||||
#define BOARD_LED2_BIT (1 << BOARD_LED2)
|
||||
#define BOARD_LED3_BIT (1 << BOARD_LED3)
|
||||
|
||||
/* If CONFIG_ARCH_LEDS is defined, the usage by the board port is defined in
|
||||
* include/board.h and src/stm32_leds.c. The LEDs are used to encode OS-related
|
||||
* events as follows:
|
||||
*
|
||||
*
|
||||
* SYMBOL Meaning LED state
|
||||
* Red Green Blue
|
||||
* ---------------------- -------------------------- ------ ------ ----*/
|
||||
|
||||
#define LED_STARTED 0 /* NuttX has been started OFF OFF OFF */
|
||||
#define LED_HEAPALLOCATE 1 /* Heap has been allocated OFF OFF ON */
|
||||
#define LED_IRQSENABLED 2 /* Interrupts enabled OFF ON OFF */
|
||||
#define LED_STACKCREATED 3 /* Idle stack created OFF ON ON */
|
||||
#define LED_INIRQ 4 /* In an interrupt N/C N/C GLOW */
|
||||
#define LED_SIGNAL 5 /* In a signal handler N/C GLOW N/C */
|
||||
#define LED_ASSERTION 6 /* An assertion failed GLOW N/C GLOW */
|
||||
#define LED_PANIC 7 /* The system has crashed Blink OFF N/C */
|
||||
#define LED_IDLE 8 /* MCU is is sleep mode ON OFF OFF */
|
||||
|
||||
/* Thus if the Green LED is statically on, NuttX has successfully booted and
|
||||
* is, apparently, running normally. If the Red LED is flashing at
|
||||
* approximately 2Hz, then a fatal error has been detected and the system
|
||||
* has halted.
|
||||
*/
|
||||
|
||||
/* Alternate function pin selections ************************************************/
|
||||
|
||||
#define GPIO_USART1_RX GPIO_USART1_RX_3 /* PB7 */
|
||||
#define GPIO_USART1_TX GPIO_USART1_TX_3 /* PB6 */
|
||||
|
||||
#define GPIO_USART2_RX GPIO_USART2_RX_1 /* PA3 */
|
||||
#define GPIO_USART2_TX GPIO_USART2_TX_2 /* PD5 */
|
||||
#define GPIO_USART2_RTS GPIO_USART2_RTS_2 /* PD4 */
|
||||
#define GPIO_USART2_CTS GPIO_USART2_CTS_NSS_2 /* PD3 */
|
||||
|
||||
#define GPIO_USART3_RX GPIO_USART3_RX_3 /* PD9 */
|
||||
#define GPIO_USART3_TX GPIO_USART3_TX_3 /* PD8 */
|
||||
|
||||
#define GPIO_UART4_RX GPIO_UART4_RX_6 /* PH14 */
|
||||
#define GPIO_UART4_TX GPIO_UART4_TX_6 /* PH13 */
|
||||
|
||||
#define GPIO_UART5_RX GPIO_UART5_RX_3 /* PD2 */
|
||||
#define GPIO_UART5_TX GPIO_UART5_TX_3 /* PC12 */
|
||||
// GPIO_UART5_RTS no remap /* PC8 */
|
||||
#undef GPIO_UART5_CTS
|
||||
#define GPIO_UART5_CTS ((GPIO_ALT|GPIO_AF8|GPIO_PORTC|GPIO_PIN9) | GPIO_PULLDOWN)
|
||||
|
||||
#define GPIO_USART6_RX GPIO_USART6_RX_1 /* PC7 */
|
||||
#define GPIO_USART6_TX GPIO_USART6_TX_1 /* PC6 */
|
||||
|
||||
#define GPIO_UART7_RX GPIO_UART7_RX_4 /* PF6 */
|
||||
#define GPIO_UART7_TX GPIO_UART7_TX_3 /* PE8 */
|
||||
#define GPIO_UART7_RTS GPIO_UART7_RTS_2 /* PF8 */
|
||||
#define GPIO_UART7_CTS (GPIO_UART7_CTS_1 | GPIO_PULLDOWN) /* PE10 */
|
||||
|
||||
#define GPIO_UART8_RX GPIO_UART8_RX_1 /* PE0 */
|
||||
#define GPIO_UART8_TX GPIO_UART8_TX_1 /* PE1 */
|
||||
|
||||
|
||||
/* CAN
|
||||
*
|
||||
* CAN1 is routed to transceiver.
|
||||
* CAN2 is routed to transceiver.
|
||||
*/
|
||||
#define GPIO_CAN1_RX GPIO_CAN1_RX_3 /* PD0 */
|
||||
#define GPIO_CAN1_TX GPIO_CAN1_TX_3 /* PD1 */
|
||||
#define GPIO_CAN2_RX GPIO_CAN2_RX_1 /* PB12 */
|
||||
#define GPIO_CAN2_TX GPIO_CAN2_TX_1 /* PB13 */
|
||||
|
||||
/* SPI
|
||||
* SPI1 is sensors1
|
||||
* SPI2 is sensors2
|
||||
* SPI3 is sensors3
|
||||
* SPI4 is Not Used
|
||||
* SPI5 is FRAM
|
||||
* SPI6 is EXTERNAL1
|
||||
*
|
||||
*/
|
||||
|
||||
#define ADJ_SLEW_RATE(p) (((p) & ~GPIO_SPEED_MASK) | (GPIO_SPEED_50MHz))
|
||||
|
||||
#define GPIO_SPI1_MISO ADJ_SLEW_RATE(GPIO_SPI1_MISO_3) /* PG9 */
|
||||
#define GPIO_SPI1_MOSI GPIO_SPI1_MOSI_2 /* PB5 */
|
||||
#define GPIO_SPI1_SCK GPIO_SPI1_SCK_1 /* PA5 */
|
||||
|
||||
#define GPIO_SPI2_MISO ADJ_SLEW_RATE(GPIO_SPI2_MISO_3) /* PI2 */
|
||||
#define GPIO_SPI2_MOSI GPIO_SPI2_MOSI_4 /* PI3 */
|
||||
#define GPIO_SPI2_SCK GPIO_SPI2_SCK_6 /* PI1 */
|
||||
|
||||
#define GPIO_SPI4_MISO ADJ_SLEW_RATE(GPIO_SPI4_MISO_1) /* PE13 */
|
||||
#define GPIO_SPI4_MOSI GPIO_SPI4_MOSI_1 /* PE14 */
|
||||
#define GPIO_SPI4_SCK GPIO_SPI4_SCK_1 /* PE12 */
|
||||
|
||||
#define GPIO_SPI5_MISO ADJ_SLEW_RATE(GPIO_SPI5_MISO_2) /* PH7 */
|
||||
#define GPIO_SPI5_MOSI GPIO_SPI5_MOSI_1 /* PF11 */
|
||||
#define GPIO_SPI5_SCK GPIO_SPI5_SCK_1 /* PF7 */
|
||||
|
||||
#define GPIO_SPI6_MISO ADJ_SLEW_RATE(GPIO_SPI6_MISO_2) /* PA6 */
|
||||
#define GPIO_SPI6_MOSI GPIO_SPI6_MOSI_1 /* PG14 */
|
||||
#define GPIO_SPI6_SCK GPIO_SPI6_SCK_3 /* PB3 */
|
||||
|
||||
/* I2C
|
||||
*
|
||||
* The optional _GPIO configurations allow the I2C driver to manually
|
||||
* reset the bus to clear stuck slaves. They match the pin configuration,
|
||||
* but are normally-high GPIOs.
|
||||
*
|
||||
*/
|
||||
|
||||
#define GPIO_I2C1_SCL GPIO_I2C1_SCL_2 /* PB8 */
|
||||
#define GPIO_I2C1_SDA GPIO_I2C1_SDA_2 /* PB9 */
|
||||
|
||||
#define GPIO_I2C1_SCL_GPIO (GPIO_OUTPUT | GPIO_OPENDRAIN |GPIO_SPEED_50MHz | GPIO_OUTPUT_SET | GPIO_PORTB | GPIO_PIN8)
|
||||
#define GPIO_I2C1_SDA_GPIO (GPIO_OUTPUT | GPIO_OPENDRAIN |GPIO_SPEED_50MHz | GPIO_OUTPUT_SET | GPIO_PORTB | GPIO_PIN9)
|
||||
|
||||
#define GPIO_I2C2_SCL GPIO_I2C2_SCL_2 /* PF1 */
|
||||
#define GPIO_I2C2_SDA GPIO_I2C2_SDA_2 /* PF0 */
|
||||
|
||||
#define GPIO_I2C2_SCL_GPIO (GPIO_OUTPUT | GPIO_OPENDRAIN |GPIO_SPEED_50MHz | GPIO_OUTPUT_SET | GPIO_PORTF | GPIO_PIN1)
|
||||
#define GPIO_I2C2_SDA_GPIO (GPIO_OUTPUT | GPIO_OPENDRAIN |GPIO_SPEED_50MHz | GPIO_OUTPUT_SET | GPIO_PORTF | GPIO_PIN0)
|
||||
|
||||
#define GPIO_I2C3_SCL GPIO_I2C3_SCL_1 /* PA8 */
|
||||
#define GPIO_I2C3_SDA GPIO_I2C3_SDA_2 /* PH8 */
|
||||
|
||||
#define GPIO_I2C3_SCL_GPIO (GPIO_OUTPUT | GPIO_OPENDRAIN |GPIO_SPEED_50MHz | GPIO_OUTPUT_SET | GPIO_PORTA | GPIO_PIN8)
|
||||
#define GPIO_I2C3_SDA_GPIO (GPIO_OUTPUT | GPIO_OPENDRAIN |GPIO_SPEED_50MHz | GPIO_OUTPUT_SET | GPIO_PORTH | GPIO_PIN8)
|
||||
|
||||
#define GPIO_I2C4_SCL GPIO_I2C4_SCL_2 /* PF14 */
|
||||
#define GPIO_I2C4_SDA GPIO_I2C4_SDA_2 /* PF15 */
|
||||
|
||||
#define GPIO_I2C4_SCL_GPIO (GPIO_OUTPUT | GPIO_OPENDRAIN | GPIO_SPEED_50MHz | GPIO_OUTPUT_SET | GPIO_PORTF | GPIO_PIN14)
|
||||
#define GPIO_I2C4_SDA_GPIO (GPIO_OUTPUT | GPIO_OPENDRAIN | GPIO_SPEED_50MHz | GPIO_OUTPUT_SET | GPIO_PORTF | GPIO_PIN15)
|
||||
|
||||
/* SDMMC2
|
||||
*
|
||||
* VDD 3.3
|
||||
* GND
|
||||
* SDMMC2_CK PD6
|
||||
* SDMMC2_CMD PD7
|
||||
* SDMMC2_D0 PB14
|
||||
* SDMMC2_D1 PB15
|
||||
* SDMMC2_D2 PG11
|
||||
* SDMMC2_D3 PB4
|
||||
*/
|
||||
|
||||
#define GPIO_SDMMC2_CK GPIO_SDMMC2_CK_1 /* PD6 */
|
||||
#define GPIO_SDMMC2_CMD GPIO_SDMMC2_CMD_1 /* PD7 */
|
||||
// GPIO_SDMMC2_D0 No Remap /* PB14 */
|
||||
// GPIO_SDMMC2_D1 No Remap /* PB15 */
|
||||
#define GPIO_SDMMC2_D2 GPIO_SDMMC2_D2_1 /* PG11 */
|
||||
// GPIO_SDMMC2_D3 No Remap /* PB4 */
|
||||
|
||||
/* The STM32 H7 connects to a TI DP83848TSQ/NOPB
|
||||
* using RMII
|
||||
*
|
||||
* STM32 H7 BOARD DP83848TSQ/NOPB
|
||||
* GPIO SIGNAL PIN NAME
|
||||
* -------- ------------ -------------
|
||||
* PA7 ETH_CRS_DV CRS_DV
|
||||
* PC1 ETH_MDC MDC
|
||||
* PA2 ETH_MDIO MDIO
|
||||
* PA1 ETH_REF_CL X1
|
||||
* PC4 ETH_RXD0 RX_D0
|
||||
* PC5 ETH_RXD1 RX_D1
|
||||
* PB11 ETH_TX_EN TX_EN
|
||||
* PG13 ETH_TXD0 TX_D0
|
||||
* PG12 ETH_TXD1 TX_D1
|
||||
*
|
||||
* The PHY address is 1, since COL/PHYAD0 features a pull up.
|
||||
*/
|
||||
|
||||
#define GPIO_ETH_RMII_TX_EN GPIO_ETH_RMII_TX_EN_1 /* PB11 */
|
||||
#define GPIO_ETH_RMII_TXD0 GPIO_ETH_RMII_TXD0_2 /* PG13 */
|
||||
#define GPIO_ETH_RMII_TXD1 GPIO_ETH_RMII_TXD1_2 /* PG12 */
|
||||
|
||||
|
||||
/* USB
|
||||
*
|
||||
* OTG_FS_DM PA11
|
||||
* OTG_FS_DP PA12
|
||||
* VBUS PA9
|
||||
*/
|
||||
|
||||
|
||||
/* Board provides GPIO or other Hardware for signaling to timing analyzer */
|
||||
|
||||
#if defined(CONFIG_BOARD_USE_PROBES)
|
||||
# include "stm32_gpio.h"
|
||||
# define PROBE_N(n) (1<<((n)-1))
|
||||
# define PROBE_1 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTI|GPIO_PIN0) /* PI0 AUX1 */
|
||||
# define PROBE_2 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTH|GPIO_PIN12) /* PH12 AUX2 */
|
||||
# define PROBE_3 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTH|GPIO_PIN11) /* PH11 AUX3 */
|
||||
# define PROBE_4 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTH|GPIO_PIN10) /* PH10 AUX4 */
|
||||
# define PROBE_5 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTD|GPIO_PIN13) /* PD13 AUX5 */
|
||||
# define PROBE_6 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTD|GPIO_PIN14) /* PD14 AUX6 */
|
||||
# define PROBE_7 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTH|GPIO_PIN6) /* PH6 AUX7 */
|
||||
# define PROBE_8 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTH|GPIO_PIN9) /* PH9 AUX8 */
|
||||
# define PROBE_9 (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_2MHz|GPIO_OUTPUT_CLEAR|GPIO_PORTE|GPIO_PIN11) /* PE11 CAP1 */
|
||||
|
||||
# define PROBE_INIT(mask) \
|
||||
do { \
|
||||
if ((mask)& PROBE_N(1)) { stm32_configgpio(PROBE_1); } \
|
||||
if ((mask)& PROBE_N(2)) { stm32_configgpio(PROBE_2); } \
|
||||
if ((mask)& PROBE_N(3)) { stm32_configgpio(PROBE_3); } \
|
||||
if ((mask)& PROBE_N(4)) { stm32_configgpio(PROBE_4); } \
|
||||
if ((mask)& PROBE_N(5)) { stm32_configgpio(PROBE_5); } \
|
||||
if ((mask)& PROBE_N(6)) { stm32_configgpio(PROBE_6); } \
|
||||
if ((mask)& PROBE_N(7)) { stm32_configgpio(PROBE_7); } \
|
||||
if ((mask)& PROBE_N(8)) { stm32_configgpio(PROBE_8); } \
|
||||
if ((mask)& PROBE_N(9)) { stm32_configgpio(PROBE_9); } \
|
||||
} while(0)
|
||||
|
||||
# define PROBE(n,s) do {stm32_gpiowrite(PROBE_##n,(s));}while(0)
|
||||
# define PROBE_MARK(n) PROBE(n,false);PROBE(n,true)
|
||||
#else
|
||||
# define PROBE_INIT(mask)
|
||||
# define PROBE(n,s)
|
||||
# define PROBE_MARK(n)
|
||||
#endif
|
||||
|
||||
#endif /*__NUTTX_CONFIG_CUAV_X25_SUPER_INCLUDE_BOARD_H */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user