Compare commits

...

53 Commits

Author SHA1 Message Date
Marco Hauswirth 8542261cab ekf2: implement multi-instance AGP fusion
- Support up to 4 independent AGP sources simultaneously
- Dynamic parameter lookup without boilerplate switch statements
- Per-source state tracking and fusion logic
- Update simulator to use new AuxGlobalPosition message
2025-12-19 14:42:13 +01:00
Marco Hauswirth b9af95903b ekf2: add multi-instance AGP parameters
Add per-instance parameters (ID, CTRL, MODE, DELAY, NOISE, GATE)
for up to 4 auxiliary global position sources.
2025-12-19 14:42:13 +01:00
Marco Hauswirth ec7973e4a2 uorb: add AuxGlobalPosition message
Add dedicated message type for auxiliary global position sources.
Separates aux_global_position from VehicleGlobalPosition topic.
2025-12-19 14:42:12 +01:00
Marco Hauswirth fee7da696d uxrce_dds: support multi-instance uORB topics
Enable DDS bridge to handle multi-instance uORB topics by mapping
multiple topic instances to a single DDS topic with instance field.
2025-12-19 09:06:27 +01:00
Nick c51502781f ekf2: revent yaw spikes on reset (#25972) 2025-12-18 09:08:44 -09:00
Jacob Dahl 339a0b40af airframes: exclude px4_fmu-v6x on 6002_draco_r 2025-12-18 08:52:17 -09:00
Jacob Dahl d2aa1b801c airframes: exclude px4_fmu-v6x on 4052_holybro_qav250 2025-12-18 08:52:17 -09:00
Jacob Dahl 26d847e6e7 airframes: exclude px4_fmu-v6x on 4016_holybro_px4vision 2025-12-18 08:52:17 -09:00
Jacob Dahl 1cf2dc8791 airframes: exclude px4_fmu-v6x on 17002_TF-AutoG2 2025-12-18 08:52:17 -09:00
Jacob Dahl 209a9935e7 airframes: exclude px4_fmu-v6x on 17003_TF-G2 2025-12-18 08:52:17 -09:00
Jacob Dahl 0fa667fd92 airframes: exclude px4_fmu-v6x on 4053_holybro_kopis2 (uses Kakute FC) 2025-12-18 08:52:17 -09:00
Marco Hauswirth bbf32a537e EKF2: Improve Manual Position Reset Handling (#25885)
* reset by fusion:
* state correction with tiny observation variance
* covariance matrix upate with correct observation variance

* reset wind to 0 on hard-reset during global-position-reset
increase gate

* adjust unittest: velocity gets now reset on resetGlobalPosToExternalObservation
2025-12-18 15:21:09 +01:00
Matthias Grob 3e425210e0 Hysteresis: enable initializing arrays with default constructor initial state false 2025-12-18 13:29:40 +01:00
Matthias Grob f25997a15b RoverLandDetector: move static hysteresis configuration to constructor 2025-12-18 13:29:40 +01:00
Matthias Grob e132568430 FixedWingLandDetector: remove useless 0 delay away from landed 2025-12-18 13:29:40 +01:00
bresch 6195629373 ekf2: add test for external wind reset 2025-12-18 11:37:10 +01:00
bresch 2c044b327e ekf2: refactor airspeed starting logic
- avoids using invalid velocity estimate to reset wind
- do not set fusion flags if starting was impossible
- reset wind to 0 before resetting velocity using airspeed if wind data
  is outdated
2025-12-18 11:37:10 +01:00
Hamish Willee b7ffd6ea2c uORB doc standard - add page (#25878) 2025-12-18 17:08:41 +11:00
Jacob Dahl b26db22c1a docs: ark_flow: add EKF2_RNG_CTRL (#26130) 2025-12-18 14:21:24 +11:00
Hamish Willee 6f18ff8ff9 Add copilot reviewer instructions for docs (#26141) 2025-12-18 14:07:05 +11:00
jobs f224b81eec boards: modify vendor ID NarinFC-H7(#26117)
- CONFIG_CDCACM_PRODUCTID : 0x0047
  - CONFIG_CDCACM_VENDORID : 0x3fc5
2025-12-17 21:46:56 -05:00
isentek-webbyeh b537601b7a driver: ist8310: add IST8310J device ID support (#26134)
* driver: ist8310: add IST8310J device ID support and cross-axis compensation

IST8310J shares the same register map and initialization sequence as IST8310.

This change extends the existing IST8310 driver to:
- Accept the IST8310J device ID during probe and reset
- Load factory cross-axis calibration data from OTP
- Apply cross-axis compensation to raw magnetometer samples

The cross-axis compensation corrects factory axis misalignment only and
does not replace PX4 runtime magnetometer calibration.

Tested on Raspberry Pi using I2C with both IST8310 and IST8310J devices.

Signed-off-by: webbyeh <webbyeh@isentek.com>

* driver: ist8310: add IST8310J device ID support

IST8310J shares the same register map and initialization sequence as IST8310.

Factory cross-axis compensation support was evaluated but has been removed
in this revision due to flash size constraints on embedded targets. The
driver now focuses on device identification and stability, while relying on
the existing PX4 magnetometer calibration framework.

This commit also addresses review feedback by caching the WAI register value
to avoid redundant I2C reads during the reset wait state.

Tested on Raspberry Pi using I2C with both IST8310 and IST8310J devices.

Signed-off-by: webbyeh <webbyeh@isentek.com>

* Fix formatting issues in IST8310.cpp

---------

Signed-off-by: webbyeh <webbyeh@isentek.com>
Co-authored-by: Jacob Dahl <37091262+dakejahl@users.noreply.github.com>
2025-12-17 17:14:17 -09:00
Patrik Dominik Pordi e01c4b0692 Ark esc docs + restructure top level ESC docs (#26132) 2025-12-18 10:28:34 +11:00
Jacob Dahl ca43fc8f5a workflows: remove stale comment to reduce notification spam (#26140) 2025-12-17 12:39:54 -09:00
Jacob Dahl 11cac778ba ark: fpv: use same magnetometer subset as v6x 2025-12-17 11:13:42 -09:00
Jacob Dahl 441892b33f ark: v6x: remove MMC5983MA driver to save flash 2025-12-17 10:53:47 -09:00
Jacob Dahl 1d5a8531e4 update gps submodule 2025-12-17 10:33:21 -09:00
Jacob Dahl e71348967d gps: fix RTCM injection and enable MSM7 for PPK (#26095)
* serial: add txSpaceAvailable function

* serial: txSpaceAvailable and bytesAvailable fixups

* msg: GpsDump: increase queue from 8 to 16 and replace instance with device_id

* lib: gnss: add RTCM parsing library. Generated by Claude Code.

* gps: fix RTCM injection to use inject-before-read pattern as before. Add RTCM parser to frame-align injection. Drain GpsInjectData uORB queue into RTCM parser buffer and then inject. Add GPS_UBX_PPK parameter to enable MSM7 output from the GPS module (rather than the default of MSM4) which is required for PPK workflows.

* gps: replace PX4_WARN with perf counters
2025-12-17 10:20:00 -09:00
Jacob Dahl fbe49db571 uavcannode: publisher: MovingBaseLine enhancements (#26092)
* uavcannode: publishers: MovingBaselineData: publish all GpsInjectData updates during BroadcastAnyUpdates. Check and report data loss via uorb generationcounter. Only registerCallback outside of the loop.

* remove unnecessary include
2025-12-17 10:18:17 -09:00
Jacob Dahl 4b36cfccfc uavcan: gnss: MovingBaselineData subscriber (#26094)
* msg: GpsDump: increase queue from 8 to 16 and replace instance with device_id

* uavcan: gnss: MovingBaselineData subscriber for RTCM logging for PPK. Add uORB generation tracking to detect data loss on RTCM injection.

* add instance TODO
2025-12-17 10:17:56 -09:00
Jacob Dahl 699ec30c9c logger: gps_dump non-optional (#26096) 2025-12-17 09:59:38 -09:00
Jacob Dahl e6c49edd20 docs: battery: clarify BAT${i}_SOURCE parameter documentation (#26071)
* docs: battery: clarify Power Module option to state that it works additionally for onboard Analog

* Update src/lib/battery/module.yaml

Co-authored-by: Hamish Willee <hamishwillee@gmail.com>

* Update src/lib/battery/module.yaml

Co-authored-by: Hamish Willee <hamishwillee@gmail.com>

* Update src/lib/battery/module.yaml

* Apply suggestions from code review

* Update msg/versioned/BatteryStatus.msg

---------

Co-authored-by: Hamish Willee <hamishwillee@gmail.com>
2025-12-17 09:58:41 -09:00
Alexander Lerach cb6bdee4c2 boards: v6x, remove MS5611 driver to save flash (#26138) 2025-12-17 09:57:34 -09:00
Phil-Engljaehringer 3438d593a1 drivers: MCP23009 & MCP23017 shared code base (#25924)
* Implemented driver for MCP23017

* fixed compatability with mcp23009. (naming of instantiated GPIO-Devices)

* removed some comments

* removed even more comments

* commented out instatntiation of driver since it will not be used with v6x

* removed last useless comments

* re-activated gpio_mcp23009 driver, removed useless comments and empty lines

* removed empty lines at the end of mcp23017.cpp

* removed empty line

* Implemented driver for MCP23017

* fixed compatability with mcp23009. (naming of instantiated GPIO-Devices)

* removed some comments

* removed even more comments

* commented out instatntiation of driver since it will not be used with v6x

* removed last useless comments

* re-activated gpio_mcp23009 driver, removed useless comments and empty lines

* removed empty lines at the end of mcp23017.cpp

* removed empty line

* basic working implementation

* first improved driver version with shared code base for MCP23009 & MCP23017 (built as state machine with sanity checks)

* removed unused imports

* changed module name from MCP to MCP230XX

* removed debug print statements

* adjusted auto start command of driver

* removed comments

* -added seperate main functions for both derivative modules (mcp23009 and mcp23017)

* compile common functions as shared library in src/lib/drivers

* fixed cleanup of modules

* ->unclean working version with shared common library

* used make format

* working & cleaned version

* -> Added CallbackHandler to be able to use multiple reading GPIO-expanders simultaneously
-> Removed old mcap23009 calls and pin registrations
-> Adjusted GpioIn.msg to contain MAX_INSTANCES

* ->removed unused imports
->used make format

* Fix: Re-enabled platform_mcp_gpio for accton-godwit, cuav, fmu-v5x

* Fix: enabled platform_mcp_gpio in px4/fmu-v5x

* added depency to fmu-v5x

* Fix: removed new lines

* Fix: fixed linker errors

* removed unused linkage against mcp-library

* Made mcp start calls consistent for fmu-v5x and fmu-v6x

* moved logging of comm errors to read/write function directly

* added perf_count for sanity_check

* removed error message

* ensured member variables follow naming convention

* added retries to probe function

* simplyfied state-logic

* add break to terminate loop early

* ensured clean state when register_gpios() fails

* add registered-flag to pins

* used path from top dir instead of relative path in CMakeLists

* used constexpr to set parameters instead of calculating them at runtime

* style: used make format

* fix: corrected i2c_bus assignment

* style: init input of callbackhandler to 0

* fix: mark pin as registered if successful

* style: made arguments const type

---------

Co-authored-by: Alexander Lerach <alexander@auterion.com>
2025-12-17 17:48:30 +01:00
dirksavage88 38f89a8b69 remove extraneous newlines
Signed-off-by: dirksavage88 <dirksavage88@gmail.com>
2025-12-17 17:12:12 +01:00
dirksavage88 3e290695ef fix to check q
Signed-off-by: dirksavage88 <dirksavage88@gmail.com>
2025-12-17 17:12:12 +01:00
Andrew Brahim 11bf3cffde Update src/lib/drivers/rangefinder/PX4Rangefinder.hpp
Co-authored-by: Jacob Dahl <37091262+dakejahl@users.noreply.github.com>
2025-12-17 17:12:12 +01:00
dirksavage88 ba4437ae60 revert to passing raw pointer and length
Signed-off-by: dirksavage88 <dirksavage88@gmail.com>
2025-12-17 17:12:12 +01:00
dirksavage88 b64860f9f8 consolidated into update method; use default value in declaration
Signed-off-by: dirksavage88 <dirksavage88@gmail.com>
2025-12-17 17:12:12 +01:00
dirksavage88 e51d09612f vl53l1x quaternion example
Signed-off-by: dirksavage88 <dirksavage88@gmail.com>
2025-12-17 17:12:12 +01:00
dirksavage88 eb1c0322e3 rangefinder update quaternion
Signed-off-by: dirksavage88 <dirksavage88@gmail.com>
2025-12-17 17:12:12 +01:00
Alexander Lerach e822da74b2 io timer: fix input capture on various boards 2025-12-17 17:02:13 +01:00
bresch ccaad82f61 mc_att: remove dependency to heading_god_for_control
Absolute heading is not required in stabilized mode and a change in
heading convergence is already handled by the StickYaw class using
unaided_heading
2025-12-17 16:59:47 +01:00
Hamish Willee 4e5c1140b7 [docs] CAN - update wiring/multi-CAN port details (#26080) 2025-12-17 16:57:00 +11:00
PX4 Build Bot 4acb8ec799 New Crowdin translations - zh-CN (#26111)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-12-17 16:13:51 +11:00
PX4 Build Bot 89d43185c7 New Crowdin translations - ko (#26109)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-12-17 16:13:35 +11:00
PX4 Build Bot df0a197050 New Crowdin translations - uk (#26110)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-12-17 16:13:23 +11:00
Jacob Dahl c726c7e4f4 px4: v6x: mavlink-dev: remove UXRCE DDS client to free up flash (#26131) 2025-12-17 14:17:57 +13:00
Claudio Chies 24d06047bd UAVCAN: Add device tracking and information publishing (#25617)
* uavcan: collect node info and publish every second

* UORB: Add DeviceInformation Message

Format DeviceInformation.msg with standard comment spaces

* SENS: add getter for device_id

* UAVCAN: add publishing of DeviceInformation based on publised message type, and Node Information

* LOG: add deviceInformation

* MSG:BAT: fix comment to be inline with the max_instaces

* UAVCAN: DeviceInformation, incorporated feedback

* UAVCAN: DeviceInformation, incorporated feedback

* UAVCAN: DeviceInformation, Fixed bug with Powermonitor

---------

Co-authored-by: Beat Küng <beat-kueng@gmx.net>
2025-12-16 14:53:44 -09:00
Pedro Roque cb682006fe fix: esc status sizing fix for gz simulation
* init: working towards dual-action ATMOS

* fix: update gz sim to latest

* fix: add motor number max fitting Actuator

* fix: revert non-necessary changes

* fix: ensure esc count does not exceed maximum number of ESCs

* fix: remove extra modules

* fix: sync submodules with remote

* fix: sync with main
2025-12-16 15:46:51 -08:00
Jacob Dahl 5632728467 lib: gnss: add RTCM parsing library (#26093)
* lib: gnss: add RTCM parsing library. Generated by Claude Code.

* lib: gnss: rtcm: use rtcm3_payload_length()

* lib: gnss: rtcm: set header year

* lib: gnss: rtcm: add units tests

* Update src/lib/gnss/rtcm.h

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/lib/gnss/CMakeLists.txt

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/lib/gnss/rtcm.h

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* remove mention of reset()

* lib: gnss: rtcm: more effecient preamble search

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-16 09:28:10 -09:00
Jacob Dahl 778ad160f2 msg: GpsDump: queue 8->16 and add device_id (#26091)
* msg: GpsDump: increase queue from 8 to 16 and replace instance with device_id

* gps: add back instance
2025-12-16 08:36:02 -09:00
Marco Hauswirth 8393f46100 Ekf2 add jamming to gnss checks (#26085)
* add jamming check to gnss checks

* keep original order of gnss_check params for default backwards compability
2025-12-16 10:10:37 +01:00
183 changed files with 4670 additions and 1151 deletions
@@ -0,0 +1,31 @@
---
applyTo: "docs/en/**"
---
# Review Guidelines docs/en Tree
## File System & Structure
- **Naming:** Use `lowercase_with_underscores` for all filenames. No spaces.
- **Hierarchy:** Markdown files must reside exactly in a first-level category folder.
- Valid: `docs/en/category/file.md`
- Invalid: `docs/en/category/subcategory/file.md`
- **Text Files:** Any `.txt` or `.text` files must start with an underscore (e.g., `_notes.txt`).
- **Assets:** All images/non-docs must be in `/docs/assets/`. Deep nesting is permitted here.
- **Formats:** Prefer **SVG** for diagrams and **PNG** for screenshots. Flag JPG files.
## Markdown & Style
- **Headings:** Use Title Case ("First Letter Capitalisation").
- The Page Title must be the only H1 (`#`). All others must be `##` or lower.
- 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`).
- **Structure:** End every line at the end of a sentence (Semantic Line Breaks).
## Quality Control
- **Formatting:** Ensure Prettier rules have been applied.
- **Language:** Enforce **UK English** spelling and grammar.
+4 -4
View File
@@ -15,7 +15,7 @@ jobs:
stale-issue-label: 'stale'
stale-pr-label: 'stale'
remove-stale-when-updated: true
stale-issue-message: 'This issue has been marked as stale due to 90 days of inactivity. If no further activity occurs, it will be automatically closed in 30 days. Please leave a comment, add a reaction, make an update, or remove the stale label if youd like to keep it open.'
close-issue-message: 'This issue has been closed due to prolonged inactivity after being marked as stale. If you believe this was closed in error or the topic is still relevant, please feel free to reopen it or create a new issue.'
stale-pr-message: 'This PR was identified as stale and it will be closed in 30 days unless any activity is detected.'
close-pr-message: 'This pull request has been closed after being marked as stale with no further activity. Thank you for the time and effort you put into this contribution. If youd like to continue the discussion or update the work, please feel free to reopen it or submit a new PR.'
stale-issue-message: ''
close-issue-message: 'This issue has been automatically closed due to 120 days of inactivity. If you believe this is still relevant, please feel free to reopen it or create a new issue.'
stale-pr-message: ''
close-pr-message: 'This pull request has been automatically closed due to 120 days of inactivity. If you would like to continue, please feel free to reopen it or submit a new PR.'
@@ -18,6 +18,7 @@
#
# @board px4_fmu-v2 exclude
# @board bitcraze_crazyflie exclude
# @board px4_fmu-v6x exclude
#
. ${R}etc/init.d/rc.fw_defaults
@@ -16,6 +16,7 @@
#
# @board px4_fmu-v2 exclude
# @board bitcraze_crazyflie exclude
# @board px4_fmu-v6x exclude
#
. ${R}etc/init.d/rc.fw_defaults
@@ -7,6 +7,7 @@
#
# @board px4_fmu-v2 exclude
# @board bitcraze_crazyflie exclude
# @board px4_fmu-v6x exclude
#
. ${R}etc/init.d/rc.mc_defaults
@@ -11,6 +11,7 @@
#
# @board px4_fmu-v2 exclude
# @board bitcraze_crazyflie exclude
# @board px4_fmu-v6x exclude
#
. ${R}etc/init.d/rc.mc_defaults
@@ -9,6 +9,7 @@
#
# @board px4_fmu-v2 exclude
# @board bitcraze_crazyflie exclude
# @board px4_fmu-v6x exclude
#
. ${R}etc/init.d/rc.mc_defaults
@@ -12,6 +12,7 @@
# @board px4_fmu-v4pro exclude
# @board px4_fmu-v5 exclude
# @board px4_fmu-v5x exclude
# @board px4_fmu-v6x exclude
# @board bitcraze_crazyflie exclude
#
@@ -71,6 +71,5 @@ else()
nuttx_arch # sdio
nuttx_drivers # sdio
px4_layer
platform_gpio_mcp23009
)
endif()
-8
View File
@@ -74,7 +74,6 @@
#include <px4_platform/gpio.h>
#include <px4_platform/board_determine_hw_info.h>
#include <px4_platform/board_dma_alloc.h>
#include <px4_platform/gpio/mcp23009.hpp>
/****************************************************************************
* Pre-Processor Definitions
@@ -286,13 +285,6 @@ __EXPORT int board_app_initialize(uintptr_t arg)
# endif /* CONFIG_MMCSD */
ret = mcp23009_register_gpios(3, 0x25);
if (ret != OK) {
led_on(LED_RED);
return ret;
}
#endif /* !defined(BOOTLOADER) */
return OK;
-1
View File
@@ -35,7 +35,6 @@ CONFIG_DRIVERS_MAGNETOMETER_ISENTEK_IST8310=y
CONFIG_DRIVERS_MAGNETOMETER_LIS3MDL=y
CONFIG_DRIVERS_MAGNETOMETER_LSM303AGR=y
CONFIG_DRIVERS_MAGNETOMETER_RM3100=y
CONFIG_DRIVERS_MAGNETOMETER_MEMSIC_MMC5983MA=y
CONFIG_DRIVERS_MAGNETOMETER_ST_IIS2MDC=y
CONFIG_DRIVERS_POWER_MONITOR_INA226=y
CONFIG_DRIVERS_POWER_MONITOR_INA228=y
+9 -1
View File
@@ -19,7 +19,15 @@ CONFIG_DRIVERS_HEATER=y
CONFIG_DRIVERS_IMU_INVENSENSE_IIM42653=y
CONFIG_DRIVERS_IMU_MURATA_SCH16T=y
CONFIG_COMMON_LIGHT=y
CONFIG_COMMON_MAGNETOMETER=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_OSD_MSP_OSD=y
CONFIG_DRIVERS_PCA9685_PWM_OUT=y
CONFIG_DRIVERS_PWM_OUT=y
@@ -20,9 +20,9 @@ safety_button start
if ver hwbasecmp 009 010 011
then
# No USB
mcp23009 start -b 3 -X -D 0xf0 -O 0xf0 -P 0x0f -U 10
mcp23009 start -b 3 -X -D 0xf0 -O 0xf0 -P 0x0f -M 0 -U 10
fi
if ver hwbasecmp 00a 008
then
mcp23009 start -b 3 -X -D 0xf1 -O 0xf0 -P 0x0f -U 10
mcp23009 start -b 3 -X -D 0xf1 -O 0xf0 -P 0x0f -M 0 -U 10
fi
@@ -86,8 +86,5 @@ fi
#external baro
bmp388 -X start
# Don't try to start external baro on I2C3 as it can conflict with the MS5525DSO airspeed sensor.
#ms5611 -X start
unset INA_CONFIGURED
unset HAVE_PM2
+1 -1
View File
@@ -69,6 +69,6 @@ else()
nuttx_arch # sdio
nuttx_drivers # sdio
px4_layer
platform_gpio_mcp23009
platform_gpio_mcp
)
endif()
-8
View File
@@ -74,7 +74,6 @@
#include <px4_platform/gpio.h>
#include <px4_platform/board_determine_hw_info.h>
#include <px4_platform/board_dma_alloc.h>
#include <px4_platform/gpio/mcp23009.hpp>
/****************************************************************************
* Pre-Processor Definitions
@@ -282,13 +281,6 @@ __EXPORT int board_app_initialize(uintptr_t arg)
# endif /* CONFIG_MMCSD */
ret = mcp23009_register_gpios(3, 0x25);
if (ret != OK) {
led_on(LED_RED);
return ret;
}
#endif /* !defined(BOOTLOADER) */
return OK;
-1
View File
@@ -71,6 +71,5 @@ else()
nuttx_arch # sdio
nuttx_drivers # sdio
px4_layer
platform_gpio_mcp23009
)
endif()
-8
View File
@@ -74,7 +74,6 @@
#include <px4_platform/gpio.h>
#include <px4_platform/board_determine_hw_info.h>
#include <px4_platform/board_dma_alloc.h>
#include <px4_platform/gpio/mcp23009.hpp>
/****************************************************************************
* Pre-Processor Definitions
@@ -286,13 +285,6 @@ __EXPORT int board_app_initialize(uintptr_t arg)
# endif /* CONFIG_MMCSD */
ret = mcp23009_register_gpios(3, 0x25);
if (ret != OK) {
led_on(LED_RED);
return ret;
}
#endif /* !defined(BOOTLOADER) */
return OK;
@@ -13,7 +13,7 @@ CONFIG_ARCH="arm"
CONFIG_ARCH_BOARD_CUSTOM=y
CONFIG_ARCH_BOARD_CUSTOM_DIR="../../../../boards/narinfc/h7/nuttx-config"
CONFIG_ARCH_BOARD_CUSTOM_DIR_RELPATH=y
CONFIG_ARCH_BOARD_CUSTOM_NAME="PX4 NarinFC-H7"
CONFIG_ARCH_BOARD_CUSTOM_NAME="px4"
CONFIG_ARCH_CHIP="stm32h7"
CONFIG_ARCH_CHIP_STM32H743XI=y
CONFIG_ARCH_CHIP_STM32H7=y
@@ -31,11 +31,11 @@ CONFIG_BOARD_LOOPSPERMSEC=22114
CONFIG_BOARD_RESET_ON_ASSERT=2
CONFIG_CDCACM=y
CONFIG_CDCACM_IFLOWCONTROL=y
CONFIG_CDCACM_PRODUCTID=0x004c
CONFIG_CDCACM_PRODUCTSTR="PX4 BL NarinFC H7"
CONFIG_CDCACM_PRODUCTID=0x0047
CONFIG_CDCACM_PRODUCTSTR="PX4 BL VOLOLAND NarinFC-H7"
CONFIG_CDCACM_RXBUFSIZE=600
CONFIG_CDCACM_TXBUFSIZE=12000
CONFIG_CDCACM_VENDORID=0x3163
CONFIG_CDCACM_VENDORID=0x3fc5
CONFIG_CDCACM_VENDORSTR="VOLOLAND"
CONFIG_DEBUG_FULLOPT=y
CONFIG_DEBUG_SYMBOLS=y
+2 -2
View File
@@ -29,9 +29,9 @@ safety_button start
if ver hwbasecmp 009
then
# No USB
mcp23009 start -b 3 -X -D 0xf0 -O 0xf0 -P 0x0f -U 10
mcp23009 start -b 3 -X -D 0xf0 -O 0xf0 -P 0x0f -M 0 -U 10
fi
if ver hwbasecmp 00a 008
then
mcp23009 start -b 3 -X -D 0xf1 -O 0xf0 -P 0x0f -U 10
mcp23009 start -b 3 -X -D 0xf1 -O 0xf0 -P 0x0f -M 0 -U 10
fi
+1 -3
View File
@@ -43,8 +43,6 @@ add_library(drivers_board
usb.c
)
add_dependencies(drivers_board platform_gpio_mcp23009)
target_link_libraries(drivers_board
PRIVATE
arch_io_pins
@@ -54,5 +52,5 @@ target_link_libraries(drivers_board
nuttx_arch # sdio
nuttx_drivers # sdio
px4_layer
platform_gpio_mcp23009
platform_gpio_mcp
)
-8
View File
@@ -74,7 +74,6 @@
#include <px4_platform/gpio.h>
#include <px4_platform/board_determine_hw_info.h>
#include <px4_platform/board_dma_alloc.h>
#include <px4_platform/gpio/mcp23009.hpp>
/****************************************************************************
* Pre-Processor Definitions
@@ -280,12 +279,5 @@ __EXPORT int board_app_initialize(uintptr_t arg)
#endif /* CONFIG_MMCSD */
ret = mcp23009_register_gpios(3, 0x25);
if (ret != OK) {
led_on(LED_RED);
return ret;
}
return OK;
}
-1
View File
@@ -12,7 +12,6 @@ CONFIG_DRIVERS_ADC_ADS1115=y
CONFIG_DRIVERS_ADC_BOARD_ADC=y
CONFIG_DRIVERS_BAROMETER_BMP388=y
CONFIG_DRIVERS_BAROMETER_INVENSENSE_ICP201XX=y
CONFIG_DRIVERS_BAROMETER_MS5611=y
CONFIG_DRIVERS_CAMERA_CAPTURE=y
CONFIG_DRIVERS_CAMERA_TRIGGER=y
CONFIG_DRIVERS_CDCACM_AUTOSTART=y
-3
View File
@@ -146,8 +146,5 @@ else
bmp388 -X start
fi
# Don't try to start external baro on I2C3 as it can conflict with the MS5525DSO airspeed sensor.
#ms5611 -X start
unset INA_CONFIGURED
unset HAVE_PM2
+1
View File
@@ -1 +1,2 @@
CONFIG_MAVLINK_DIALECT="development"
CONFIG_MODULES_UXRCE_DDS_CLIENT=n
@@ -54,6 +54,7 @@ CONFIG_DEBUG_MEMFAULT=y
CONFIG_DEBUG_SYMBOLS=y
CONFIG_DEBUG_TCBINFO=y
CONFIG_DEV_FIFO_SIZE=0
CONFIG_DEV_GPIO=y
CONFIG_DEV_PIPE_SIZE=70
CONFIG_DEV_URANDOM=y
CONFIG_ETH0_PHY_MULTI=y
Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

+7 -4
View File
@@ -312,15 +312,17 @@
- [Actuator Allocation](config/actuators.md)
- [ESC Calibration](advanced_config/esc_calibration.md)
- [ESCs & Motors](peripherals/esc_motors.md)
- [ESC Protocols](esc/esc_protocols.md)
- [PWM ESCs and Servos](peripherals/pwm_escs_and_servo.md)
- [DShot ESCs](peripherals/dshot.md)
- [OneShot ESCs and Servos](peripherals/oneshot.md)
- [DroneCAN ESCs](dronecan/escs.md)
- [Zubax Telega](dronecan/zubax_telega.md)
- [PX4 Sapog ESC Firmware](dronecan/sapog.md)
- [Holybro Kotleta](dronecan/holybro_kotleta.md)
- [Vertiq](peripherals/vertiq.md)
- [VESC](peripherals/vesc.md)
- [ARK 4IN1 ESC](esc/ark_4in1_esc.md)
- [Holybro Kotleta](dronecan/holybro_kotleta.md)
- [Vertiq Motor/ESC Modules](peripherals/vertiq.md)
- [VESC Project ESCs](peripherals/vesc.md)
- [Zubax Telega ESCs](dronecan/zubax_telega.md)
- [Radio Control (RC)](getting_started/rc_transmitter_receiver.md)
- [Radio Setup](config/radio.md)
- [Flight Modes](config/flight_mode.md)
@@ -499,6 +501,7 @@
- [PPS Time Synchronization](advanced/pps_time_sync.md)
- [Middleware](middleware/index.md)
- [uORB Messaging](middleware/uorb.md)
- [uORB Docs Standard](uorb/uorb_documentation.md)
- [uORB Graph](middleware/uorb_graph.md)
- [uORB Message Reference](msg_docs/index.md)
- [Versioned](msg_docs/versioned_messages.md)
+41 -6
View File
@@ -1,7 +1,13 @@
# CAN
# CAN (DroneCAN & Cyphal)
[Controller Area Network (CAN)](https://en.wikipedia.org/wiki/CAN_bus) is a robust wired network that allows drone components such as flight controller, ESCs, sensors, and other peripherals, to communicate with each other.
Because it is designed to be democratic and uses differential signaling, it is very robust even over longer cable lengths (on large vehicles), and avoids a single point of failure.
It is particularly recommended on larger vehicles.
## Overview
CAN it is designed to be democratic and uses differential signaling.
For this reason it is very robust even over longer cable lengths (on large vehicles), and avoids a single point of failure.
CAN also allows status feedback from peripherals and convenient firmware upgrades over the bus.
PX4 supports two software protocols for communicating with CAN devices:
@@ -18,29 +24,35 @@ In 2022 the project split into two: the original version of UAVCAN (UAVCAN v0) w
The differences between the two protocols are outlined in [Cyphal vs. DroneCAN](https://forum.opencyphal.org/t/cyphal-vs-dronecan/1814).
:::
:::warning
PX4 does not support other CAN software protocols for drones such as KDECAN (at time of writing).
:::
## Wiring
The wiring for CAN networks is the same for both DroneCAN and Cyphal/CAN (in fact, for all CAN networks).
Devices are connected in a chain in any order.
Devices within a network are connected in a _daisy-chain_ in any order (this differs from UARTs peripherals, where you attach just one component per port).
:::warning Don't connect each CAN peripheral to a separate CAN port!
Unlike UARTs, CAN peripherals are designed to be daisy chained, with additional ports such as `CAN2` used for [redundancy](redundancy).
:::
At either end of the chain, a 120Ω termination resistor should be connected between the two data lines.
Flight controllers and some GNSS modules have built in termination resistors for convenience, thus should be placed at opposite ends of the chain.
Otherwise, you can use a termination resistor such as [this one from Zubax Robotics](https://shop.zubax.com/products/uavcan-micro-termination-plug?variant=6007985111069), or solder one yourself if you have access to a JST-GH crimper.
The following diagram shows an example of a CAN bus connecting a flight controller to 4 CAN ESCs and a GNSS.
It includes a redundant bus connected to `CAN 2`.
![CAN Wiring](../../assets/can/uavcan_wiring.svg)
The diagram does not show any power wiring.
Refer to your manufacturer instructions to confirm whether components require separate power or can be powered from the CAN bus itself.
::: info
For more information, see [Cyphal/CAN device interconnection](https://wiki.zubax.com/public/cyphal/CyphalCAN-device-interconnection?pageId=2195476) (kb.zubax.com).
While the article is written with the Cyphal protocol in mind, it applies equally to DroneCAN hardware and any other CAN setup.
For more advanced scenarios, consult with [On CAN bus topology and termination](https://forum.opencyphal.org/t/on-can-bus-topology-and-termination/1685).
:::
### Connectors
@@ -54,7 +66,30 @@ However, as long as the device firmware supports DroneCAN or Cyphal, it can be u
DroneCAN and Cyphal/CAN support using a second (redundant) CAN interface.
This is completely optional but increases the robustness of the connection.
All Pixhawk flight controllers come with 2 CAN interfaces; if your peripherals support 2 CAN interfaces as well, it is recommended to wire both up for increased safety.
Pixhawk flight controllers come with 2 CAN interfaces; if your peripherals support 2 CAN interfaces as well, it is recommended to wire both up for increased safety.
### Flight Controllers with Multiple CAN Ports
[Flight Controllers](../flight_controller/index.md) may have up to three independent CAN ports, such as `CAN1`, `CAN2`, `CAN3` (neither DroneCAN or Cyphal support more than three).
Note that you can't have both DroneCAN and Cyphal running on PX4 at the same time.
::: tip
You only _need_ one CAN port to support an arbitrary number of CAN devices using a particular CAN protocol.
Don't connect each CAN peripheral to a separate CAN port!
:::
Generally you'll daisy all CAN peripherals off a single port, and if there is more than one CAN port, use the second one for [redundancy](redundancy).
If three are three ports, you might use the remaining network for devices that support another CAN protocol.
The documentation for your flight controller should indicate which ports are supported/enabled.
At runtime you can check what DroneCAN ports are enabled and their status using the following command on the [MAVLink Shell](../debug/mavlink_shell.md) (or some other console):
```sh
uavcan status
```
Note that you can also check the number of supported CAN interfaces for a board by searching for `CONFIG_BOARD_UAVCAN_INTERFACES` in its [default.px4board](https://github.com/PX4/PX4-Autopilot/blob/main/boards/px4/fmu-v6xrt/default.px4board#) configuration file.
## Firmware
+1
View File
@@ -95,6 +95,7 @@ Set the following parameters in _QGroundControl_:
- To optionally disable GPS aiding, set [EKF2_GPS_CTRL](../advanced_config/parameter_reference.md#EKF2_GPS_CTRL) to `0`.
- Enable [UAVCAN_SUB_FLOW](../advanced_config/parameter_reference.md#UAVCAN_SUB_FLOW).
- Enable [UAVCAN_SUB_RNG](../advanced_config/parameter_reference.md#UAVCAN_SUB_RNG).
- Set [EKF2_RNG_CTRL](../advanced_config/parameter_reference.md#EKF2_RNG_CTRL) to `1`.
- Set [EKF2_RNG_A_HMAX](../advanced_config/parameter_reference.md#EKF2_RNG_A_HMAX) to `10`.
- Set [EKF2_RNG_QLTY_T](../advanced_config/parameter_reference.md#EKF2_RNG_QLTY_T) to `0.2`.
- Set [UAVCAN_RNG_MIN](../advanced_config/parameter_reference.md#UAVCAN_RNG_MIN) to `0.08`.
+1
View File
@@ -92,6 +92,7 @@ Set the following parameters in _QGroundControl_:
- To optionally disable GPS aiding, set [EKF2_GPS_CTRL](../advanced_config/parameter_reference.md#EKF2_GPS_CTRL) to `0`.
- Enable [UAVCAN_SUB_FLOW](../advanced_config/parameter_reference.md#UAVCAN_SUB_FLOW).
- Enable [UAVCAN_SUB_RNG](../advanced_config/parameter_reference.md#UAVCAN_SUB_RNG).
- Set [EKF2_RNG_CTRL](../advanced_config/parameter_reference.md#EKF2_RNG_CTRL) to `1`.
- Set [EKF2_RNG_A_HMAX](../advanced_config/parameter_reference.md#EKF2_RNG_A_HMAX) to `10`.
- Set [EKF2_RNG_QLTY_T](../advanced_config/parameter_reference.md#EKF2_RNG_QLTY_T) to `0.2`.
- Set [UAVCAN_RNG_MIN](../advanced_config/parameter_reference.md#UAVCAN_RNG_MIN) to `0.08`.
+58
View File
@@ -0,0 +1,58 @@
# ARK 4IN1 ESC (with/without Connectors)
4 in 1 Electronic Speed Controller (ESC) that is made in the USA, NDAA compliant, and DIU Blue Framework listed.
The ESC comes in variants without connectors that you can solder in place, and a variant that has built-in motor and battery connectors (no soldering required).
![ARK 4IN1 ESC without connectors ](../../assets/hardware/esc/ark/ark_4_in_1_esc.jpg)![ARK 4IN1 ESC with connectors](../../assets/hardware/esc/ark/ark_4_in_1_esc_with_connectors.jpg)
## Where to Buy
Order this module from:
- [4IN1 ESC (with connectors)](https://arkelectron.com/product/ark-4in1-esc/) (ARK Electronics - US)
- [ARK Electronics (without connectors)](https://arkelectron.com/product/ark-4in1-esc-cons/) (ARK Electronics US)
## Hardware Specifications
- Battery Voltage: 3-8s
- 6V Minimum
- 65V Absolute Maximum
- Current Rating: 50A Continuous, 75A Burst Per Motor
- [STM32F0](https://www.st.com/en/microcontrollers-microprocessors/stm32f0-series.html)
- [AM32 Firmware](https://github.com/am32-firmware/AM32/pull/27)
- Onboard Current Sensor, Serial Telemetry
- 100V/A
- Input Protocols
- DShot (300, 600)
- Bi-directional DShot
- KISS Serial Telemetry
- PWM
- 8 Pin JST-SH Input/Output
- 10 Pin JST-SH Debug
- Motor & Battery Connectors (with-connector version)
- MR30 Connector Limit Per Motor: 30A Continuous, 40A Burst
- Four MR30 Motor Connectors
- Dimensions (with connectors)
- Size: 77.00mm x 42.00mm x 9.43mm
- Mounting Pattern: 30.5mm
- Weight: 24g
- Dimensions (without connectors)
- Size: 43.00mm x 40.50mm x 7.60mm
- Mounting Pattern: 30.5mm
- Weight: 14.5g
Other
- Made in the USA
- Open source AM32 firmware
- [DIU Blue Framework Listed](https://www.diu.mil/blue-uas/framework)
## See Also
- [ARK 4IN1 ESC CONS](https://docs.arkelectron.com/electronic-speed-controller/ark-4in1-esc) (ARK Docs)
+66
View File
@@ -0,0 +1,66 @@
# ESC Protocols
This topic lists the main [Electronic Speed Controller (ESC)](../peripherals/esc_motors.md) protocols supported by PX4.
## DShot
[DShot](../peripherals/dshot.md) is a digital ESC protocol that is highly recommended for vehicles that can benefit from reduced latency, in particular racing multicopters, VTOL vehicles, and so on.
It has reduced latency and is more robust than both [PWM](#pwm) and [OneShot](#oneshot-125).
In addition it does not require ESC calibration, telemetry is available from some ESCs, and you can reverse motor spin directions.
PX4 configuration is done in the [Actuator Configuration](../config/actuators.md).
Selecting a higher rate DShot ESC in the UI results in lower latency, but lower rates are more robust (and hence more suitable for large aircraft with longer leads); some ESCs only support lower rates (see datasheets for information).
Setup:
- [ESC Wiring](../peripherals/pwm_escs_and_servo.md) (same as for PWM ESCs)
- [DShot](../peripherals/dshot.md) also contains information about how to send commands etc.
## DroneCAN
[DroneCAN ESCs](../dronecan/escs.md) are recommended when DroneCAN is the primary bus used for your vehicle.
The PX4 implementation is currently limited to update rates of 200 Hz.
DroneCAN shares many similar benefits to [DShot](#dshot) including high data rates, robust connection over long leads, telemetry feedback, no need for calibration of the ESC itself.
[DroneCAN ESCs](../dronecan/escs.md) are connected via the DroneCAN bus (setup and configuration are covered at that link).
## PWM
[PWM ESCs](../peripherals/pwm_escs_and_servo.md) are commonly used for fixed-wing vehicles and ground vehicles (vehicles that require a lower latency like multicopters typically use oneshot or dshot ESCs).
PWM ESCs communicate using a periodic pulse, where the _width_ of the pulse indicates the desired speed.
The pulse width typically ranges between 1000 μs for zero power and 2000 μs for full power.
The periodic frame rate of the signal depends on the capability of the ESC, and commonly ranges between 50 Hz and 490 Hz (the theoretical maximum being 500 Hz for a very small "off" cycle).
A higher rate is better for ESCs, in particular where a rapid response to setpoint changes is needed.
For PWM servos 50 Hz is usually sufficient, and many don't support higher rates.
![duty cycle for PWM](../../assets/peripherals/esc_pwm_duty_cycle.png)
In addition to being a relatively slow protocol PWM ESCs require [calibration](../advanced_config/esc_calibration.md) because the pulse widths representing low and high values can vary significantly.
Unlike [DShot](#dshot) and [DroneCAN ESC](#dronecan) they do not have the ability to provide telemetry and feedback on ESC (or servo) state.
Setup:
- [ESC Wiring](../peripherals/pwm_escs_and_servo.md)
- [PX4 Configuration](../peripherals/pwm_escs_and_servo.md#px4-configuration)
- [ESC Calibration](../advanced_config/esc_calibration.md)
## OneShot 125
[OneShot 125 ESCs](../peripherals/oneshot.md) are usually much faster than PWM ESCs, and hence more responsive and easier to tune.
They are preferred over PWM for multicopters (but not as much as [DShot ESCs](#dshot), which do not require calibration, and may provide telemetry feedback).
There are a number of variants of the OneShot protocol, which support different rates.
PX4 only supports OneShot 125.
OneShot 125 is the same as PWM but uses pulse widths that are 8 times shorter (from 125 μs to 250 μs for zero to full power).
This allows OneShot 125 ESCs to have a much shorter duty cycle/higher rate.
For PWM the theoretical maximum is close to 500 Hz while for OneShot it approaches 4 kHz.
The actual supported rate depends on the ESC used.
Setup:
- [ESC Wiring](../peripherals/pwm_escs_and_servo.md) (same as for PWM ESCs)
- [PX4 Configuration](../peripherals/oneshot.md#px4-configuration)
- [ESC Calibration](../advanced_config/esc_calibration.md)
+2
View File
@@ -280,6 +280,8 @@ For more information see: [Plotting uORB Topic Data in Real Time using PlotJuggl
## See Also
- [uORB Documentation Standard](../uorb/uorb_documentation.md)
- _PX4 uORB Explained_ Blog series
- [Part 1](https://px4.io/px4-uorb-explained-part-1/)
- [Part 2](https://px4.io/px4-uorb-explained-part-2/)
+30 -68
View File
@@ -3,80 +3,42 @@
Many PX4 drones use brushless motors that are driven by the flight controller via an Electronic Speed Controller (ESC).
The ESC takes a signal from the flight controller and uses it to set control the level of power delivered to the motor.
PX4 supports a number of common protocols for sending the signals to ESCs: [PWM ESCs](../peripherals/pwm_escs_and_servo.md), [OneShot ESCs](../peripherals/oneshot.md), [DShot ESCs](../peripherals/dshot.md), [DroneCAN ESCs](../dronecan/escs.md), PCA9685 ESC (via I2C), and some UART ESCs (from Yuneec).
PX4 supports a number of [common protocols](../esc/esc_protocols.md) for sending the signals to ESCs: [PWM ESCs](../peripherals/pwm_escs_and_servo.md), [OneShot ESCs](../peripherals/oneshot.md), [DShot ESCs](../peripherals/dshot.md), [DroneCAN ESCs](../dronecan/escs.md), PCA9685 ESC (via I2C), and some UART ESCs (from Yuneec).
## Supported ESC
The following list is non-exhaustive.
| ESC Device | Protocols | Firmwares | Notes |
| ---------------------------- | ------------------------------------ | ------------------------ | ----------------------------------------------------- |
| [ARK 4IN1 ESC] | [Dshot], [PWM] | [AM32] | Has versions with/without connnectors |
| [Holybro Kotleta 20] | [DroneCAN], [PWM] | [PX4 Sapog ESC Firmware] | |
| [Vertiq Motor & ESC modules] | [Dshot], [OneShot], Multishot, [PWM] | Vertiq firmware | Larger modules support DroneCAN, ESC and Motor in one |
| [VESC ESCs] | [DroneCAN], [PWM] | VESC project firmware | |
| [Zubax Telega] | [DroneCAN], [PWM] | Telega-based | ESC and Motor in one |
<!-- Links for table above -->
[ARK 4IN1 ESC]: ../esc/ark_4in1_esc.md
[AM32]: https://am32.ca/
[PX4 Sapog ESC Firmware]: ../dronecan/sapog.md
[VESC ESCs]: ../peripherals/vesc.md
[DroneCAN]: ../dronecan/escs.md
[Dshot]: ../peripherals/dshot.md
[OneShot]: ../peripherals/oneshot.md
[PWM]: ../peripherals/pwm_escs_and_servo.md
[Holybro Kotleta 20]: ../dronecan/holybro_kotleta.md
[Vertiq Motor & ESC modules]: ../peripherals/vertiq.md
[Zubax Telega]: ../dronecan/zubax_telega.md
## See Also
For more information see:
- [ESC Protocols](../esc/esc_protocols.md) — overview of main ESC/Servo protocols supported by PX4
- [PWM ESCs and Servos](../peripherals/pwm_escs_and_servo.md)
- [OneShot ESCs and Servos](../peripherals/oneshot.md)
- [DShot](../peripherals/dshot.md)
- [DroneCAN ESCs](../dronecan/escs.md)
- [ESC Calibration](../advanced_config/esc_calibration.md)
- [ESC Firmware and Protocols Overview](https://oscarliang.com/esc-firmware-protocols/) (oscarliang.com)
A high level overview of the main ESC/Servo protocols supported by PX4 is given below.
## ESC Protocols
### PWM
[PWM ESCs](../peripherals/pwm_escs_and_servo.md) are commonly used for fixed-wing vehicles and ground vehicles (vehicles that require a lower latency like multicopters typically use oneshot or dshot ESCs).
PWM ESCs communicate using a periodic pulse, where the _width_ of the pulse indicates the desired power level.
The pulse wdith typically ranges between 1000uS for zero power and 2000uS for full power.
The periodic frame rate of the signal depends on the capability of the ESC, and commonly ranges between 50Hz and 490 Hz (the theoretical maximum being 500Hz for a very small "off" cycle).
A higher rate is better for ESCs, in particular where a rapid response to setpoint changes is needed.
For PWM servos 50Hz is usually sufficient, and many don't support higher rates.
![duty cycle for PWM](../../assets/peripherals/esc_pwm_duty_cycle.png)
In addition to being a relatively slow protocol PWM ESCs require [calibration](../advanced_config/esc_calibration.md) because the range values representing low and high values can vary significantly.
Unlike [dshot](#dshot) and [DroneCAN ESC](#dronecan) they do not have the ability to provide telemetry and feedback on ESC (or servo) state.
Setup:
- [ESC Wiring](../peripherals/pwm_escs_and_servo.md)
- [PX4 Configuration](../peripherals/pwm_escs_and_servo.md#px4-configuration)
- [ESC Calibration](../advanced_config/esc_calibration.md)
### Oneshot 125
[OneShot 125 ESCs](../peripherals/oneshot.md) are usually much faster than PWM ESCs, and hence more responsive and easier to tune.
They are preferred over PWM for multicopters (but not as much as [DShot ESCs](#dshot), which do not require calibration, and may provide telemetry feedback).
There are a number of variants of the OneShot protocol, which support different rates.
PX4 only supports OneShot 125.
OneShot 125 is the same as PWM but uses pulse widths that are 8 times shorter (from 125us to 250us for zero to full power).
This allows OneShot 125 ESCs to have a much shorter duty cycle/higher rate.
For PWM the theoretical maximum is close to 500 Hz while for OneShot it approaches 4 kHz.
The actual supported rate depends on the ESC used.
Setup:
- [ESC Wiring](../peripherals/pwm_escs_and_servo.md) (same as for PWM ESCs)
- [PX4 Configuration](../peripherals/oneshot.md#px4-configuration)
- [ESC Calibration](../advanced_config/esc_calibration.md)
### DShot
[DShot](../peripherals/dshot.md) is a digital ESC protocol that is highly recommended for vehicles that can benefit from reduce latency, in particular racing multicopters, VTOL vehicles, and so on.
It has reduced latency and is more robust than both [PWM](#pwm) and [OneShot](#oneshot-125).
In addition it does not require ESC calibration, telemetry is available from some ESCs, and you can revers motor spin directions
PX4 configuration is done in the [Actuator Configuration](../config/actuators.md).
Selecting a higher rate DShot ESC in the UI result in lower latency, but lower rates are more robust (and hence more suitable for large aircraft with longer leads); some ESCs only support lower rates (see datasheets for information).
Setup:
- [ESC Wiring](../peripherals/pwm_escs_and_servo.md) (same as for PWM ESCs)
- [DShot](../peripherals/dshot.md) also contains information about how to send commands etc.
### DroneCAN
[DroneCAN ESCs](../dronecan/escs.md) are recommended when DroneCAN is the primary bus used for your vehicle.
The PX4 implementation is currently limited to update rates of 200Hz.
DroneCAN shares many similar benefits to [Dshot](#dshot) including high data rates, robust connection over long leads, telemetry feedback, no need for calibration of the ESC itself.
[DroneCAN ESCs](../dronecan/escs.md) are connected via the DroneCAN bus (setup and configuration are covered at that link).
+170
View File
@@ -0,0 +1,170 @@
# uORB Documentation Standard
This topic demonstrates and explains how to document uORB messages.
::: info
At time of writing many topics have not been updated.
:::
## Overview
The [AirspeedValidated](../msg_docs/AirspeedValidated.md) message shown below is a good example of a uORB topic that has been documented to the current standard.
```py
# Validated airspeed
#
# Provides information about airspeed (indicated, true, calibrated) and the source of the data.
# Used by controllers, estimators and for airspeed reporting to operator.
uint32 MESSAGE_VERSION = 1
uint64 timestamp # [us] Time since system start
float32 indicated_airspeed_m_s # [m/s] [@invalid NaN] Indicated airspeed (IAS)
float32 calibrated_airspeed_m_s # [m/s] [@invalid NaN] Calibrated airspeed (CAS)
float32 true_airspeed_m_s # [m/s] [@invalid NaN] True airspeed (TAS)
int8 airspeed_source # [@enum SOURCE] Source of currently published airspeed values
int8 SOURCE_DISABLED = -1 # Disabled
int8 SOURCE_GROUND_MINUS_WIND = 0 # Ground speed minus wind
int8 SOURCE_SENSOR_1 = 1 # Sensor 1
int8 SOURCE_SENSOR_2 = 2 # Sensor 2
int8 SOURCE_SENSOR_3 = 3 # Sensor 3
int8 SOURCE_SYNTHETIC = 4 # Synthetic airspeed
float32 calibrated_ground_minus_wind_m_s # [m/s] [@invalid NaN] CAS calculated from groundspeed - windspeed, where windspeed is estimated based on a zero-sideslip assumption
float32 calibraded_airspeed_synth_m_s # [m/s] [@invalid NaN] Synthetic airspeed
float32 airspeed_derivative_filtered # [m/s^2] Filtered indicated airspeed derivative
float32 throttle_filtered # [-] Filtered fixed-wing throttle
float32 pitch_filtered # [rad] Filtered pitch
```
The main things to note are:
- Documentation is added using formatted uORB comments.
Any text on a line after the `#` character is a comment, except for lines that start with the text `# TOPIC` (which indicates a multi-topic message).
- The message starts with a comment block consisting of short description (mandatory), followed by a longer description and then a space.
- Field and constants almost all have comments.
The comments are added on the same line as the field/constant, separated by one space.
- Fields:
- Comments are all on the same line as the field (extra lines become internal comments).
- Comments start with metadata, such as the units (`[m/s]`, `[rad/s]`) or allowed values (`[@enum SOURCE]`), and can also list invalid values (`[@invalid NaN]`) and allowed ranges (`[@range min, max]`).
- Units are required except for boolean fields or for fields with an enum value.
`[-]` is used to indicate unitless fields.
- Comments follow the metadata after a space.
The line should not be terminated in a full stop.
- Constants:
- Don't have metadata: the description follows the comment marker after one space.
- Some constants, such as `MESSAGE_VERSION`, don't need documentation because they are standardized.
- Constants with the same name prefix are grouped together as enums after the associated field.
The following sections expand on the allowed formats.
## Message Description
Every message should start with a comment block that describes the message:
```py
# Short description (mandatory)
#
# Longer description for the message if needed.
# Can be multiline, and should have punctuation.
# Should be followed by an empty line.
```
This consists of a mandatory short description, optionally followed by an empty comment line, and then a longer description.
Short description (mandatory):
- A succinct explanation for the purpose of the message.
- Usually just one line without a terminating full stop.
- Minimally it may just mirror the message name.
- For example, [`AirspeedValidated`](../msg_docs/AirspeedValidated.md) above has the short description `Validated airspeed`.
Long description (Optional):
- Additional context required to understand how the message is used.
- In particular this should be anything that can't be inferred from the name, fields or constants, such as the publishers and expected consumers.
It might also cover whether the message is only used for a particular frame type or mode.
- The message is often multiline and contains punctuation.
- May include comment lines that are empty, in order to indicate paragraphs.
Both short and long descriptions may be multi-line.
Single line descriptions should not include a terminating full stop, but multiline comments should do so.
The message description block ends at the first non-comment line, which should be an empty line, but might be a field or constant.
Any subsequent comment lines are considered "internal comments".
### Fields
A typical field comment looks like this:
```py
float32 indicated_airspeed_m_s # [m/s] [@invalid NaN] Indicated airspeed (IAS)
```
Field comments must all be on the same line as the field, and consist of optional metadata followed by a description:
- `metadata` (Optional)
- Information about the field units and allowed values:
- `[<unit>]`
- The unit of measurement inside square brackets (note, no `@` delineator indicates a unit), such as `[m]` for metres.
- Allowed units include: `m`, `m/s`, `m/s^2`, `rad`, `rad/s`, `rpm`, `V`, `A`, `mA`, `mAh`, `W`, `dBm`, `s`, `ms`, `us`, `Ohm`, `MB`, `Kb/s`, `degC`, `Pa`.
- Units are required unless clearly invalid, such as when the field is a boolean, or is an enum value.
- Unitless values should be specified as `[-]`.
Note though that units are not required for boolean fields or enum fields.
- `[@enum <enum_name>]`
- The `enum_name` gives the prefix of constant values in the message that can be assigned to the field.
Note that enums in uORB are just a naming convention: they are not explicitly declared.
Multiple enum names allowed for a field indicates a possible error in the field design.
- `[@range <lower_value>, <upper_value>]`
- The allowed range of the field, specified as a `lower_value` and/or an `upper_value`.
Either value can be omitted to indicate an unbounded upper or lower value.
For example `[@range 0, 3]`, `[@range 5.3, ]`, `[@range , 3]`.
- `[@invalid <value> <description>]`
- The `value` to set the field to indicate that the field doesn't contain valid data, such as `[@invalid NaN]`.
The `description` is optional, and might be used to indicate the conditions under which data is invalid.
- `[@frame <value>]`
- The `frame` in which the field is set, such as `[@frame NED]` or `[@frame Body]`.
- `description`
- A concise description of the purpose of the field, and including any important information that can't be inferred from the name!
Use a capital first letter, and omit the full stop if the description is a single sentence.
Multiple sentences may also omit the final full stop.
### Constants
Constants follow the documentation conventions as fields except they only have a description (no metadata).
Documentation for a constant might look like this:
```py
int8 SOURCE_GROUND_MINUS_WIND = 0 # Ground speed minus wind
```
Constants are often grouped together following a field as enum values.
Note below how the prefix `SOURCE` for the values is specified as an enum against the _field_.
```py
int8 airspeed_source # [@enum SOURCE] Source of currently published airspeed values
int8 SOURCE_DISABLED = -1 # Disabled
int8 SOURCE_GROUND_MINUS_WIND = 0 # Ground speed minus wind
...
```
A small number of constants have a standardised meaning and do not require documentation.
These are:
- `ORB_QUEUE_LENGTH`
- `MESSAGE_VERSION`
### `# TOPICS`
The prefix `# TOPICS` is used to indicate topic names for multi-topic messages.
For example, the [VehicleGlobalPosition.msg](../msg_docs/VehicleGlobalPosition.md) message definition is used to define the topic ids as shown:
```text
# TOPICS vehicle_global_position vehicle_global_position_groundtruth external_ins_global_position
# TOPICS estimator_global_position
# TOPICS aux_global_position
```
At time of writing there is no format for documenting these.
+4
View File
@@ -26,6 +26,10 @@ If you are looking for more resources to learn about the module, a website has b
## Neural Network PX4 Firmware
:::warning
This module requires Ubuntu 24.04 or newer (it is not supported in Ubuntu 22.04).
:::
The module has been tested on a number of configurations, which can be build locally using the commands:
```sh
+38 -6
View File
@@ -10,7 +10,7 @@ If you have [mounted the compass](../assembly/mount_gps_compass.md#compass-orien
## 개요
You will need to calibrate your compass(es) when you first setup your vehicle, and you may need to recalibrate it if the vehicles is ever exposed to a very strong magnetic field, or if it is used in an area with abnormal magnetic characteristics.
You will need to calibrate your compass(es) when you first setup your vehicle, and you may need to [recalibrate](#recalibration) it if the vehicles is ever exposed to a very strong magnetic field, or if it is used in an area with abnormal magnetic characteristics.
:::tip
Indications of a poor compass calibration include multicopter circling during hover, toilet bowling (circling at increasing radius/spiraling-out, usually constant altitude, leading to fly-way), or veering off-path when attempting to fly straight.
@@ -20,13 +20,16 @@ _QGroundControl_ should also notify the error `mag sensors inconsistent`.
The process calibrates all compasses and autodetects the orientation of any external compasses.
If any external magnetometers are available, it then disables the internal magnetometers (these are primarily needed for automatic rotation detection of external magnetometers).
### Types of Calibration
Several types of compass calibration are available:
1. [Complete](#complete-calibration): This calibration is required after installing the autopilot on an airframe for the first time or when the configuration of the vehicle has changed significantly.
It compensates for hard and soft iron effects by estimating an offset and a scale factor for each axis.
2. [Partial](#partial-quick-calibration): This calibration can be performed as a routine when preparing the vehicle for a flight, after changing the payload, or simply when the compass rose seems inaccurate.
This type of calibration only estimates the offsets to compensate for a hard iron effect.
3. [Large vehicle](#large-vehicle-calibration): This calibration can be performed when the vehicle is too large or heavy to perform a complete calibration. This type of calibration only estimates the offsets to compensate for a hard iron effect.
3. [Large vehicle](#large-vehicle-calibration): This calibration can be performed when the vehicle is too large or heavy to perform a complete calibration.
This type of calibration only estimates the offsets to compensate for a hard iron effect.
## 보정 절차
@@ -57,19 +60,23 @@ Before starting the calibration:
![Select Compass calibration PX4](../../assets/qgc/setup/sensor/sensor_compass_select_px4.png)
::: info
You should already have set the [Autopilot Orientation](../config/flight_controller_orientation.md). 미리 설정하지 않았다면, 여기에서 설정할 수 있습니다.
You should already have set the [Autopilot Orientation](../config/flight_controller_orientation.md).
미리 설정하지 않았다면, 여기에서 설정할 수 있습니다.
:::
4. Click **OK** to start the calibration.
5. 기체를 아래에 표시된 자세로 놓고 그대로 유지해주십시오 메시지가 표시되면(방향 이미지가 노란색으로 변함) 기체를 지정축을 기준으로 한 방향으로 회전시킵니다. 현재 방향에 대해 보정이 완료되면 화면의 그림이 녹색으로 바뀝니다.
5. 기체를 아래에 표시된 자세로 놓고 그대로 유지해주십시오
메시지가 표시되면(방향 이미지가 노란색으로 변함) 기체를 지정축을 기준으로 한 방향으로 회전시킵니다.
현재 방향에 대해 보정이 완료되면 화면의 그림이 녹색으로 바뀝니다.
![Compass calibration steps on PX4](../../assets/qgc/setup/sensor/sensor_compass_calibrate_px4.png)
6. 드론의 모든 방향에 대해 보정 과정을 반복합니다.
Once you've calibrated the vehicle in all the positions _QGroundControl_ will display _Calibration complete_ (all orientation images will be displayed in green and the progress bar will fill completely). 그런 다음 다음 센서의 보정 작업을 진행합니다.
Once you've calibrated the vehicle in all the positions _QGroundControl_ will display _Calibration complete_ (all orientation images will be displayed in green and the progress bar will fill completely).
그런 다음 다음 센서의 보정 작업을 진행합니다.
### Partial "Quick" Calibration
@@ -92,7 +99,8 @@ This calibration is similar to the well-known figure-8 compass calibration done
This calibration process leverages external knowledge of vehicle's orientation and location, and a World Magnetic Model (WMM) to calibrate the hard iron biases.
1. Ensure GNSS Fix. This is required to find the expected Earth magnetic field in WMM tables.
1. Ensure GNSS Fix.
This is required to find the expected Earth magnetic field in WMM tables.
2. Align the vehicle to face True North.
Be as accurate as possible for best results.
3. Open the [QGroundControl MAVLink Console](https://docs.qgroundcontrol.com/master/en/qgc-user-guide/analyze_view/mavlink_console.html) and send the following command:
@@ -112,6 +120,30 @@ This calibration process leverages external knowledge of vehicle's orientation a
After the calibration is complete, check that the heading indicator and the heading of the arrow on the map are stable and match the orientation of the vehicle when turning it e.g. to the cardinal directions.
## Recalibration
Recalibration is recommended whenever the magnetic environment of the vehicle has changed or when heading behavior appears unreliable.
You can use either complete calibration or mag quick calibration depending on the size of the vehicle and your ability to rotate it through the required orientations.
Complete calibration provides the most accurate soft-iron compensation.
Recalibrate the compass when:
- _The compass module or its mounting orientation has changed._
This includes replacing the GPS or mag unit, rotating the mast, or altering how the module is fixed to the airframe.
- _The vehicle has been exposed to a strong magnetic disturbance._
Examples include transport or storage near large steel structures, welding operations near the airframe, or operation close to high-current equipment.
- _Structural, wiring, or payload changes may have altered the magnetic field around the sensors._
New payloads, rerouted wires, additional batteries, or metal fasteners can introduce soft-iron effects that affect heading accuracy.
- _The vehicle is operated in a region with significantly different magnetic characteristics._
Large changes in latitude, longitude, or magnetic inclination can require re-estimation of offsets.
- _QGroundControl reports magnetometer inconsistencies_.
For example, if you see the error `mag sensors inconsistent`.
- _Heading behavior does not match the vehicles observed orientation._
Symptoms include drifting yaw, sudden heading jumps when attempting to fly straight, and toilet bowling
- _QGroundControl_ sends the error `mag sensors inconsistent`.
This indicates that multiple magnetometers are reporting different headings.
## Additional Calibration/Configuration
The process above will autodetect, [set default rotations](../advanced_config/parameter_reference.md#SENS_MAG_AUTOROT), calibrate, and prioritise, all available magnetometers.
+1 -1
View File
@@ -2,7 +2,7 @@
<Badge type="tip" text="PX4 v1.15" />
PX4 implements the MAVLink [Standard Modes Protocol](https://mavlink.io/en/services/standard_modes.md) from PX4 v1.15, with a corresponding implementation in QGroundControl Daily builds (and future release builds).
PX4 implements the MAVLink [Standard Modes Protocol](https://mavlink.io/en/services/standard_modes.html) from PX4 v1.15, with a corresponding implementation in QGroundControl Daily builds (and future release builds).
The protocol allows you to discover all flight modes available to the vehicle, including PX4 External modes created using the [PX4 ROS 2 Control Interface](../ros2/px4_ros2_control_interface.md), and get or set the current mode.
+2
View File
@@ -899,6 +899,8 @@ fetching the latest mixing result and write them to PCA9685 at its scheduling ti
It can do full 12bits output as duty-cycle mode, while also able to output precious pulse width
that can be accepted by most ESCs and servos.
The I2C bus and address can be configured via parameters `PCA9685_EN_BUS` and `PCA9685_I2C_ADDR`, or via command line arguments.
### 예
It is typically started with:
+1
View File
@@ -69,6 +69,7 @@ uint16 VEHICLE_CMD_DO_PARACHUTE=208 # Mission command to trigger a parachute. |a
uint16 VEHICLE_CMD_DO_MOTOR_TEST=209 # Motor test command. |Instance (@range 1, )|throttle type|throttle|timeout [s]|Motor count|Test order|Unused|
uint16 VEHICLE_CMD_DO_INVERTED_FLIGHT=210 # Change to/from inverted flight. |inverted (0=normal, 1=inverted)|Unused|Unused|Unused|Unused|Unused|Unused|
uint16 VEHICLE_CMD_DO_GRIPPER = 211 # Command to operate a gripper.
uint16 VEHICLE_CMD_DO_AUTOTUNE_ENABLE = 212 # Enable autotune module. |1 to enable|Unused|Unused|Unused|Unused|Unused|Unused|
uint16 VEHICLE_CMD_DO_SET_CAM_TRIGG_INTERVAL=214 # Mission command to set TRIG_INTERVAL for this flight. |[m] Camera trigger distance|Shutter integration time (ms)|Unused|Unused|Unused|Unused|Unused|
uint16 VEHICLE_CMD_DO_MOUNT_CONTROL_QUAT=220 # Mission command to control a camera or antenna mount, using a quaternion as reference. |q1 - quaternion param #1, w (1 in null-rotation)|q2 - quaternion param #2, x (0 in null-rotation)|q3 - quaternion param #3, y (0 in null-rotation)|q4 - quaternion param #4, z (0 in null-rotation)|Unused|Unused|Unused|
uint16 VEHICLE_CMD_DO_GUIDED_MASTER=221 # Set id of master controller. |System ID|Component ID|Unused|Unused|Unused|Unused|Unused|
+1
View File
@@ -55,6 +55,7 @@ Please continue reading for [upgrade instructions](#upgrade-guide).
### 센서
- Add [sbgECom INS driver](../sensor/sbgecom.md) ([PX4-Autopilot#24137](https://github.com/PX4/PX4-Autopilot/pull/24137))
- Quick magnetometer calibration now supports specifying an arbitrary initial heading ([PX4-Autopilot#24637](https://github.com/PX4/PX4-Autopilot/pull/24637))
### 시뮬레이션
+4
View File
@@ -26,6 +26,10 @@ If you are looking for more resources to learn about the module, a website has b
## Neural Network PX4 Firmware
:::warning
This module requires Ubuntu 24.04 or newer (it is not supported in Ubuntu 22.04).
:::
The module has been tested on a number of configurations, which can be build locally using the commands:
```sh
+38 -6
View File
@@ -10,7 +10,7 @@ If you have [mounted the compass](../assembly/mount_gps_compass.md#compass-orien
## Загальний огляд
You will need to calibrate your compass(es) when you first setup your vehicle, and you may need to recalibrate it if the vehicles is ever exposed to a very strong magnetic field, or if it is used in an area with abnormal magnetic characteristics.
You will need to calibrate your compass(es) when you first setup your vehicle, and you may need to [recalibrate](#recalibration) it if the vehicles is ever exposed to a very strong magnetic field, or if it is used in an area with abnormal magnetic characteristics.
:::tip
Indications of a poor compass calibration include multicopter circling during hover, toilet bowling (circling at increasing radius/spiraling-out, usually constant altitude, leading to fly-way), or veering off-path when attempting to fly straight.
@@ -20,13 +20,16 @@ _QGroundControl_ should also notify the error `mag sensors inconsistent`.
The process calibrates all compasses and autodetects the orientation of any external compasses.
If any external magnetometers are available, it then disables the internal magnetometers (these are primarily needed for automatic rotation detection of external magnetometers).
### Types of Calibration
Several types of compass calibration are available:
1. [Complete](#complete-calibration): This calibration is required after installing the autopilot on an airframe for the first time or when the configuration of the vehicle has changed significantly.
Воно компенсує впливи твердого та м'якого заліза, оцінюючи зміщення та коефіцієнт масштабу для кожної вісі.
2. [Partial](#partial-quick-calibration): This calibration can be performed as a routine when preparing the vehicle for a flight, after changing the payload, or simply when the compass rose seems inaccurate.
Цей тип калібрування лише оцінює зміщення для компенсації ефекту твердого заліза.
3. [Large vehicle](#large-vehicle-calibration): This calibration can be performed when the vehicle is too large or heavy to perform a complete calibration. Цей тип калібрування лише оцінює зміщення для компенсації ефекту твердого заліза.
3. [Large vehicle](#large-vehicle-calibration): This calibration can be performed when the vehicle is too large or heavy to perform a complete calibration.
Цей тип калібрування лише оцінює зміщення для компенсації ефекту твердого заліза.
## Виконання калібрування
@@ -57,19 +60,23 @@ Several types of compass calibration are available:
![Select Compass calibration PX4](../../assets/qgc/setup/sensor/sensor_compass_select_px4.png)
::: info
You should already have set the [Autopilot Orientation](../config/flight_controller_orientation.md). Якщо ні, ви також можете встановити це тут.
You should already have set the [Autopilot Orientation](../config/flight_controller_orientation.md).
Якщо ні, ви також можете встановити це тут.
:::
4. Click **OK** to start the calibration.
5. Розмістіть транспортний засіб у будь-якому з показаних червоних орієнтацій (неповний) та утримуйте його нерухомим. Після запиту (орієнтаційне зображення стає жовтим) обертайте транспортний засіб навколо вказаної вісі в одному або обох напрямках. Після завершення калібрування для поточного орієнтації пов'язане зображення на екрані стане зеленим.
5. Розмістіть транспортний засіб у будь-якому з показаних червоних орієнтацій (неповний) та утримуйте його нерухомим.
Після запиту (орієнтаційне зображення стає жовтим) обертайте транспортний засіб навколо вказаної вісі в одному або обох напрямках.
Після завершення калібрування для поточного орієнтації пов'язане зображення на екрані стане зеленим.
![Compass calibration steps on PX4](../../assets/qgc/setup/sensor/sensor_compass_calibrate_px4.png)
6. Повторіть процес калібрування для всіх орієнтацій автомобіля.
Once you've calibrated the vehicle in all the positions _QGroundControl_ will display _Calibration complete_ (all orientation images will be displayed in green and the progress bar will fill completely). Ви можете потім перейти до наступного сенсора.
Once you've calibrated the vehicle in all the positions _QGroundControl_ will display _Calibration complete_ (all orientation images will be displayed in green and the progress bar will fill completely).
Ви можете потім перейти до наступного сенсора.
### Часткова "Швидка" Калібрування
@@ -92,7 +99,8 @@ Once you've calibrated the vehicle in all the positions _QGroundControl_ will di
This calibration process leverages external knowledge of vehicle's orientation and location, and a World Magnetic Model (WMM) to calibrate the hard iron biases.
1. Ensure GNSS Fix. This is required to find the expected Earth magnetic field in WMM tables.
1. Ensure GNSS Fix.
This is required to find the expected Earth magnetic field in WMM tables.
2. Align the vehicle to face True North.
Be as accurate as possible for best results.
3. Open the [QGroundControl MAVLink Console](https://docs.qgroundcontrol.com/master/en/qgc-user-guide/analyze_view/mavlink_console.html) and send the following command:
@@ -112,6 +120,30 @@ This calibration process leverages external knowledge of vehicle's orientation a
Після завершення калібрування перевірте, щоб індикатор напрямку та напрямок стрілки на карті були стабільними і відповідали орієнтації транспортного засобу при його повороті, наприклад, на головні сторони.
## Recalibration
Recalibration is recommended whenever the magnetic environment of the vehicle has changed or when heading behavior appears unreliable.
You can use either complete calibration or mag quick calibration depending on the size of the vehicle and your ability to rotate it through the required orientations.
Complete calibration provides the most accurate soft-iron compensation.
Recalibrate the compass when:
- _The compass module or its mounting orientation has changed._
This includes replacing the GPS or mag unit, rotating the mast, or altering how the module is fixed to the airframe.
- _The vehicle has been exposed to a strong magnetic disturbance._
Examples include transport or storage near large steel structures, welding operations near the airframe, or operation close to high-current equipment.
- _Structural, wiring, or payload changes may have altered the magnetic field around the sensors._
New payloads, rerouted wires, additional batteries, or metal fasteners can introduce soft-iron effects that affect heading accuracy.
- _The vehicle is operated in a region with significantly different magnetic characteristics._
Large changes in latitude, longitude, or magnetic inclination can require re-estimation of offsets.
- _QGroundControl reports magnetometer inconsistencies_.
For example, if you see the error `mag sensors inconsistent`.
- _Heading behavior does not match the vehicles observed orientation._
Symptoms include drifting yaw, sudden heading jumps when attempting to fly straight, and toilet bowling
- _QGroundControl_ sends the error `mag sensors inconsistent`.
This indicates that multiple magnetometers are reporting different headings.
## Додаткова калібрування/конфігурація
The process above will autodetect, [set default rotations](../advanced_config/parameter_reference.md#SENS_MAG_AUTOROT), calibrate, and prioritise, all available magnetometers.
+1 -1
View File
@@ -2,7 +2,7 @@
<Badge type="tip" text="PX4 v1.15" />
PX4 implements the MAVLink [Standard Modes Protocol](https://mavlink.io/en/services/standard_modes.md) from PX4 v1.15, with a corresponding implementation in QGroundControl Daily builds (and future release builds).
PX4 implements the MAVLink [Standard Modes Protocol](https://mavlink.io/en/services/standard_modes.html) from PX4 v1.15, with a corresponding implementation in QGroundControl Daily builds (and future release builds).
The protocol allows you to discover all flight modes available to the vehicle, including PX4 External modes created using the [PX4 ROS 2 Control Interface](../ros2/px4_ros2_control_interface.md), and get or set the current mode.
+2
View File
@@ -899,6 +899,8 @@ fetching the latest mixing result and write them to PCA9685 at its scheduling ti
It can do full 12bits output as duty-cycle mode, while also able to output precious pulse width
that can be accepted by most ESCs and servos.
The I2C bus and address can be configured via parameters `PCA9685_EN_BUS` and `PCA9685_I2C_ADDR`, or via command line arguments.
### Приклади
It is typically started with:
+1
View File
@@ -69,6 +69,7 @@ uint16 VEHICLE_CMD_DO_PARACHUTE=208 # Mission command to trigger a parachute. |a
uint16 VEHICLE_CMD_DO_MOTOR_TEST=209 # Motor test command. |Instance (@range 1, )|throttle type|throttle|timeout [s]|Motor count|Test order|Unused|
uint16 VEHICLE_CMD_DO_INVERTED_FLIGHT=210 # Change to/from inverted flight. |inverted (0=normal, 1=inverted)|Unused|Unused|Unused|Unused|Unused|Unused|
uint16 VEHICLE_CMD_DO_GRIPPER = 211 # Command to operate a gripper.
uint16 VEHICLE_CMD_DO_AUTOTUNE_ENABLE = 212 # Enable autotune module. |1 to enable|Unused|Unused|Unused|Unused|Unused|Unused|
uint16 VEHICLE_CMD_DO_SET_CAM_TRIGG_INTERVAL=214 # Mission command to set TRIG_INTERVAL for this flight. |[m] Camera trigger distance|Shutter integration time (ms)|Unused|Unused|Unused|Unused|Unused|
uint16 VEHICLE_CMD_DO_MOUNT_CONTROL_QUAT=220 # Mission command to control a camera or antenna mount, using a quaternion as reference. |q1 - quaternion param #1, w (1 in null-rotation)|q2 - quaternion param #2, x (0 in null-rotation)|q3 - quaternion param #3, y (0 in null-rotation)|q4 - quaternion param #4, z (0 in null-rotation)|Unused|Unused|Unused|
uint16 VEHICLE_CMD_DO_GUIDED_MASTER=221 # Set id of master controller. |System ID|Component ID|Unused|Unused|Unused|Unused|Unused|
+1
View File
@@ -55,6 +55,7 @@ Please continue reading for [upgrade instructions](#upgrade-guide).
### Датчики
- Add [sbgECom INS driver](../sensor/sbgecom.md) ([PX4-Autopilot#24137](https://github.com/PX4/PX4-Autopilot/pull/24137))
- Quick magnetometer calibration now supports specifying an arbitrary initial heading ([PX4-Autopilot#24637](https://github.com/PX4/PX4-Autopilot/pull/24637))
### Симуляція
+4
View File
@@ -26,6 +26,10 @@ If you are looking for more resources to learn about the module, a website has b
## Neural Network PX4 Firmware
:::warning
This module requires Ubuntu 24.04 or newer (it is not supported in Ubuntu 22.04).
:::
The module has been tested on a number of configurations, which can be build locally using the commands:
```sh
+38 -6
View File
@@ -10,7 +10,7 @@ If you have [mounted the compass](../assembly/mount_gps_compass.md#compass-orien
## 综述
You will need to calibrate your compass(es) when you first setup your vehicle, and you may need to recalibrate it if the vehicles is ever exposed to a very strong magnetic field, or if it is used in an area with abnormal magnetic characteristics.
You will need to calibrate your compass(es) when you first setup your vehicle, and you may need to [recalibrate](#recalibration) it if the vehicles is ever exposed to a very strong magnetic field, or if it is used in an area with abnormal magnetic characteristics.
:::tip
Indications of a poor compass calibration include multicopter circling during hover, toilet bowling (circling at increasing radius/spiraling-out, usually constant altitude, leading to fly-way), or veering off-path when attempting to fly straight.
@@ -20,13 +20,16 @@ _QGroundControl_ should also notify the error `mag sensors inconsistent`.
The process calibrates all compasses and autodetects the orientation of any external compasses.
If any external magnetometers are available, it then disables the internal magnetometers (these are primarily needed for automatic rotation detection of external magnetometers).
### Types of Calibration
Several types of compass calibration are available:
1. [Complete](#complete-calibration): This calibration is required after installing the autopilot on an airframe for the first time or when the configuration of the vehicle has changed significantly.
It compensates for hard and soft iron effects by estimating an offset and a scale factor for each axis.
2. [Partial](#partial-quick-calibration): This calibration can be performed as a routine when preparing the vehicle for a flight, after changing the payload, or simply when the compass rose seems inaccurate.
This type of calibration only estimates the offsets to compensate for a hard iron effect.
3. [Large vehicle](#large-vehicle-calibration): This calibration can be performed when the vehicle is too large or heavy to perform a complete calibration. This type of calibration only estimates the offsets to compensate for a hard iron effect.
3. [Large vehicle](#large-vehicle-calibration): This calibration can be performed when the vehicle is too large or heavy to perform a complete calibration.
This type of calibration only estimates the offsets to compensate for a hard iron effect.
## 执行校准
@@ -57,19 +60,23 @@ The calibration steps are:
![Select Compass calibration PX4](../../assets/qgc/setup/sensor/sensor_compass_select_px4.png)
::: info
You should already have set the [Autopilot Orientation](../config/flight_controller_orientation.md). If not, you can also set it here.
You should already have set the [Autopilot Orientation](../config/flight_controller_orientation.md).
If not, you can also set it here.
:::
4. Click **OK** to start the calibration.
5. 把你的飞机放置在下面显示的某一个方向,并保持静止。 随后提示(方向图像变为黄色)在指定方向旋转飞行器。 该位置标定完成后,屏幕上的相应图示将变成绿色。
5. 把你的飞机放置在下面显示的某一个方向,并保持静止。
随后提示(方向图像变为黄色)在指定方向旋转飞行器。
该位置标定完成后,屏幕上的相应图示将变成绿色。
![Compass calibration steps on PX4](../../assets/qgc/setup/sensor/sensor_compass_calibrate_px4.png)
6. 在机体的所有方向上重复校准步骤。
Once you've calibrated the vehicle in all the positions _QGroundControl_ will display _Calibration complete_ (all orientation images will be displayed in green and the progress bar will fill completely). You can then proceed to the next sensor.
Once you've calibrated the vehicle in all the positions _QGroundControl_ will display _Calibration complete_ (all orientation images will be displayed in green and the progress bar will fill completely).
You can then proceed to the next sensor.
### Partial "Quick" Calibration
@@ -92,7 +99,8 @@ This calibration is similar to the well-known figure-8 compass calibration done
This calibration process leverages external knowledge of vehicle's orientation and location, and a World Magnetic Model (WMM) to calibrate the hard iron biases.
1. Ensure GNSS Fix. This is required to find the expected Earth magnetic field in WMM tables.
1. Ensure GNSS Fix.
This is required to find the expected Earth magnetic field in WMM tables.
2. Align the vehicle to face True North.
Be as accurate as possible for best results.
3. Open the [QGroundControl MAVLink Console](https://docs.qgroundcontrol.com/master/en/qgc-user-guide/analyze_view/mavlink_console.html) and send the following command:
@@ -112,6 +120,30 @@ This calibration process leverages external knowledge of vehicle's orientation a
After the calibration is complete, check that the heading indicator and the heading of the arrow on the map are stable and match the orientation of the vehicle when turning it e.g. to the cardinal directions.
## Recalibration
Recalibration is recommended whenever the magnetic environment of the vehicle has changed or when heading behavior appears unreliable.
You can use either complete calibration or mag quick calibration depending on the size of the vehicle and your ability to rotate it through the required orientations.
Complete calibration provides the most accurate soft-iron compensation.
Recalibrate the compass when:
- _The compass module or its mounting orientation has changed._
This includes replacing the GPS or mag unit, rotating the mast, or altering how the module is fixed to the airframe.
- _The vehicle has been exposed to a strong magnetic disturbance._
Examples include transport or storage near large steel structures, welding operations near the airframe, or operation close to high-current equipment.
- _Structural, wiring, or payload changes may have altered the magnetic field around the sensors._
New payloads, rerouted wires, additional batteries, or metal fasteners can introduce soft-iron effects that affect heading accuracy.
- _The vehicle is operated in a region with significantly different magnetic characteristics._
Large changes in latitude, longitude, or magnetic inclination can require re-estimation of offsets.
- _QGroundControl reports magnetometer inconsistencies_.
For example, if you see the error `mag sensors inconsistent`.
- _Heading behavior does not match the vehicles observed orientation._
Symptoms include drifting yaw, sudden heading jumps when attempting to fly straight, and toilet bowling
- _QGroundControl_ sends the error `mag sensors inconsistent`.
This indicates that multiple magnetometers are reporting different headings.
## Additional Calibration/Configuration
The process above will autodetect, [set default rotations](../advanced_config/parameter_reference.md#SENS_MAG_AUTOROT), calibrate, and prioritise, all available magnetometers.
+1 -1
View File
@@ -2,7 +2,7 @@
<Badge type="tip" text="PX4 v1.15" />
PX4 implements the MAVLink [Standard Modes Protocol](https://mavlink.io/en/services/standard_modes.md) from PX4 v1.15, with a corresponding implementation in QGroundControl Daily builds (and future release builds).
PX4 implements the MAVLink [Standard Modes Protocol](https://mavlink.io/en/services/standard_modes.html) from PX4 v1.15, with a corresponding implementation in QGroundControl Daily builds (and future release builds).
The protocol allows you to discover all flight modes available to the vehicle, including PX4 External modes created using the [PX4 ROS 2 Control Interface](../ros2/px4_ros2_control_interface.md), and get or set the current mode.
+2
View File
@@ -899,6 +899,8 @@ fetching the latest mixing result and write them to PCA9685 at its scheduling ti
It can do full 12bits output as duty-cycle mode, while also able to output precious pulse width
that can be accepted by most ESCs and servos.
The I2C bus and address can be configured via parameters `PCA9685_EN_BUS` and `PCA9685_I2C_ADDR`, or via command line arguments.
### 示例
It is typically started with:
+1
View File
@@ -69,6 +69,7 @@ uint16 VEHICLE_CMD_DO_PARACHUTE=208 # Mission command to trigger a parachute. |a
uint16 VEHICLE_CMD_DO_MOTOR_TEST=209 # Motor test command. |Instance (@range 1, )|throttle type|throttle|timeout [s]|Motor count|Test order|Unused|
uint16 VEHICLE_CMD_DO_INVERTED_FLIGHT=210 # Change to/from inverted flight. |inverted (0=normal, 1=inverted)|Unused|Unused|Unused|Unused|Unused|Unused|
uint16 VEHICLE_CMD_DO_GRIPPER = 211 # Command to operate a gripper.
uint16 VEHICLE_CMD_DO_AUTOTUNE_ENABLE = 212 # Enable autotune module. |1 to enable|Unused|Unused|Unused|Unused|Unused|Unused|
uint16 VEHICLE_CMD_DO_SET_CAM_TRIGG_INTERVAL=214 # Mission command to set TRIG_INTERVAL for this flight. |[m] Camera trigger distance|Shutter integration time (ms)|Unused|Unused|Unused|Unused|Unused|
uint16 VEHICLE_CMD_DO_MOUNT_CONTROL_QUAT=220 # Mission command to control a camera or antenna mount, using a quaternion as reference. |q1 - quaternion param #1, w (1 in null-rotation)|q2 - quaternion param #2, x (0 in null-rotation)|q3 - quaternion param #3, y (0 in null-rotation)|q4 - quaternion param #4, z (0 in null-rotation)|Unused|Unused|Unused|
uint16 VEHICLE_CMD_DO_GUIDED_MASTER=221 # Set id of master controller. |System ID|Component ID|Unused|Unused|Unused|Unused|Unused|
+1
View File
@@ -55,6 +55,7 @@ Please continue reading for [upgrade instructions](#upgrade-guide).
### 传感器
- Add [sbgECom INS driver](../sensor/sbgecom.md) ([PX4-Autopilot#24137](https://github.com/PX4/PX4-Autopilot/pull/24137))
- Quick magnetometer calibration now supports specifying an arbitrary initial heading ([PX4-Autopilot#24637](https://github.com/PX4/PX4-Autopilot/pull/24637))
### 仿真
+2 -2
View File
@@ -552,8 +552,8 @@ ros2 run ros_gz_bridge parameter_bridge /clock@rosgraph_msgs/msg/Clock[gz.msgs.C
代码首先导入了与 ROS 2 中间件进行交互所需的 C++ 库,以及该节点所订阅的SensorCombined消息对应的头部文件:
```cpp
#include <0>
#include <1>
#include <rclcpp/rclcpp.hpp>
#include <px4_msgs/msg/sensor_combined.hpp>
```
随后,代码创建了一个 SensorCombinedListener 类,该类继承自通用的 rclcpp::Node 基类。
+22
View File
@@ -0,0 +1,22 @@
# Auxiliary global position
# This message provides global position data from an external source such as
# radio-triangulation, viusal navigation, or other positioning system.
uint32 MESSAGE_VERSION = 0
uint64 timestamp # [us] Time since system start
uint64 timestamp_sample # [us] Timestamp of the raw data
uint8 id # Unique identifier for the AGP sourcxe, 1X for Visual Navigation, 2X for Radio Triangulation
float64 lat # [deg] Latitude in WGS84
float64 lon # [deg] Longitude in WGS84
float32 alt # [m] Altitude above mean sea level (AMSL)
float32 eph # [m] Standard deviation of horizontal position error
float32 epv # [m] Standard deviation of vertical position error
uint8 lat_lon_reset_counter # Counter for reset events on horizontal position coordinates
# TOPICS aux_global_position
+2
View File
@@ -47,6 +47,7 @@ set(msg_files
Airspeed.msg
AirspeedWind.msg
AutotuneAttitudeControlStatus.msg
AuxGlobalPosition.msg
BatteryInfo.msg
ButtonEvent.msg
CameraCapture.msg
@@ -63,6 +64,7 @@ set(msg_files
DebugKeyValue.msg
DebugValue.msg
DebugVect.msg
DeviceInformation.msg
DifferentialPressure.msg
DistanceSensor.msg
DistanceSensorModeChangeRequest.msg
+33
View File
@@ -0,0 +1,33 @@
# Device information
#
# Can be used to uniquely associate a device_id from a sensor topic with a physical device using serial number.
# as well as tracking of the used firmware versions on the devices.
uint64 timestamp # time since system start (microseconds)
uint8 device_type # [@enum DEVICE_TYPE] Type of the device. Matches MAVLink DEVICE_TYPE enum
uint8 DEVICE_TYPE_GENERIC = 0 # Generic/unknown sensor
uint8 DEVICE_TYPE_AIRSPEED = 1 # Airspeed sensor
uint8 DEVICE_TYPE_ESC = 2 # ESC
uint8 DEVICE_TYPE_SERVO = 3 # Servo
uint8 DEVICE_TYPE_GPS = 4 # GPS
uint8 DEVICE_TYPE_MAGNETOMETER = 5 # Magnetometer
uint8 DEVICE_TYPE_PARACHUTE = 6 # Parachute
uint8 DEVICE_TYPE_RANGEFINDER = 7 # Rangefinder
uint8 DEVICE_TYPE_WINCH = 8 # Winch
uint8 DEVICE_TYPE_BAROMETER = 9 # Barometer
uint8 DEVICE_TYPE_OPTICAL_FLOW = 10 # Optical flow
uint8 DEVICE_TYPE_ACCELEROMETER = 11 # Accelerometer
uint8 DEVICE_TYPE_GYROSCOPE = 12 # Gyroscope
uint8 DEVICE_TYPE_DIFFERENTIAL_PRESSURE = 13 # Differential pressure
uint8 DEVICE_TYPE_BATTERY = 14 # Battery
uint8 DEVICE_TYPE_HYGROMETER = 15 # Hygrometer
char[32] vendor_name # Name of the device vendor
char[32] model_name # Name of the device model
uint32 device_id # [-] [@invalid 0 if not available] Unique device ID for the sensor. Does not change between power cycles.
char[24] firmware_version # [-] [@invalid empty if not available] Firmware version.
char[24] hardware_version # [-] [@invalid empty if not available] Hardware version.
char[33] serial_number # [-] [@invalid empty if not available] Device serial number or unique identifier.
+1
View File
@@ -16,6 +16,7 @@ uint8 GPS_CHECK_FAIL_MAX_VERT_DRIFT = 7 # 7 : maximum allowed vertical position
uint8 GPS_CHECK_FAIL_MAX_HORZ_SPD_ERR = 8 # 8 : maximum allowed horizontal speed fail - requires stationary vehicle
uint8 GPS_CHECK_FAIL_MAX_VERT_SPD_ERR = 9 # 9 : maximum allowed vertical velocity discrepancy fail
uint8 GPS_CHECK_FAIL_SPOOFED = 10 # 10 : GPS signal is spoofed
uint8 GPS_CHECK_FAIL_JAMMED = 11 # 11 : GPS signal is jammed
uint64 control_mode_flags # Bitmask to indicate EKF logic state
uint8 CS_TILT_ALIGN = 0 # 0 - true if the filter tilt alignment is complete
+1
View File
@@ -1,4 +1,5 @@
# GPIO mask and state
uint8 MAX_INSTANCES = 8
uint64 timestamp # time since system start (microseconds)
uint32 device_id # Device id
+5 -1
View File
@@ -2,9 +2,13 @@
uint64 timestamp # time since system start (microseconds)
uint8 INSTANCE_MAIN = 0
uint8 INSTANCE_SECONDARY = 1
uint8 instance # Instance of GNSS receiver
uint32 device_id
uint8 len # length of data, MSB bit set = message to the gps device,
# clear = message from the device
uint8[79] data # data to write to the log
uint8 ORB_QUEUE_LENGTH = 8
uint8 ORB_QUEUE_LENGTH = 16
+3 -3
View File
@@ -21,9 +21,9 @@ uint8 cell_count # [@invalid 0] Number of cells
uint8 source # [@enum SOURCE] Battery source
uint8 SOURCE_POWER_MODULE = 0 # Power module
uint8 SOURCE_EXTERNAL = 1 # External
uint8 SOURCE_ESCS = 2 # ESCs
uint8 SOURCE_POWER_MODULE = 0 # Power module (analog ADC or I2C power monitor)
uint8 SOURCE_EXTERNAL = 1 # External (MAVLink, CAN, or external driver)
uint8 SOURCE_ESCS = 2 # ESCs (via ESC telemetry)
uint8 priority # Zero based priority is the connection on the Power Controller V1..Vn AKA BrickN-1
uint16 capacity # [mAh] Capacity of the battery when fully charged
+4 -4
View File
@@ -1,6 +1,6 @@
# Battery status
#
# Battery status information for up to 4 battery instances.
# Battery status information for up to 3 battery instances.
# These are populated from power module and smart battery device drivers, and one battery updated from MAVLink.
# Battery instance information is also logged and streamed in MAVLink telemetry.
@@ -22,9 +22,9 @@ uint8 cell_count # [-] [@invalid 0] Number of cells
uint8 source # [@enum SOURCE] Battery source
uint8 SOURCE_POWER_MODULE = 0 # Power module
uint8 SOURCE_EXTERNAL = 1 # External
uint8 SOURCE_ESCS = 2 # ESCs
uint8 SOURCE_POWER_MODULE = 0 # Power module (analog ADC or I2C power monitor)
uint8 SOURCE_EXTERNAL = 1 # External (MAVLink, CAN, or external driver)
uint8 SOURCE_ESCS = 2 # ESCs (via ESC telemetry)
uint8 priority # [-] Zero based priority is the connection on the Power Controller V1..Vn AKA BrickN-1
uint16 capacity # [mAh] Capacity of the battery when fully charged
-1
View File
@@ -34,4 +34,3 @@ bool dead_reckoning # True if this position is estimated through dead-reckoning
# TOPICS vehicle_global_position vehicle_global_position_groundtruth external_ins_global_position
# TOPICS estimator_global_position
# TOPICS aux_global_position
@@ -31,5 +31,4 @@
#
############################################################################
add_subdirectory(mcp23009)
add_subdirectory(mcp)
@@ -30,7 +30,7 @@
# POSSIBILITY OF SUCH DAMAGE.
#
############################################################################
px4_add_library(platform_gpio_mcp23009
mcp23009.cpp
px4_add_library(platform_gpio_mcp
mcp.cpp
)
target_link_libraries(platform_gpio_mcp23009 PRIVATE drivers__device) # device::I2C
target_link_libraries(platform_gpio_mcp PRIVATE drivers__device) # device::I2C
@@ -0,0 +1,169 @@
/****************************************************************************
*
* 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.
*
****************************************************************************/
#include <nuttx/ioexpander/gpio.h>
#include <lib/drivers/device/Device.hpp>
#include <px4_platform/gpio/mcp.hpp>
#include <uORB/topics/gpio_config.h>
#include <uORB/topics/gpio_out.h>
#include <uORB/PublicationMulti.hpp>
#include <drivers/drv_hrt.h>
static int mcp230XX_read(struct gpio_dev_s *dev, bool *value)
{
mcp_gpio_dev_s *gpio = (struct mcp_gpio_dev_s *)dev;
*value = gpio->callback_handler->input & gpio->mask;
return OK;
}
static uORB::PublicationMulti<gpio_out_s> toGpioOut{ORB_ID(gpio_out)};
static int mcp230XX_write(struct gpio_dev_s *dev, bool value)
{
mcp_gpio_dev_s *gpio = (struct mcp_gpio_dev_s *)dev;
gpio_out_s msg{
hrt_absolute_time(),
gpio->callback_handler->dev_id,
gpio->mask, // clear mask
value ? gpio->mask : 0u, // set mask
};
return toGpioOut.publish(msg) ? OK : -ETIMEDOUT;
}
static uORB::PublicationMulti<gpio_config_s> toGpioConfig{ORB_ID(gpio_config)};
static int mcp230XX_setpintype(struct gpio_dev_s *dev, enum gpio_pintype_e pintype)
{
mcp_gpio_dev_s *gpio = (struct mcp_gpio_dev_s *)dev;
gpio_config_s msg{
hrt_absolute_time(),
gpio->callback_handler->dev_id,
gpio->mask,
};
switch (pintype) {
case GPIO_INPUT_PIN:
msg.config = gpio_config_s::INPUT;
break;
case GPIO_INPUT_PIN_PULLUP:
msg.config = gpio_config_s::INPUT_PULLUP;
break;
case GPIO_OUTPUT_PIN:
msg.config = gpio_config_s::OUTPUT;
break;
default:
return -ENOTSUP;
}
return toGpioConfig.publish(msg) ? OK : -ETIMEDOUT;
}
static const struct gpio_operations_s mcp_gpio_ops {
mcp230XX_read,
mcp230XX_write,
nullptr,
nullptr,
mcp230XX_setpintype,
};
int mcp230XX_register_gpios(uint8_t i2c_bus, uint8_t i2c_addr, int first_minor, uint16_t dir_mask, int num_pins, uint8_t device_type,
mcp_gpio_dev_s *_gpio)
{
bool all_registered = true;
for (int i = 0; i < num_pins; i++) {
uint16_t mask = 1u << i;
if (!_gpio[i].registered) {
if (dir_mask & mask) {
_gpio[i] = { {GPIO_INPUT_PIN, {}, &mcp_gpio_ops}, mask, false, nullptr};
} else {
_gpio[i] = { {GPIO_OUTPUT_PIN, {}, &mcp_gpio_ops}, mask, false, nullptr};
}
int ret = gpio_pin_register(&_gpio[i].gpio, first_minor + i);
if (ret != OK) {
all_registered = false;
} else {
_gpio[i].registered = true;
}
}
}
const auto device_id = device::Device::DeviceId{device::Device::DeviceBusType_I2C, i2c_bus, i2c_addr, device_type};
CallbackHandler *callback_handler = new CallbackHandler(ORB_ID(gpio_in));
callback_handler->dev_id = device_id.devid;
bool callback_registered = callback_handler->registerCallback();
if (!all_registered || !callback_registered) {
for (int i = 0; i < num_pins; i++) {
if (_gpio[i].registered) {
gpio_pin_unregister(&_gpio[i].gpio, first_minor + i);
_gpio[i].registered = false;
}
}
callback_handler->unregisterCallback();
delete callback_handler;
return ERROR;
}
for (int i = 0; i < num_pins; i++) {
_gpio[i].callback_handler = callback_handler;
}
return OK;
}
int mcp230XX_unregister_gpios(int first_minor, int num_pins, mcp_gpio_dev_s *_gpio)
{
if (_gpio[0].callback_handler) {
CallbackHandler *callback_handler = _gpio[0].callback_handler;
callback_handler->unregisterCallback();
delete callback_handler;
}
for (int i = 0; i < num_pins; ++i) {
if (_gpio[i].registered) {
mcp230XX_setpintype(&_gpio[i].gpio, GPIO_INPUT_PIN);
gpio_pin_unregister(&_gpio[i].gpio, first_minor + i);
_gpio[i].registered = false;
_gpio[i].callback_handler = nullptr;
}
}
return OK;
}
@@ -1,172 +0,0 @@
/****************************************************************************
*
* Copyright (c) 2023 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.
*
****************************************************************************/
#include <nuttx/ioexpander/gpio.h>
#include <drivers/drv_sensor.h>
#include <lib/drivers/device/Device.hpp>
#include <uORB/topics/gpio_config.h>
#include <uORB/topics/gpio_in.h>
#include <uORB/topics/gpio_out.h>
#include <uORB/topics/gpio_request.h>
#include <uORB/Publication.hpp>
#include <uORB/SubscriptionCallback.hpp>
static uint32_t DEVID{0};
struct mcp23009_gpio_dev_s {
struct gpio_dev_s gpio;
uint8_t mask;
};
/* Copy the read input data */
class ReadCallback : public uORB::SubscriptionCallback
{
public:
using SubscriptionCallback::SubscriptionCallback;
void call() override
{
px4::msg::GpioIn new_input;
if (update(&new_input) && new_input.device_id == DEVID) {
input = new_input.state;
}
}
uint8_t input;
};
static uORB::Publication<px4::msg::GpioRequest> toGpioRequest{ORB_ID(gpio_request)};
static ReadCallback fromGpioIn{ORB_ID(gpio_in)};
static int mcp23009_read(struct gpio_dev_s *dev, bool *value)
{
mcp23009_gpio_dev_s *gpio = (struct mcp23009_gpio_dev_s *)dev;
*value = fromGpioIn.input & gpio->mask;
return OK;
}
static uORB::Publication<gpio_out_s> toGpioOut{ORB_ID(gpio_out)};
static int mcp23009_write(struct gpio_dev_s *dev, bool value)
{
mcp23009_gpio_dev_s *gpio = (struct mcp23009_gpio_dev_s *)dev;
gpio_out_s msg{
hrt_absolute_time(),
DEVID,
gpio->mask, // clear mask
value ? gpio->mask : 0u, // set mask
};
return toGpioOut.publish(msg) ? OK : -ETIMEDOUT;
}
static uORB::Publication<gpio_config_s> toGpioConfig{ORB_ID(gpio_config)};
static int mcp23009_setpintype(struct gpio_dev_s *dev, enum gpio_pintype_e pintype)
{
mcp23009_gpio_dev_s *gpio = (struct mcp23009_gpio_dev_s *)dev;
gpio_config_s msg{
hrt_absolute_time(),
DEVID,
gpio->mask,
};
switch (pintype) {
case GPIO_INPUT_PIN:
msg.config = gpio_config_s::INPUT;
break;
case GPIO_INPUT_PIN_PULLUP:
msg.config = gpio_config_s::INPUT_PULLUP;
break;
case GPIO_OUTPUT_PIN:
msg.config = gpio_config_s::OUTPUT;
break;
default:
return -ENOTSUP;
}
return toGpioConfig.publish(msg) ? OK : -ETIMEDOUT;
}
// ----------------------------------------------------------------------------
static const struct gpio_operations_s mcp23009_gpio_ops {
mcp23009_read,
mcp23009_write,
nullptr,
nullptr,
mcp23009_setpintype,
};
static constexpr uint8_t NUM_GPIOS = 8;
static mcp23009_gpio_dev_s _gpio[NUM_GPIOS] {
{ {GPIO_INPUT_PIN, {}, &mcp23009_gpio_ops}, (1u << 0) },
{ {GPIO_INPUT_PIN, {}, &mcp23009_gpio_ops}, (1u << 1) },
{ {GPIO_INPUT_PIN, {}, &mcp23009_gpio_ops}, (1u << 2) },
{ {GPIO_INPUT_PIN, {}, &mcp23009_gpio_ops}, (1u << 3) },
{ {GPIO_INPUT_PIN, {}, &mcp23009_gpio_ops}, (1u << 4) },
{ {GPIO_INPUT_PIN, {}, &mcp23009_gpio_ops}, (1u << 5) },
{ {GPIO_INPUT_PIN, {}, &mcp23009_gpio_ops}, (1u << 6) },
{ {GPIO_INPUT_PIN, {}, &mcp23009_gpio_ops}, (1u << 7) }
};
// ----------------------------------------------------------------------------
int mcp23009_register_gpios(uint8_t i2c_bus, uint8_t i2c_addr, int first_minor)
{
const auto device_id = device::Device::DeviceId{
device::Device::DeviceBusType_I2C, i2c_bus, i2c_addr, DRV_GPIO_DEVTYPE_MCP23009};
DEVID = device_id.devid;
for (int i = 0; i < NUM_GPIOS; ++i) {
int ret = gpio_pin_register(&_gpio[i].gpio, first_minor + i);
if (ret != OK) {
return ret;
}
}
fromGpioIn.registerCallback();
return OK;
}
int mcp23009_unregister_gpios(int first_minor)
{
for (int i = 0; i < NUM_GPIOS; ++i) {
mcp23009_setpintype(&_gpio[i].gpio, GPIO_INPUT_PIN);
gpio_pin_unregister(&_gpio[i].gpio, first_minor + i);
}
fromGpioIn.unregisterCallback();
return OK;
}
@@ -0,0 +1,53 @@
/****************************************************************************
*
* 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.
*
****************************************************************************/
#pragma once
#include <stdint.h>
#include <nuttx/config.h>
#include <nuttx/ioexpander/gpio.h>
#include <lib/drivers/mcp_common/CallbackHandler.hpp>
#ifndef CONFIG_DEV_GPIO
# error "CONFIG_DEV_GPIO is required to use MCP GPIO expander, enable it in your NuttX config"
#endif
struct mcp_gpio_dev_s {
struct gpio_dev_s gpio;
uint16_t mask;
bool registered = false;
CallbackHandler *callback_handler;
};
int mcp230XX_register_gpios(uint8_t i2c_bus, uint8_t i2c_addr, int first_minor = 0, uint16_t dir_mask = 0x0000, int num_pins = 8,
uint8_t device_type = 0, mcp_gpio_dev_s *_gpio = nullptr);
int mcp230XX_unregister_gpios(int first_minor = 0, int num_pins = 0, mcp_gpio_dev_s *_gpio = nullptr);
@@ -889,6 +889,7 @@ int io_timer_channel_init(unsigned channel, io_timer_channel_mode_t mode,
case IOTimerChanMode_Capture:
setbits = CCMR_C1_CAPTURE_INIT;
gpio = timer_io_channels[channel].gpio_in | GPIO_PULLUP;
break;
case IOTimerChanMode_CaptureDMA:
+50 -29
View File
@@ -39,27 +39,15 @@
#define VL53L1X_DELAY 50000 // delay to reduce CPU usage (us)
#define VL53L1X_SAMPLE_RATE 200 // ms, default
#define VL53L1X_INTER_MEAS_MS 200 // ms
#define VL53L1X_INTER_MEAS_MS 250 // ms
#define VL53L1X_SHORT_RANGE 1 // sub-2 meter distance mode
#define VL53L1X_LONG_RANGE 2 // sub-4 meter distance mode
#define VL53L1X_RANGE_STATUS_OUT_OF_BOUNDS 13 // region of interest out of bounds error
#define VL53L1X_RANGE_STATUS_OK 0 // range status ok
#define VL53L1X_ROI_FAR_RIGHT 247 // ROI far right of optical center
#define VL53L1X_ROI_MID_RIGHT 215 // ROI middle right of optical center
#define VL53L1X_ROI_CENTER_LEFT 183 // ROI optical center left
#define VL53L1X_ROI_MID_RIGHT 231 // ROI middle right of optical center
#define VL53L1X_ROI_MID_LEFT 167 // ROI middle left of optical center
#define VL53L1X_ROI_FAR_LEFT 151 // ROI far left of optical center
#define VL53L1X_ROI_FAR_RIGHT_LO 10 // ROI far right lo
#define VL53L1X_ROI_MID_RIGHT_LO 42 // ROI middle right of optical center
#define VL53L1X_ROI_CENTER_LEFT_LO 74 // ROI optical center left lo
#define VL53L1X_ROI_MID_LEFT_LO 90 // ROI middle left of optical center
#define VL53L1X_ROI_FAR_LEFT_LO 106 // ROI far left lo
#define VL53L1X_ROI_FAR_RIGHT_HI 243 // ROI far right hi
#define VL53L1X_ROI_MID_RIGHT_HI 211 // ROI middle right of optical center
#define VL53L1X_ROI_CENTER_LEFT_HI 179 // ROI optical center left hi
#define VL53L1X_ROI_MID_LEFT_HI 163 // ROI middle left of optical center
#define VL53L1X_ROI_FAR_LEFT_HI 147 // ROI far left hi
using namespace matrix;
/* ST */
const uint8_t VL51L1X_DEFAULT_CONFIGURATION[] = {
0x00, /* 0x2d : set bit 2 and 5 to 1 for fast plus mode (1MHz I2C), else don't touch */
@@ -164,7 +152,7 @@ static const uint8_t status_rtn[24] = { 255, 255, 255, 5, 2, 4, 1, 7, 3, 0,
// ROI array assignment
uint8_t roi_center[] = {VL53L1X_ROI_FAR_RIGHT, VL53L1X_ROI_MID_RIGHT, VL53L1X_ROI_CENTER_LEFT, VL53L1X_ROI_MID_LEFT, VL53L1X_ROI_FAR_LEFT, VL53L1X_ROI_FAR_RIGHT_LO, VL53L1X_ROI_MID_RIGHT_LO, VL53L1X_ROI_CENTER_LEFT_LO, VL53L1X_ROI_MID_LEFT_LO, VL53L1X_ROI_FAR_LEFT_LO, VL53L1X_ROI_FAR_RIGHT_HI, VL53L1X_ROI_MID_RIGHT_HI, VL53L1X_ROI_CENTER_LEFT_HI, VL53L1X_ROI_MID_LEFT_HI, VL53L1X_ROI_FAR_LEFT_HI};
uint8_t roi_center[] = { VL53L1X_ROI_MID_RIGHT, VL53L1X_ROI_MID_LEFT};
VL53L1X::VL53L1X(const I2CSPIDriverConfig &config) :
I2C(config),
@@ -173,6 +161,8 @@ VL53L1X::VL53L1X(const I2CSPIDriverConfig &config) :
{
// Set distance mode (1 for ~2m ranging, 2 for ~4m ranging
_distance_mode = VL53L1X_LONG_RANGE;
_sensor_orientation = config.rotation;
// VL53L1X typical range 0-4 meters with 27 degree field of view
_px4_rangefinder.set_min_distance(0.f);
@@ -190,6 +180,7 @@ VL53L1X::VL53L1X(const I2CSPIDriverConfig &config) :
// Zone index
_zone_index = 0;
// Allow 3 retries as the device typically misses the first measure attempts.
I2C::_retries = 3;
@@ -208,12 +199,13 @@ int VL53L1X::collect()
uint8_t ret = 0;
uint8_t rangeStatus = VL53L1X_RANGE_STATUS_OK;
uint16_t distance_mm = 0;
static constexpr float DOWNWARD_FACING_THETA = -1.57f;
static constexpr float RIGHT_PSI = 0.117809725;
static constexpr float LEFT_PSI = -0.117809725;
perf_begin(_sample_perf);
const hrt_abstime timestamp_sample = hrt_absolute_time();
ret = VL53L1X_GetRangeStatus(&rangeStatus);
const int8_t quality = VL53L1X_GetRangeStatus(&rangeStatus);
if ((ret != PX4_OK) | (rangeStatus == VL53L1X_RANGE_STATUS_OUT_OF_BOUNDS)) {
perf_count(_comms_errors);
@@ -221,9 +213,18 @@ int VL53L1X::collect()
return PX4_ERROR;
}
const hrt_abstime timestamp_sample = hrt_absolute_time();
ret = VL53L1X_GetDistance(&distance_mm);
ret |= VL53L1X_ClearInterrupt();
// zone modulus increment
_zone_index = (_zone_index + 1) % _zone_limit;
// Set the ROI center based on zone incrementation
ret |= VL53L1X_SetROICenter(roi_center[_zone_index]);
if (ret != PX4_OK) {
perf_count(_comms_errors);
perf_end(_sample_perf);
@@ -233,8 +234,34 @@ int VL53L1X::collect()
perf_end(_sample_perf);
float distance_m = distance_mm / 1000.f;
float psi = 0.f;
float theta = 0.f;
_px4_rangefinder.update(timestamp_sample, distance_m);
if (_sensor_orientation == distance_sensor_s::ROTATION_DOWNWARD_FACING) {
theta = DOWNWARD_FACING_THETA;
}
float phi = 0.f;
switch (roi_center[_zone_index]) {
case VL53L1X_ROI_MID_RIGHT:
psi = RIGHT_PSI; // Approximately 7 degrees;
break;
case VL53L1X_ROI_MID_LEFT:
psi = LEFT_PSI;
break;
default:
break;
}
Quaternionf quat_e = Eulerf(phi, theta, psi);
float dist_q[] {quat_e(0), quat_e(1), quat_e(2), quat_e(3)};
_px4_rangefinder.update(timestamp_sample, distance_m, quality, dist_q);
return PX4_OK;
}
@@ -264,7 +291,6 @@ void VL53L1X::RunImpl()
{
uint8_t dataReady = 0;
VL53L1X_CheckForDataReady(&dataReady);
if (dataReady == 1) {
@@ -273,10 +299,7 @@ void VL53L1X::RunImpl()
// Reduce CPU usage
ScheduleDelayed(VL53L1X_DELAY);
// zone modulus increment
_zone_index = (_zone_index + 1) % _zone_limit;
// Set the ROI center based on zone incrementation
VL53L1X_SetROICenter(roi_center[_zone_index]);
}
int VL53L1X::init()
@@ -290,10 +313,8 @@ int VL53L1X::init()
}
// Spad width (x) & height (y)
uint8_t x = 4;
uint8_t y = 4;
uint8_t x = 8;
uint8_t y = 16;
ret |= VL53L1X_SensorInit();
ret |= VL53L1X_ConfigBig(_distance_mode, VL53L1X_SAMPLE_RATE);
@@ -48,7 +48,7 @@
#include <drivers/drv_hrt.h>
#include <lib/perf/perf_counter.h>
#include <lib/drivers/rangefinder/PX4Rangefinder.hpp>
#include <matrix/math.hpp>
/* ST */
#define SOFT_RESET 0x0000
#define VL53L1_I2C_SLAVE__DEVICE_ADDRESS 0x0001
@@ -157,7 +157,7 @@ private:
int8_t VL53L1X_SetROICenter(uint8_t data);
int8_t VL53L1X_SetROI(uint16_t x, uint16_t y);
PX4Rangefinder _px4_rangefinder;
Rotation _sensor_orientation{ROTATION_PITCH_270};
perf_counter_t _comms_errors{perf_alloc(PC_COUNT, MODULE_NAME": com_err")};
perf_counter_t _sample_perf{perf_alloc(PC_ELAPSED, MODULE_NAME": read")};
};
+1
View File
@@ -201,6 +201,7 @@
#define DRV_DIST_DEVTYPE_GY_US42 0x9C
#define DRV_BAT_DEVTYPE_BATMON_SMBUS 0x9d
#define DRV_GPIO_DEVTYPE_MCP23017 0x9E
#define DRV_GPIO_DEVTYPE_MCP23009 0x9F
#define DRV_GPS_DEVTYPE_ASHTECH 0xA0
+2 -1
View File
@@ -1728,7 +1728,8 @@ void SeptentrioDriver::publish_rtcm_corrections(uint8_t *data, size_t len)
void SeptentrioDriver::dump_gps_data(const uint8_t *data, size_t len, DataDirection data_direction)
{
gps_dump_s *dump_data = data_direction == DataDirection::FromReceiver ? _message_data_from_receiver : _message_data_to_receiver;
dump_data->instance = _instance == Instance::Main ? 0 : 1;
dump_data->instance = _instance == Instance::Main ? gps_dump_s::INSTANCE_MAIN : gps_dump_s::INSTANCE_SECONDARY;
dump_data->device_id = get_device_id();
while (len > 0) {
size_t write_len = len;
-6
View File
@@ -1,9 +1,3 @@
menu "GPIO"
menuconfig COMMON_GPIO
bool "Common GPIOs"
default n
select DRIVERS_GPIO_MCP23009
---help---
Enable default set of GPIO drivers
rsource "*/Kconfig"
endmenu
+5 -4
View File
@@ -30,12 +30,13 @@
# POSSIBILITY OF SUCH DAMAGE.
#
############################################################################
px4_add_module(
MODULE drivers__mcp23009
MODULE drivers__gpio__mcp23009
MAIN mcp23009
COMPILE_FLAGS
SRCS
mcp23009_main.cpp
mcp23009.cpp
MCP23009_main.cpp
MCP23009.cpp
DEPENDS
mcp_common
)
+2 -2
View File
@@ -1,5 +1,5 @@
menuconfig DRIVERS_GPIO_MCP23009
bool "mcp23009"
bool "MCP23009"
default n
---help---
Enable support for mcp23009
Enable support for MCP23009 8-bit GPIO expander
+92
View File
@@ -0,0 +1,92 @@
/****************************************************************************
*
* 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.
*
****************************************************************************/
#include "MCP23009.hpp"
MCP23009::MCP23009(const I2CSPIDriverConfig &config) :
MCP230XX(config)
{
}
int MCP23009::get_olat(int bank, uint8_t *addr)
{
switch (bank) {
case 0:
*addr = (uint8_t) Register::OLAT;
return PX4_OK;
default:
return PX4_ERROR;
}
}
int MCP23009::get_gppu(int bank, uint8_t *addr)
{
switch (bank) {
case 0:
*addr = (uint8_t) Register::GPPU;
return PX4_OK;
default:
return PX4_ERROR;
}
}
int MCP23009::get_iodir(int bank, uint8_t *addr)
{
switch (bank) {
case 0:
*addr = (uint8_t) Register::IODIR;
return PX4_OK;
default:
return PX4_ERROR;
}
}
int MCP23009::get_gpio(int bank, uint8_t *addr)
{
switch (bank) {
case 0:
*addr = (uint8_t) Register::GPIO;
return PX4_OK;
default:
return PX4_ERROR;
}
}
int MCP23009::get_probe_reg(uint8_t *addr)
{
*addr = (uint8_t) Register::IOCON;
return PX4_OK;
}
+64
View File
@@ -0,0 +1,64 @@
/****************************************************************************
*
* 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.
*
****************************************************************************/
#pragma once
#include <lib/drivers/mcp_common/MCP.hpp>
using namespace time_literals;
class MCP23009 : public MCP230XX
{
public:
MCP23009(const I2CSPIDriverConfig &config);
private:
enum class
Register : uint8_t {
IODIR = 0x00,
IPOL = 0x01,
GPINTEN = 0x02,
DEFVAL = 0x03,
INTCON = 0x04,
IOCON = 0x05,
GPPU = 0x06,
INTF = 0x07,
INTCAP = 0x08,
GPIO = 0x09,
OLAT = 0x0a
};
int get_olat(int bank, uint8_t *addr) override;
int get_gppu(int bank, uint8_t *addr) override;
int get_iodir(int bank, uint8_t *addr) override;
int get_gpio(int bank, uint8_t *addr) override;
int get_probe_reg(uint8_t *addr) override;
};
+102
View File
@@ -0,0 +1,102 @@
/****************************************************************************
*
* 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.
*
****************************************************************************/
#include "MCP23009.hpp"
constexpr MCP230XX_config_t def_mcp_config{
.device_type = DRV_GPIO_DEVTYPE_MCP23009,
.i2c_addr = I2C_ADDRESS_MCP23009,
.num_pins = 8,
.num_banks = 1,
};
extern "C" int mcp23009_main(int argc, char *argv[])
{
using ThisDriver = MCP23009;
BusCLIArguments cli{true, false};
cli.default_i2c_frequency = 400000;
cli.i2c_address = 0x25;
MCP230XX_config_t mcp_config = def_mcp_config;
int ch;
while ((ch = cli.getOpt(argc, argv, "D:O:P:U:R:M:")) != EOF) {
switch (ch) {
case 'D':
mcp_config.direction = (int)strtol(cli.optArg(), nullptr, 0);
break;
case 'O':
mcp_config.state = (int)strtol(cli.optArg(), nullptr, 0);
break;
case 'P':
mcp_config.pullup = (int)strtol(cli.optArg(), nullptr, 0);
break;
case 'U':
mcp_config.interval = (uint16_t)atoi(cli.optArg());
break;
case 'M':
mcp_config.first_minor = (uint8_t)atoi(cli.optArg());
break;
}
}
const char *verb = cli.optArg();
if (!verb) {
MCP230XX::print_usage();
return -1;
}
cli.custom_data = &mcp_config;
BusInstanceIterator iterator("MCP23009", cli, mcp_config.device_type);
if (!strcmp(verb, "start")) {
return ThisDriver::module_start(cli, iterator);
}
if (!strcmp(verb, "stop")) {
return ThisDriver::module_stop(iterator);
}
if (!strcmp(verb, "status")) {
return ThisDriver::module_status(iterator);
}
MCP230XX::print_usage();
return -1;
}
-123
View File
@@ -1,123 +0,0 @@
/****************************************************************************
*
* Copyright (c) 2023 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.
*
****************************************************************************/
#include "mcp23009.h"
int MCP23009::read_reg(Register address, uint8_t &data)
{
return transfer((uint8_t *)&address, 1, &data, 1);
}
int MCP23009::write_reg(Register address, uint8_t value)
{
uint8_t data[2] = {(uint8_t)address, value};
return transfer(data, sizeof(data), nullptr, 0);
}
int MCP23009::init(uint8_t direction, uint8_t state, uint8_t pull_up)
{
// do I2C init (and probe) first
int ret = I2C::init();
if (ret != PX4_OK) {
PX4_ERR("I2C init failed");
return ret;
}
// buffer the new initial states
_iodir = direction;
_olat = state;
_gppu = pull_up;
// Write the initial state to the device
ret = write_reg(Register::OLAT, _olat);
ret |= write_reg(Register::IODIR, _iodir);
ret |= write_reg(Register::GPPU, _gppu);
if (ret != PX4_OK) {
PX4_ERR("Device init failed (%i)", ret);
return ret;
}
return init_uorb();
}
int MCP23009::probe()
{
// no whoami, try to read IOCON
uint8_t data;
return read_reg(Register::IOCON, data);
}
int MCP23009::read(uint8_t *mask)
{
return read_reg(Register::GPIO, *mask);
}
int MCP23009::write(uint8_t mask_set, uint8_t mask_clear)
{
// no need to read, we can use the buffered register value
_olat = (_olat & ~mask_clear) | mask_set;
return write_reg(Register::OLAT, _olat);
}
int MCP23009::configure(uint8_t mask, PinType type)
{
// no need to read, we can use the buffered register values
switch (type) {
case PinType::Input:
_iodir |= mask;
_gppu &= ~mask;
break;
case PinType::InputPullUp:
_iodir |= mask;
_gppu |= mask;
break;
case PinType::Output:
_iodir &= ~mask;
break;
default:
return -EINVAL;
}
int ret = write_reg(Register::GPPU, _gppu);
if (ret != 0) {
return ret;
}
return write_reg(Register::IODIR, _iodir);
}
-215
View File
@@ -1,215 +0,0 @@
/****************************************************************************
*
* Copyright (C) 2023 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.
*
****************************************************************************/
/**
* Driver for the MCP23009 connected via I2C.
*/
#include "mcp23009.h"
#include <px4_platform_common/module.h>
MCP23009::MCP23009(const I2CSPIDriverConfig &config) :
I2C(config),
I2CSPIDriver(config),
_cycle_perf(perf_alloc(PC_ELAPSED, MODULE_NAME": single-sample"))
{
}
MCP23009::~MCP23009()
{
ScheduleClear();
perf_free(_cycle_perf);
}
int MCP23009::init_uorb()
{
if (!_gpio_config_sub.registerCallback() ||
!_gpio_request_sub.registerCallback() ||
!_gpio_out_sub.registerCallback()) {
PX4_ERR("callback registration failed");
return -1;
}
return PX4_OK;
}
void MCP23009::exit_and_cleanup()
{
_gpio_config_sub.unregisterCallback();
_gpio_request_sub.unregisterCallback();
_gpio_out_sub.unregisterCallback();
}
void MCP23009::RunImpl()
{
perf_begin(_cycle_perf);
gpio_config_s config;
if (_gpio_config_sub.update(&config) && config.device_id == get_device_id()) {
PinType type = PinType::Input;
switch (config.config) {
case config.INPUT_PULLUP: type = PinType::InputPullUp; break;
case config.OUTPUT: type = PinType::Output; break;
}
write(config.state, config.mask);
configure(config.mask, type);
}
gpio_out_s output;
if (_gpio_out_sub.update(&output) && output.device_id == get_device_id()) {
write(output.state, output.mask);
}
// read every time we run, either when requested or when scheduled on interval
{
gpio_in_s _gpio_in;
_gpio_in.timestamp = hrt_absolute_time();
_gpio_in.device_id = get_device_id();
uint8_t input;
read(&input);
_gpio_in.state = input;
_to_gpio_in.publish(_gpio_in);
}
perf_end(_cycle_perf);
}
void MCP23009::print_usage()
{
PRINT_MODULE_USAGE_NAME("MCP23009", "driver");
PRINT_MODULE_USAGE_COMMAND("start");
PRINT_MODULE_USAGE_PARAMS_I2C_SPI_DRIVER(true, false);
PRINT_MODULE_USAGE_PARAMS_I2C_ADDRESS(0x25);
PRINT_MODULE_USAGE_PARAM_INT('D', 0, 0, 255, "Direction", true);
PRINT_MODULE_USAGE_PARAM_INT('O', 0, 0, 255, "Output", true);
PRINT_MODULE_USAGE_PARAM_INT('P', 0, 0, 255, "Pullups", true);
PRINT_MODULE_USAGE_PARAM_INT('U', 0, 0, 1000, "Update Interval [ms]", true);
PRINT_MODULE_USAGE_DEFAULT_COMMANDS();
}
void MCP23009::print_status()
{
I2CSPIDriverBase::print_status();
perf_print_counter(_cycle_perf);
}
struct init_config_t {
uint16_t interval;
uint8_t direction;
uint8_t state;
uint8_t pullup;
};
I2CSPIDriverBase *MCP23009::instantiate(const I2CSPIDriverConfig &config, int runtime_instance)
{
auto *init = (const init_config_t *)config.custom_data;
auto *instance = new MCP23009(config);
if (!instance) {
PX4_ERR("alloc failed");
return nullptr;
}
if (OK != instance->init(init->direction, init->state, init->pullup)) {
delete instance;
return nullptr;
}
if (init->interval) {
instance->ScheduleOnInterval(init->interval * 1000);
}
return instance;
}
extern "C" int mcp23009_main(int argc, char *argv[])
{
using ThisDriver = MCP23009;
BusCLIArguments cli{true, false};
cli.default_i2c_frequency = 400000;
cli.i2c_address = 0x25;
init_config_t config_data{};
int ch;
while ((ch = cli.getOpt(argc, argv, "D:O:P:U:")) != EOF) {
switch (ch) {
case 'D':
config_data.direction = (int)strtol(cli.optArg(), nullptr, 0);
break;
case 'O':
config_data.state = (int)strtol(cli.optArg(), nullptr, 0);
break;
case 'P':
config_data.pullup = (int)strtol(cli.optArg(), nullptr, 0);
break;
case 'U':
config_data.interval = atoi(cli.optArg());
break;
}
}
cli.custom_data = &config_data;
const char *verb = cli.optArg();
if (!verb) {
ThisDriver::print_usage();
return -1;
}
BusInstanceIterator iterator(MODULE_NAME, cli, DRV_GPIO_DEVTYPE_MCP23009);
if (!strcmp(verb, "start")) {
return ThisDriver::module_start(cli, iterator);
}
if (!strcmp(verb, "stop")) {
return ThisDriver::module_stop(iterator);
}
if (!strcmp(verb, "status")) {
return ThisDriver::module_status(iterator);
}
ThisDriver::print_usage();
return -1;
}
+42
View File
@@ -0,0 +1,42 @@
############################################################################
#
# Copyright (c) 2023 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.
#
############################################################################
px4_add_module(
MODULE drivers__gpio__mcp23017
MAIN mcp23017
SRCS
MCP23017_main.cpp
MCP23017.cpp
DEPENDS
mcp_common
)
+5
View File
@@ -0,0 +1,5 @@
menuconfig DRIVERS_GPIO_MCP23017
bool "MCP23017"
default n
---help---
Enable support for MCP23017 16-bit GPIO expander
+108
View File
@@ -0,0 +1,108 @@
/****************************************************************************
*
* 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.
*
****************************************************************************/
#include "MCP23017.hpp"
MCP23017::MCP23017(const I2CSPIDriverConfig &config) :
MCP230XX(config)
{
}
int MCP23017::get_olat(int bank, uint8_t *addr)
{
switch (bank) {
case 0:
*addr = (uint8_t) Register::OLATA;
return PX4_OK;
case 1:
*addr = (uint8_t) Register::OLATB;
return PX4_OK;
default:
return PX4_ERROR;
}
}
int MCP23017::get_gppu(int bank, uint8_t *addr)
{
switch (bank) {
case 0:
*addr = (uint8_t) Register::GPPUA;
return PX4_OK;
case 1:
*addr = (uint8_t) Register::GPPUB;
return PX4_OK;
default:
return PX4_ERROR;
}
}
int MCP23017::get_iodir(int bank, uint8_t *addr)
{
switch (bank) {
case 0:
*addr = (uint8_t) Register::IODIRA;
return PX4_OK;
case 1:
*addr = (uint8_t) Register::IODIRB;
return PX4_OK;
default:
return PX4_ERROR;
}
}
int MCP23017::get_gpio(int bank, uint8_t *addr)
{
switch (bank) {
case 0:
*addr = (uint8_t) Register::GPIOA;
return PX4_OK;
case 1:
*addr = (uint8_t) Register::GPIOB;
return PX4_OK;
default:
return PX4_ERROR;
}
}
int MCP23017::get_probe_reg(uint8_t *addr)
{
*addr = (uint8_t) Register::IOCONA;
return PX4_OK;
}
+75
View File
@@ -0,0 +1,75 @@
/****************************************************************************
*
* 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.
*
****************************************************************************/
#pragma once
#include <lib/drivers/mcp_common/MCP.hpp>
using namespace time_literals;
class MCP23017 : public MCP230XX
{
public:
MCP23017(const I2CSPIDriverConfig &config);
private:
enum class
Register : uint8_t {
IODIRA = 0x00,
IODIRB = 0x01,
IPOLA = 0x02,
IPOLB = 0x03,
GPINTENA = 0x04,
GPINTENB = 0x05,
DEFVALA = 0x06,
DEFVALB = 0x07,
INTCONA = 0x08,
INTCONB = 0x09,
IOCONA = 0x0a,
IOCONB = 0x0b,
GPPUA = 0x0c,
GPPUB = 0x0d,
INTFA = 0x0e,
INTFB = 0x0f,
INTCAPA = 0x10,
INTCAPB = 0x11,
GPIOA = 0x12,
GPIOB = 0x13,
OLATA = 0x14,
OLATB = 0x15
};
int get_olat(int bank, uint8_t *addr) override;
int get_gppu(int bank, uint8_t *addr) override;
int get_iodir(int bank, uint8_t *addr) override;
int get_gpio(int bank, uint8_t *addr) override;
int get_probe_reg(uint8_t *addr) override;
};
+102
View File
@@ -0,0 +1,102 @@
/****************************************************************************
*
* 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.
*
****************************************************************************/
#include "MCP23017.hpp"
constexpr MCP230XX_config_t def_mcp_config{
.device_type = DRV_GPIO_DEVTYPE_MCP23017,
.i2c_addr = I2C_ADDRESS_MCP23017,
.num_pins = 16,
.num_banks = 2,
};
extern "C" int mcp23017_main(int argc, char *argv[])
{
using ThisDriver = MCP23017;
BusCLIArguments cli{true, false};
cli.default_i2c_frequency = 400000;
cli.i2c_address = 0x27;
MCP230XX_config_t mcp_config = def_mcp_config;
int ch;
while ((ch = cli.getOpt(argc, argv, "D:O:P:U:R:M:")) != EOF) {
switch (ch) {
case 'D':
mcp_config.direction = (int)strtol(cli.optArg(), nullptr, 0);
break;
case 'O':
mcp_config.state = (int)strtol(cli.optArg(), nullptr, 0);
break;
case 'P':
mcp_config.pullup = (int)strtol(cli.optArg(), nullptr, 0);
break;
case 'U':
mcp_config.interval = (uint16_t)atoi(cli.optArg());
break;
case 'M':
mcp_config.first_minor = (uint8_t)atoi(cli.optArg());
break;
}
}
const char *verb = cli.optArg();
if (!verb) {
MCP230XX::print_usage();
return -1;
}
cli.custom_data = &mcp_config;
BusInstanceIterator iterator("MCP23017", cli, mcp_config.device_type);
if (!strcmp(verb, "start")) {
return ThisDriver::module_start(cli, iterator);
}
if (!strcmp(verb, "stop")) {
return ThisDriver::module_stop(iterator);
}
if (!strcmp(verb, "status")) {
return ThisDriver::module_status(iterator);
}
MCP230XX::print_usage();
return -1;
}
+1
View File
@@ -55,4 +55,5 @@ px4_add_module(
module.yaml
DEPENDS
git_gps_devices
gnss
)
+74 -30
View File
@@ -50,6 +50,7 @@
#include <drivers/drv_sensor.h>
#include <lib/drivers/device/Device.hpp>
#include <lib/parameters/param.h>
#include <lib/perf/perf_counter.h>
#include <mathlib/mathlib.h>
#include <matrix/math.hpp>
#include <px4_platform_common/atomic.h>
@@ -67,6 +68,8 @@
#include <uORB/topics/sensor_gps.h>
#include <uORB/topics/sensor_gnss_relative.h>
#include <lib/gnss/rtcm.h>
#ifndef CONSTRAINED_FLASH
# include "devices/src/ashtech.h"
# include "devices/src/emlid_reach.h"
@@ -215,6 +218,11 @@ private:
gps_dump_s *_dump_from_device{nullptr};
gps_dump_comm_mode_t _dump_communication_mode{gps_dump_comm_mode_t::Disabled};
gnss::Rtcm3Parser _rtcm_parser{};
perf_counter_t _uart_tx_buffer_full_perf{perf_alloc(PC_COUNT, MODULE_NAME": tx buf full")};
perf_counter_t _rtcm_buffer_full_perf{perf_alloc(PC_COUNT, MODULE_NAME": rtcm buf full")};
static px4::atomic_bool _is_gps_main_advertised; ///< for the second gps we want to make sure that it gets instance 1
/// and thus we wait until the first one publishes at least one message.
@@ -264,7 +272,7 @@ private:
* @param data
* @param len
*/
inline bool injectData(uint8_t *data, size_t len);
inline bool injectData(const uint8_t *data, size_t len);
/**
* set the Baudrate
@@ -285,7 +293,7 @@ private:
* @param mode calling source
* @param msg_to_gps_device if true, this is a message sent to the gps device, otherwise it's from the device
*/
void dumpGpsData(uint8_t *data, size_t len, gps_dump_comm_mode_t mode, bool msg_to_gps_device);
void dumpGpsData(const uint8_t *data, size_t len, gps_dump_comm_mode_t mode, bool msg_to_gps_device);
void initializeCommunicationDump();
@@ -383,6 +391,9 @@ GPS::~GPS()
} while (_secondary_instance.load() && i < 100);
}
perf_free(_uart_tx_buffer_full_perf);
perf_free(_rtcm_buffer_full_perf);
delete _sat_info;
delete _dump_to_device;
delete _dump_from_device;
@@ -472,28 +483,16 @@ int GPS::pollOrRead(uint8_t *buf, size_t buf_length, int timeout)
const int max_timeout = 50;
int timeout_adjusted = math::min(max_timeout, timeout);
handleInjectDataTopic();
if (_interface == GPSHelper::Interface::UART) {
const ssize_t read_at_least = math::min(character_count, buf_length);
// handle injection data before read if caught up
if (_uart.bytesAvailable() < read_at_least) {
handleInjectDataTopic();
}
ret = _uart.readAtLeast(buf, buf_length, read_at_least, timeout_adjusted);
if (ret > 0) {
_num_bytes_read += ret;
}
ret = _uart.readAtLeast(buf, buf_length, math::min(character_count, buf_length), timeout_adjusted);
// SPI is only supported on LInux
#if defined(__PX4_LINUX)
} else if ((_interface == GPSHelper::Interface::SPI) && (_spi_fd >= 0)) {
handleInjectDataTopic();
//Poll only for the SPI data. In the same thread we also need to handle orb messages,
//so ideally we would poll on both, the SPI fd and orb subscription. Unfortunately the
//two pollings use different underlying mechanisms (at least under posix), which makes this
@@ -583,6 +582,7 @@ void GPS::handleInjectDataTopic()
// Looking at 8 packets thus guarantees, that at least a full injection
// data set is evaluated.
// Moving Base reuires a higher rate, so we allow up to 8 packets.
// Drain uORB messages into RTCM parser and inject full messages after draining the queue.
const size_t max_num_injections = gps_inject_data_s::ORB_QUEUE_LENGTH;
size_t num_injections = 0;
@@ -592,13 +592,13 @@ void GPS::handleInjectDataTopic()
// Prevent injection of data from self
if (msg.device_id != get_device_id()) {
/* Write the message to the gps device. Note that the message could be fragmented.
* But as we don't write anywhere else to the device during operation, we don't
* need to assemble the message first.
*/
injectData(msg.data, msg.len);
// Add data to the RTCM parser buffer for frame reassembly
size_t added = _rtcm_parser.addData(msg.data, msg.len);
if (added < msg.len) {
perf_count(_rtcm_buffer_full_perf);
}
++_rtcm_injection_rate_message_count;
_last_rtcm_injection_time = hrt_absolute_time();
}
}
@@ -616,9 +616,30 @@ void GPS::handleInjectDataTopic()
}
} while (updated && num_injections < max_num_injections);
// Now inject all complete RTCM frames from the parser buffer
size_t frame_len = {};
const uint8_t *frame_ptr = {};
while ((frame_ptr = _rtcm_parser.getNextMessage(&frame_len)) != nullptr) {
// Check TX buffer space before writing
if (_interface == GPSHelper::Interface::UART) {
ssize_t tx_available = _uart.txSpaceAvailable();
if ((ssize_t)frame_len > tx_available) {
// TX buffer full, stop and let it drain - frames stay in parser buffer
perf_count(_uart_tx_buffer_full_perf);
break;
}
}
injectData(frame_ptr, frame_len);
_rtcm_parser.consumeMessage(frame_len);
_rtcm_injection_rate_message_count++;
}
}
bool GPS::injectData(uint8_t *data, size_t len)
bool GPS::injectData(const uint8_t *data, size_t len)
{
dumpGpsData(data, len, gps_dump_comm_mode_t::Full, true);
@@ -687,7 +708,7 @@ void GPS::initializeCommunicationDump()
_dump_communication_mode = (gps_dump_comm_mode_t)param_dump_comm;
}
void GPS::dumpGpsData(uint8_t *data, size_t len, gps_dump_comm_mode_t mode, bool msg_to_gps_device)
void GPS::dumpGpsData(const uint8_t *data, size_t len, gps_dump_comm_mode_t mode, bool msg_to_gps_device)
{
gps_dump_s *dump_data = msg_to_gps_device ? _dump_to_device : _dump_from_device;
@@ -696,6 +717,7 @@ void GPS::dumpGpsData(uint8_t *data, size_t len, gps_dump_comm_mode_t mode, bool
}
dump_data->instance = (uint8_t)_instance;
dump_data->device_id = get_device_id();
while (len > 0) {
size_t write_len = len;
@@ -797,6 +819,13 @@ GPS::run()
param_get(handle, &f9p_uart2_baudrate);
}
handle = param_find("GPS_UBX_PPK");
int32_t ppk_output = 0;
if (handle != PARAM_INVALID) {
param_get(handle, &ppk_output);
}
int32_t gnssSystemsParam = static_cast<int32_t>(GPSHelper::GNSSSystemsMask::RECEIVER_DEFAULTS);
if (_instance == Instance::Main) {
@@ -878,11 +907,21 @@ GPS::run()
_mode = gps_driver_mode_t::UBX;
/* FALLTHROUGH */
case gps_driver_mode_t::UBX:
_helper = new GPSDriverUBX(_interface, &GPS::callback, this, &_sensor_gps, _p_report_sat_info,
gps_ubx_dynmodel, heading_offset, f9p_uart2_baudrate, ubx_mode);
set_device_type(DRV_GPS_DEVTYPE_UBX);
break;
case gps_driver_mode_t::UBX: {
GPSDriverUBX::Settings settings = {
.dynamic_model = (uint8_t)gps_ubx_dynmodel,
.heading_offset = heading_offset,
.uart2_baudrate = f9p_uart2_baudrate,
.ppk_output = ppk_output > 0,
.mode = ubx_mode,
};
_helper = new GPSDriverUBX(_interface, &GPS::callback, this, &_sensor_gps, _p_report_sat_info, settings);
set_device_type(DRV_GPS_DEVTYPE_UBX);
break;
}
#ifndef CONSTRAINED_FLASH
case gps_driver_mode_t::MTK:
@@ -1020,6 +1059,8 @@ GPS::run()
healthy_timeout += TIMEOUT_DUMP_ADD;
}
PX4_INFO("GPS device configured @ %u baud", _baudrate);
while ((helper_ret = _helper->receive(receive_timeout)) > 0 && !should_exit()) {
if (helper_ret & 1) {
@@ -1207,6 +1248,9 @@ GPS::print_status()
print_message(ORB_ID(sensor_gps), _sensor_gps);
}
perf_print_counter(_uart_tx_buffer_full_perf);
perf_print_counter(_rtcm_buffer_full_perf);
if (_instance == Instance::Main && _secondary_instance.load()) {
GPS *secondary_instance = _secondary_instance.load();
secondary_instance->print_status();
+9
View File
@@ -144,6 +144,15 @@ PARAM_DEFINE_INT32(GPS_UBX_BAUD2, 230400);
*/
PARAM_DEFINE_INT32(GPS_UBX_CFG_INTF, 0);
/**
* Enable MSM7 message output for PPK workflow.
*
* @boolean
* @reboot_required true
* @group GPS
*/
PARAM_DEFINE_INT32(GPS_UBX_PPK, 0);
/**
* Wipes the flash config of UBX modules.
*
@@ -94,9 +94,11 @@ int IST8310::probe()
while (hrt_elapsed_time(&start_time) < 50_ms) {
set_device_address(start_addr);
const int WAI = RegisterRead(Register::WAI);
if (WAI == Device_ID) {
const uint8_t wai = RegisterRead(Register::WAI);
if ((wai == IST8310_Device_ID) || (wai == IST8310J_Device_ID)) {
// Device has the right I2C address and register content
return PX4_OK;
}
@@ -128,30 +130,31 @@ void IST8310::RunImpl()
ScheduleDelayed(50_ms); // Power On Reset: max 50ms
break;
case STATE::WAIT_FOR_RESET:
case STATE::WAIT_FOR_RESET: {
// SRST: This bit is automatically reset to zero after POR routine
const uint8_t wai = RegisterRead(Register::WAI);
// SRST: This bit is automatically reset to zero after POR routine
if ((RegisterRead(Register::WAI) == Device_ID)
&& ((RegisterRead(Register::CNTL2) & CNTL2_BIT::SRST) == 0)) {
// if reset succeeded then configure
_state = STATE::CONFIGURE;
ScheduleDelayed(10_ms);
} else {
// RESET not complete
if (hrt_elapsed_time(&_reset_timestamp) > 1000_ms) {
PX4_DEBUG("Reset failed, retrying");
_state = STATE::RESET;
ScheduleDelayed(100_ms);
if (((wai == IST8310_Device_ID) || (wai == IST8310J_Device_ID))
&& ((RegisterRead(Register::CNTL2) & CNTL2_BIT::SRST) == 0)) {
// if reset succeeded then configure
_state = STATE::CONFIGURE;
ScheduleDelayed(10_ms);
} else {
PX4_DEBUG("Reset not complete, check again in 10 ms");
ScheduleDelayed(10_ms);
}
}
// RESET not complete
if (hrt_elapsed_time(&_reset_timestamp) > 1000_ms) {
PX4_DEBUG("Reset failed, retrying");
_state = STATE::RESET;
ScheduleDelayed(100_ms);
break;
} else {
PX4_DEBUG("Reset not complete, check again in 10 ms");
ScheduleDelayed(10_ms);
}
}
break;
}
case STATE::CONFIGURE:
if (Configure()) {
@@ -57,7 +57,8 @@ namespace iSentek_IST8310
static constexpr uint32_t I2C_SPEED = 400 * 1000; // 400 kHz I2C serial interface
static constexpr uint8_t I2C_ADDRESS_DEFAULT = 0x0E;
static constexpr uint8_t Device_ID = 0x10;
static constexpr uint8_t IST8310_Device_ID = 0x10;
static constexpr uint8_t IST8310J_Device_ID = 0xA3;
enum class Register : uint8_t {
WAI = 0x00, // Who Am I Register
+1
View File
@@ -158,6 +158,7 @@ px4_add_module(
# Main
uavcan_main.cpp
uavcan_servers.cpp
node_info.cpp
# Actuators
actuators/esc.cpp
+7
View File
@@ -128,6 +128,13 @@ UavcanEscController::esc_status_sub_cb(const uavcan::ReceivedDataStructure<uavca
_esc_status.timestamp = hrt_absolute_time();
_esc_status_pub.publish(_esc_status);
}
// Register device capability for each ESC channel
if (_node_info_publisher != nullptr) {
uint8_t node_id = msg.getSrcNodeID().get();
uint32_t device_id = msg.esc_index;
_node_info_publisher->registerDeviceCapability(node_id, device_id, NodeInfoPublisher::DeviceCapability::ESC);
}
}
uint8_t
+5 -1
View File
@@ -52,7 +52,7 @@
#include <uORB/topics/actuator_outputs.h>
#include <uORB/topics/esc_status.h>
#include <drivers/drv_hrt.h>
#include <lib/mixer_module/mixer_module.hpp>
#include <drivers/uavcan/node_info.hpp>
#include <parameters/param.h>
class UavcanEscController
@@ -78,6 +78,8 @@ public:
*/
void set_rotor_count(uint8_t count);
void set_node_info_publisher(NodeInfoPublisher *publisher) { _node_info_publisher = publisher; }
static int max_output_value() { return uavcan::equipment::esc::RawCommand::FieldTypes::cmd::RawValueType::max(); }
esc_status_s &esc_status() { return _esc_status; }
@@ -114,4 +116,6 @@ private:
uavcan::INode &_node;
uavcan::Publisher<uavcan::equipment::esc::RawCommand> _uavcan_pub_raw_cmd;
uavcan::Subscriber<uavcan::equipment::esc::Status, StatusCbBinder> _uavcan_sub_status;
NodeInfoPublisher *_node_info_publisher{nullptr};
};
+308
View File
@@ -0,0 +1,308 @@
/****************************************************************************
*
* 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.
*
****************************************************************************/
#include "node_info.hpp"
#include <px4_platform_common/log.h>
#include <px4_platform_common/px4_config.h>
#include <drivers/drv_hrt.h>
#include <cstring>
using namespace time_literals;
NodeInfoPublisher::NodeInfoPublisher(uavcan::INode &node, uavcan::NodeInfoRetriever &node_info_retriever)
: TimerBase(node), _node_info_retriever(node_info_retriever)
{
_node_info_retriever.addListener(this);
}
NodeInfoPublisher::~NodeInfoPublisher()
{
_node_info_retriever.removeListener(this);
delete[] _device_informations;
}
void NodeInfoPublisher::handleNodeInfoRetrieved(uavcan::NodeID node_id,
const uavcan::protocol::GetNodeInfo_::Response &node_info)
{
const NodeInfo info(node_id, node_info);
registerDevice(info.node_id.get(), &info, UINT32_MAX, DeviceCapability::NONE);
startTimerIfNotRunning();
}
void NodeInfoPublisher::handleNodeInfoUnavailable(uavcan::NodeID node_id)
{
}
void NodeInfoPublisher::handleTimerEvent(const uavcan::TimerEvent &event)
{
// Publish device information using round-robin approach
publishDeviceInformationPeriodic();
}
void NodeInfoPublisher::startTimerIfNotRunning()
{
if (!TimerBase::isRunning()) {
TimerBase::startPeriodic(uavcan::MonotonicDuration::fromMSec(DEVICE_INFO_PUBLISH_INTERVAL_MS));
}
}
void NodeInfoPublisher::registerDevice(uint8_t node_id, const NodeInfo *info, uint32_t device_id,
DeviceCapability capability)
{
const bool is_registering_info = (info != nullptr);
int multi_capability_index = -1;
for (size_t i = 0; i < _device_informations_size; ++i) {
if (is_registering_info) {
// Case 1: Check if this entry already has node info - skip this specific entry
if (_device_informations[i].node_id == node_id &&
_device_informations[i].has_node_info) {
continue; // Continue to check other entries with same node_id
}
// Case 2: Check if node_id already exists with capability but no info - update that entry
if (_device_informations[i].node_id == node_id &&
_device_informations[i].capability != DeviceCapability::NONE &&
!_device_informations[i].has_node_info) {
populateDeviceInfoFields(_device_informations[i], *info);
publishSingleDeviceInformation(_device_informations[i]);
continue;
}
} else { // registering capabilities
// Case 1: Check if this exact capability already exists - skip
if (_device_informations[i].node_id == node_id &&
_device_informations[i].device_id == device_id &&
_device_informations[i].capability == capability) {
return;
}
// Case 1b: if this node has multiple capabilities, continue
if (_device_informations[i].node_id == node_id &&
_device_informations[i].capability != DeviceCapability::NONE &&
_device_informations[i].capability != capability) {
multi_capability_index = i;
continue;
}
// Case 2: Check if node_id already exists with node info but no capability - update that entry
if (_device_informations[i].node_id == node_id &&
_device_informations[i].has_node_info &&
_device_informations[i].capability == DeviceCapability::NONE) {
_device_informations[i].device_id = device_id;
_device_informations[i].capability = capability;
publishSingleDeviceInformation(_device_informations[i]);
return;
}
}
}
// Case 3: extend array and add entry at the end
if (extendDeviceInformationsArray()) {
_device_informations[_device_informations_size - 1] = DeviceInformation();
_device_informations[_device_informations_size - 1].node_id = node_id;
if (multi_capability_index >= 0) {
_device_informations[_device_informations_size - 1] = _device_informations[multi_capability_index];
_device_informations[_device_informations_size - 1].node_id = node_id;
}
if (is_registering_info) {
populateDeviceInfoFields(_device_informations[_device_informations_size - 1], *info);
} else {
_device_informations[_device_informations_size - 1].device_id = device_id;
_device_informations[_device_informations_size - 1].capability = capability;
}
} else {
PX4_DEBUG("Failed to extend device informations array for %s",
is_registering_info ? "node info" : "capability");
}
}
void NodeInfoPublisher::registerDeviceCapability(uint8_t node_id, uint32_t device_id, DeviceCapability capability)
{
registerDevice(node_id, nullptr, device_id, capability);
}
void NodeInfoPublisher::publishDeviceInformationPeriodic()
{
// Using round-robin approach to publish one device info per timer event
if (_device_informations_size == 0) {
return;
}
size_t devices_checked = 0;
while (devices_checked < _device_informations_size) {
if (_next_device_to_publish >= _device_informations_size) {
_next_device_to_publish = 0;
}
const auto &device_info = _device_informations[_next_device_to_publish];
if (device_info.has_node_info && device_info.capability != DeviceCapability::NONE) {
publishSingleDeviceInformation(device_info);
_next_device_to_publish++;
return;
}
_next_device_to_publish++;
devices_checked++;
}
PX4_DEBUG("No devices ready for periodic publishing");
}
void NodeInfoPublisher::publishSingleDeviceInformation(const DeviceInformation &device_info)
{
const uint64_t now = hrt_absolute_time();
device_information_s msg{};
msg.timestamp = now;
msg.device_type = static_cast<uint8_t>(device_info.capability);
msg.device_id = device_info.device_id;
// Copy pre-populated fields directly from the struct
static_assert(sizeof(msg.model_name) == sizeof(device_info.model_name), "Array size mismatch");
static_assert(sizeof(msg.vendor_name) == sizeof(device_info.vendor_name), "Array size mismatch");
static_assert(sizeof(msg.firmware_version) == sizeof(device_info.firmware_version), "Array size mismatch");
static_assert(sizeof(msg.hardware_version) == sizeof(device_info.hardware_version), "Array size mismatch");
static_assert(sizeof(msg.serial_number) == sizeof(device_info.serial_number), "Array size mismatch");
memcpy(msg.model_name, device_info.model_name, sizeof(msg.model_name));
msg.model_name[sizeof(msg.model_name) - 1] = '\0';
memcpy(msg.vendor_name, device_info.vendor_name, sizeof(msg.vendor_name));
msg.vendor_name[sizeof(msg.vendor_name) - 1] = '\0';
memcpy(msg.firmware_version, device_info.firmware_version, sizeof(msg.firmware_version));
msg.firmware_version[sizeof(msg.firmware_version) - 1] = '\0';
memcpy(msg.hardware_version, device_info.hardware_version, sizeof(msg.hardware_version));
msg.hardware_version[sizeof(msg.hardware_version) - 1] = '\0';
memcpy(msg.serial_number, device_info.serial_number, sizeof(msg.serial_number));
msg.serial_number[sizeof(msg.serial_number) - 1] = '\0';
_device_info_pub.publish(msg);
PX4_DEBUG("Published device info for node %d, device_id %lu, type %d",
device_info.node_id, static_cast<unsigned long>(device_info.device_id),
static_cast<int>(device_info.capability));
}
void NodeInfoPublisher::populateDeviceInfoFields(DeviceInformation &device_info, const NodeInfo &info)
{
device_info.has_node_info = true;
// Parse the node name to extract vendor and model information
parseNodeName(info.name, device_info);
snprintf(device_info.firmware_version, sizeof(device_info.firmware_version),
"%d.%d.%lu", info.sw_major, info.sw_minor, static_cast<unsigned long>(info.vcs_commit));
snprintf(device_info.hardware_version, sizeof(device_info.hardware_version),
"%d.%d", info.hw_major, info.hw_minor);
snprintf(device_info.serial_number, sizeof(device_info.serial_number),
"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
info.unique_id[0], info.unique_id[1], info.unique_id[2], info.unique_id[3],
info.unique_id[4], info.unique_id[5], info.unique_id[6], info.unique_id[7],
info.unique_id[8], info.unique_id[9], info.unique_id[10], info.unique_id[11],
info.unique_id[12], info.unique_id[13], info.unique_id[14], info.unique_id[15]);
}
void NodeInfoPublisher::parseNodeName(const char *name, DeviceInformation &device_info)
{
if (!name || strlen(name) == 0) {
strlcpy(device_info.vendor_name, "", sizeof(device_info.vendor_name));
strlcpy(device_info.model_name, "", sizeof(device_info.model_name));
return;
}
// Find first dot and skip everything before it
const char *after_first_dot = strchr(name, '.');
if (after_first_dot == nullptr) {
// No dot - whole string is model, vendor is -1
strlcpy(device_info.vendor_name, "", sizeof(device_info.vendor_name));
strlcpy(device_info.model_name, name, sizeof(device_info.model_name));
return;
}
after_first_dot++;
// Find next dot in remaining string
const char *second_dot = strchr(after_first_dot, '.');
if (second_dot == nullptr) {
// Only one dot - everything after first dot is model, vendor is -1
strlcpy(device_info.vendor_name, "", sizeof(device_info.vendor_name));
strlcpy(device_info.model_name, after_first_dot, sizeof(device_info.model_name));
return;
}
// Copy vendor (between first and second dot)
size_t vendor_len = second_dot - after_first_dot;
size_t copy_len = (vendor_len < sizeof(device_info.vendor_name) - 1) ? vendor_len : sizeof(device_info.vendor_name) - 1;
strncpy(device_info.vendor_name, after_first_dot, copy_len);
device_info.vendor_name[copy_len] = '\0';
// Copy model (everything after second dot)
strlcpy(device_info.model_name, second_dot + 1, sizeof(device_info.model_name));
}
bool NodeInfoPublisher::extendDeviceInformationsArray()
{
const size_t new_size = _device_informations_size + 1;
DeviceInformation *new_array = new DeviceInformation[new_size];
if (!new_array) {
return false;
}
if (_device_informations_size > 0 && _device_informations != nullptr) {
memcpy(new_array, _device_informations, _device_informations_size * sizeof(DeviceInformation));
delete[] _device_informations;
}
_device_informations = new_array;
_device_informations_size = new_size;
return true;
}
+164
View File
@@ -0,0 +1,164 @@
/****************************************************************************
*
* 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.
*
****************************************************************************/
#pragma once
#include <uavcan/protocol/node_info_retriever.hpp>
#include <uORB/Publication.hpp>
#include <uORB/topics/device_information.h>
#include <stdint.h> // For UINT8_MAX, UINT32_MAX
#include <px4_platform_common/time.h>
using namespace time_literals;
constexpr int DEVICE_INFO_PUBLISH_INTERVAL_MS = 1000;
constexpr hrt_abstime DEVICE_INFO_PUBLISH_RATE_LIMIT_US = 100_ms;
class NodeInfoPublisher : private uavcan::INodeInfoListener, private uavcan::TimerBase
{
public:
enum class DeviceCapability : uint8_t {
NONE = UINT8_MAX, // Invalid/unset capability value (255)
GENERIC = device_information_s::DEVICE_TYPE_GENERIC,
AIRSPEED = device_information_s::DEVICE_TYPE_AIRSPEED,
ESC = device_information_s::DEVICE_TYPE_ESC,
SERVO = device_information_s::DEVICE_TYPE_SERVO,
GPS = device_information_s::DEVICE_TYPE_GPS,
MAGNETOMETER = device_information_s::DEVICE_TYPE_MAGNETOMETER,
PARACHUTE = device_information_s::DEVICE_TYPE_PARACHUTE,
RANGEFINDER = device_information_s::DEVICE_TYPE_RANGEFINDER,
WINCH = device_information_s::DEVICE_TYPE_WINCH,
BAROMETER = device_information_s::DEVICE_TYPE_BAROMETER,
OPTICAL_FLOW = device_information_s::DEVICE_TYPE_OPTICAL_FLOW,
ACCELEROMETER = device_information_s::DEVICE_TYPE_ACCELEROMETER,
GYROSCOPE = device_information_s::DEVICE_TYPE_GYROSCOPE,
DIFFERENTIAL_PRESSURE = device_information_s::DEVICE_TYPE_DIFFERENTIAL_PRESSURE,
BATTERY = device_information_s::DEVICE_TYPE_BATTERY,
HYGROMETER = device_information_s::DEVICE_TYPE_HYGROMETER,
};
NodeInfoPublisher(uavcan::INode &node, uavcan::NodeInfoRetriever &node_info_retriever);
~NodeInfoPublisher();
// Called by sensor bridges to register device capabilities
void registerDeviceCapability(uint8_t node_id, uint32_t device_id, DeviceCapability capability);
private:
struct NodeInfo {
NodeInfo(uavcan::NodeID id, const uavcan::protocol::GetNodeInfo_::Response &node_info)
: node_id(id), sw_major(node_info.software_version.major), sw_minor(node_info.software_version.minor),
vcs_commit(node_info.software_version.vcs_commit), hw_major(node_info.hardware_version.major),
hw_minor(node_info.hardware_version.minor)
{
memcpy(name, node_info.name.c_str(), node_info.name.capacity());
name[node_info.name.capacity() - 1] = '\0';
memcpy(unique_id, &node_info.hardware_version.unique_id.front(), node_info.hardware_version.unique_id.size());
}
NodeInfo() = default;
uavcan::NodeID node_id{};
char name[uavcan::protocol::GetNodeInfo_::Response::FieldTypes::name::MaxSize];
uint8_t unique_id[uavcan::protocol::GetNodeInfo_::Response::FieldTypes::hardware_version::FieldTypes::unique_id::MaxSize];
uint8_t sw_major;
uint8_t sw_minor;
uint32_t vcs_commit;
uint8_t hw_major;
uint8_t hw_minor;
};
struct DeviceInformation {
uint8_t node_id{UINT8_MAX};
uint32_t device_id{UINT32_MAX};
DeviceCapability capability{DeviceCapability::NONE};
bool has_node_info{false};
char vendor_name[32];
char model_name[32];
char firmware_version[24];
char hardware_version[24];
char serial_number[33];
DeviceInformation() : node_id(UINT8_MAX), device_id(UINT32_MAX), capability(DeviceCapability::NONE),
has_node_info(false)
{
// Initialize string fields
vendor_name[0] = '\0';
model_name[0] = '\0';
firmware_version[0] = '\0';
hardware_version[0] = '\0';
serial_number[0] = '\0';
}
DeviceInformation(uint8_t nid, uint32_t did, DeviceCapability cap)
: node_id(nid), device_id(did), capability(cap), has_node_info(false)
{
// Initialize string fields
vendor_name[0] = '\0';
model_name[0] = '\0';
firmware_version[0] = '\0';
hardware_version[0] = '\0';
serial_number[0] = '\0';
}
};
void handleNodeInfoRetrieved(uavcan::NodeID node_id,
const uavcan::protocol::GetNodeInfo_::Response &node_info) override;
void handleNodeInfoUnavailable(uavcan::NodeID node_id) override;
void handleTimerEvent(const uavcan::TimerEvent &event) override;
void startTimerIfNotRunning();
// Register device info or capability, set nodeinfo to nullptr if only registering capability
void registerDevice(uint8_t node_id, const NodeInfo *info, uint32_t device_id, DeviceCapability capability);
// Publishing methods
void publishDeviceInformationPeriodic();
void publishSingleDeviceInformation(const DeviceInformation &device_info);
// Helper functions
void populateDeviceInfoFields(DeviceInformation &device_info, const NodeInfo &info);
void parseNodeName(const char *name, DeviceInformation &device_info);
bool extendDeviceInformationsArray();
uavcan::NodeInfoRetriever &_node_info_retriever;
// Device capability tracking
DeviceInformation *_device_informations{nullptr};
size_t _device_informations_size{0};
uORB::Publication<device_information_s> _device_info_pub{ORB_ID(device_information)};
hrt_abstime _last_device_info_publish{0};
// Round-robin publishing
size_t _next_device_to_publish{0};
};
+8 -2
View File
@@ -40,8 +40,8 @@
const char *const UavcanAccelBridge::NAME = "accel";
UavcanAccelBridge::UavcanAccelBridge(uavcan::INode &node) :
UavcanSensorBridgeBase("uavcan_accel", ORB_ID(sensor_accel)),
UavcanAccelBridge::UavcanAccelBridge(uavcan::INode &node, NodeInfoPublisher *node_info_publisher) :
UavcanSensorBridgeBase("uavcan_accel", ORB_ID(sensor_accel), node_info_publisher),
_sub_imu_data(node)
{ }
@@ -77,6 +77,12 @@ void UavcanAccelBridge::imu_sub_cb(const uavcan::ReceivedDataStructure<uavcan::e
accel->set_error_count(0);
accel->update(timestamp_sample, msg.accelerometer_latest[0], msg.accelerometer_latest[1], msg.accelerometer_latest[2]);
// Register device capability if not already done
if (_node_info_publisher != nullptr) {
_node_info_publisher->registerDeviceCapability(msg.getSrcNodeID().get(), accel->get_device_id(),
NodeInfoPublisher::DeviceCapability::ACCELEROMETER);
}
}
int UavcanAccelBridge::init_driver(uavcan_bridge::Channel *channel)

Some files were not shown because too many files have changed in this diff Show More