mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-04-14 10:07:39 +08:00
drivers/actuators: new vertiq_io driver (#22892)
* brought in the Vertiq Cpp API as a submodule. updated the serial rx handling so that we can parse out IQUART data packets --------- Co-authored-by: Luca Scheuer <scheuer.luca@gmail.com>
This commit is contained in:
parent
5ffe9c6de4
commit
06dfc4b782
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -83,3 +83,7 @@
|
||||
[submodule "boards/modalai/voxl2/libfc-sensor-api"]
|
||||
path = boards/modalai/voxl2/libfc-sensor-api
|
||||
url = https://gitlab.com/voxl-public/voxl-sdk/core-libs/libfc-sensor-api.git
|
||||
[submodule "src/drivers/actuators/vertiq_io/iq-module-communication-cpp"]
|
||||
path = src/drivers/actuators/vertiq_io/iq-module-communication-cpp
|
||||
url = https://github.com/PX4/iq-module-communication-cpp.git
|
||||
branch = master
|
||||
|
||||
@ -5,6 +5,8 @@ CONFIG_BOARD_SERIAL_GPS2="/dev/ttyS6"
|
||||
CONFIG_BOARD_SERIAL_TEL1="/dev/ttyS5"
|
||||
CONFIG_BOARD_SERIAL_TEL2="/dev/ttyS3"
|
||||
CONFIG_BOARD_SERIAL_TEL3="/dev/ttyS1"
|
||||
CONFIG_DRIVERS_ACTUATORS_VERTIQ_IO=y
|
||||
CONFIG_USE_IFCI_CONFIGURATION=y
|
||||
CONFIG_DRIVERS_ADC_BOARD_ADC=y
|
||||
CONFIG_DRIVERS_BAROMETER_MS5611=y
|
||||
CONFIG_DRIVERS_BATT_SMBUS=y
|
||||
|
||||
80
src/drivers/actuators/vertiq_io/CMakeLists.txt
Normal file
80
src/drivers/actuators/vertiq_io/CMakeLists.txt
Normal file
@ -0,0 +1,80 @@
|
||||
############################################################################
|
||||
#
|
||||
# Copyright (c) 2024 PX4 Development Team. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# 3. Neither the name PX4 nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
#build our submodule as its own library so that we can avoid changing it at all
|
||||
include_directories(
|
||||
iq-module-communication-cpp/inc
|
||||
)
|
||||
|
||||
#add all of our submodule sources
|
||||
set(SOURCES
|
||||
iq-module-communication-cpp/src/generic_interface.cpp
|
||||
iq-module-communication-cpp/inc/client_communication.cpp
|
||||
iq-module-communication-cpp/src/packet_finder.c
|
||||
iq-module-communication-cpp/src/crc_helper.c
|
||||
iq-module-communication-cpp/src/byte_queue.c
|
||||
)
|
||||
|
||||
add_library(iq_communication ${SOURCES})
|
||||
add_dependencies(iq_communication prebuild_targets)
|
||||
|
||||
if("${PX4_PLATFORM}" MATCHES "nuttx")
|
||||
target_compile_definitions(iq_communication PUBLIC __NUTTX__)
|
||||
endif()
|
||||
|
||||
px4_add_git_submodule(TARGET git_iq-module-communication-cpp PATH "iq-module-communication-cpp")
|
||||
|
||||
px4_add_module(
|
||||
MODULE drivers__actuators__vertiq_io
|
||||
MAIN vertiq_io
|
||||
INCLUDES
|
||||
COMPILE_FLAGS
|
||||
SRCS
|
||||
vertiq_io.cpp
|
||||
vertiq_io.hpp
|
||||
vertiq_serial_interface.cpp
|
||||
vertiq_serial_interface.hpp
|
||||
vertiq_telemetry_manager.cpp
|
||||
vertiq_telemetry_manager.hpp
|
||||
vertiq_client_manager.cpp
|
||||
vertiq_client_manager.hpp
|
||||
vertiq_configuration_handler.cpp
|
||||
vertiq_configuration_handler.hpp
|
||||
entry_wrapper.hpp
|
||||
DEPENDS
|
||||
px4_work_queue
|
||||
mixer_module
|
||||
iq_communication
|
||||
MODULE_CONFIG
|
||||
module.yaml
|
||||
)
|
||||
17
src/drivers/actuators/vertiq_io/Kconfig
Normal file
17
src/drivers/actuators/vertiq_io/Kconfig
Normal file
@ -0,0 +1,17 @@
|
||||
menuconfig DRIVERS_ACTUATORS_VERTIQ_IO
|
||||
bool "vertiq_io"
|
||||
default n
|
||||
---help---
|
||||
Enable support for vertiq_io
|
||||
|
||||
if DRIVERS_ACTUATORS_VERTIQ_IO
|
||||
config USE_IFCI_CONFIGURATION
|
||||
bool "Include IFCI Configuration Parameters"
|
||||
default n
|
||||
|
||||
if USE_IFCI_CONFIGURATION
|
||||
config USE_PULSING_CONFIGURATION
|
||||
bool "Include Pulsing Module Configuration Parameters"
|
||||
default n
|
||||
endif
|
||||
endif
|
||||
181
src/drivers/actuators/vertiq_io/entry_wrapper.hpp
Normal file
181
src/drivers/actuators/vertiq_io/entry_wrapper.hpp
Normal file
@ -0,0 +1,181 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Copyright (c) 2024 PX4 Development Team. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name PX4 nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef ENTRY_WRAPPER_HPP
|
||||
#define ENTRY_WRAPPER_HPP
|
||||
|
||||
#include <parameters/param.h>
|
||||
|
||||
#include <px4_log.h>
|
||||
#include <px4_platform_common/module.h>
|
||||
|
||||
#include "vertiq_serial_interface.hpp"
|
||||
#include "iq-module-communication-cpp/inc/client_communication.hpp"
|
||||
|
||||
class AbstractEntryWrapper
|
||||
{
|
||||
public:
|
||||
AbstractEntryWrapper() {}
|
||||
virtual void ConfigureStruct(param_t parameter, ClientEntryAbstract *entry) = 0;
|
||||
virtual void SetNeedsInit() = 0;
|
||||
virtual ClientEntryAbstract *GetClientEntry() = 0;
|
||||
virtual void SendGet(VertiqSerialInterface *ser) = 0;
|
||||
virtual void Update(VertiqSerialInterface *serial_interface) = 0;
|
||||
};
|
||||
|
||||
template <typename module_data_type, typename px4_data_type>
|
||||
class EntryWrapper : public AbstractEntryWrapper
|
||||
{
|
||||
public:
|
||||
|
||||
param_t _param;
|
||||
ClientEntry<module_data_type> *_entry;
|
||||
bool _needs_init;
|
||||
|
||||
px4_data_type _px4_value;
|
||||
|
||||
EntryWrapper() {}
|
||||
|
||||
/**
|
||||
* @brief Initializes our EntryWrapper with a PX4 parameter and a Vetiq entry
|
||||
*
|
||||
* @param parameter A parameter stored in PX4. This can be found with the param_find function
|
||||
* @param entry A pointer to a Vertiq client entry
|
||||
*/
|
||||
void ConfigureStruct(param_t parameter, ClientEntryAbstract *entry)
|
||||
{
|
||||
_param = parameter;
|
||||
_entry = (ClientEntry<module_data_type> *)entry;
|
||||
_needs_init = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets our _needs_init flag high
|
||||
*/
|
||||
void SetNeedsInit()
|
||||
{
|
||||
_needs_init = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a pointer to our Vertiq entry
|
||||
*
|
||||
* @return _entry
|
||||
*/
|
||||
ClientEntryAbstract *GetClientEntry()
|
||||
{
|
||||
return _entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sends a get message to our Vertiq entry
|
||||
*
|
||||
* @param ser A pointer to our serial interface
|
||||
*/
|
||||
void SendGet(VertiqSerialInterface *ser)
|
||||
{
|
||||
_entry->get(*ser->GetIquartInterface());
|
||||
ser->ProcessSerialTx();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sends both a set and save message to our Vertiq entry with a new value
|
||||
*
|
||||
* @param ser A pointer to our serial interface
|
||||
* @param value The new value we are setting and saving on the connected motor
|
||||
*/
|
||||
void SendSetAndSave(VertiqSerialInterface *ser, module_data_type value)
|
||||
{
|
||||
_entry->set(*ser->GetIquartInterface(), value);
|
||||
_entry->save(*ser->GetIquartInterface());
|
||||
ser->ProcessSerialTx();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks to see if two values are the same. We will treat all parameters as floats regardless of their given type
|
||||
*
|
||||
* @param val1 The first value to compare
|
||||
* @param val2 The second value to compare
|
||||
* @param tolerance A tolerance to use in order to tell if the values are the same
|
||||
* @return true If the values are the same
|
||||
* @return false If the values are different
|
||||
*/
|
||||
bool ValuesAreTheSame(float val1, float val2, float tolerance = 0.001f)
|
||||
{
|
||||
float diff = val1 - val2;
|
||||
|
||||
if (diff < 0) {
|
||||
return -diff < tolerance;
|
||||
}
|
||||
|
||||
return diff < tolerance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates the relationship between the Vertiq and PX4 parameters. First, check to see if the connected motor sent data for us to read. Then, depending on the _needs_init
|
||||
* flag, we either set the gotten value into the PX4 parameter, or we set the new PX4 value to the motor.
|
||||
*
|
||||
* @param serial_interface A pointer to a serial interface used to send data
|
||||
*/
|
||||
void Update(VertiqSerialInterface *serial_interface)
|
||||
{
|
||||
//Make sure we have a way to store the data from PX4 and from the module
|
||||
module_data_type module_value = 0;
|
||||
px4_data_type px4_value = 0;
|
||||
|
||||
//If there's new data in the entry
|
||||
if (_entry->IsFresh()) {
|
||||
//Grab the data
|
||||
module_value = _entry->get_reply();
|
||||
|
||||
//If we need to set the PX4 data to the module's value, otherwise set the module value
|
||||
if (_needs_init) {
|
||||
px4_value = (px4_data_type)module_value;
|
||||
param_set(_param, &px4_value);
|
||||
_needs_init = false;
|
||||
|
||||
} else {
|
||||
//Grab the PX4 value
|
||||
param_get(_param, &px4_value);
|
||||
|
||||
//If the value here is different from the module's value, set and save it
|
||||
//Treat everything as a float to avoid type issues in comparison
|
||||
if (!ValuesAreTheSame((float)px4_value, (float)module_value)) {
|
||||
SendSetAndSave(serial_interface, px4_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif //ENTRY_WRAPPER_HPP
|
||||
@ -0,0 +1 @@
|
||||
Subproject commit a9b700d50bdd06a837c74750ac3c4760937333df
|
||||
289
src/drivers/actuators/vertiq_io/module.yaml
Normal file
289
src/drivers/actuators/vertiq_io/module.yaml
Normal file
@ -0,0 +1,289 @@
|
||||
module_name: Vertiq IO
|
||||
serial_config:
|
||||
- command: vertiq_io start -d ${SERIAL_DEV}
|
||||
port_config_param:
|
||||
name: VERTIQ_IO_CFG
|
||||
group: Vertiq IO
|
||||
|
||||
actuator_output:
|
||||
output_groups:
|
||||
- param_prefix: VTQ_IO
|
||||
group_label: 'CVIs'
|
||||
channel_label: 'CVI'
|
||||
instance_start: 0
|
||||
standard_params:
|
||||
disarmed: { min: 0, max: 65535, default: 0 }
|
||||
min: { min: 0, max: 65535, default: 0 }
|
||||
max: { min: 0, max: 65535, default: 65535 }
|
||||
failsafe: { min: 0, max: 500 }
|
||||
num_channels: 16
|
||||
|
||||
parameters:
|
||||
- group: Vertiq IO
|
||||
definitions:
|
||||
VTQ_BAUD:
|
||||
description:
|
||||
short: The IQUART driver's baud rate
|
||||
long: |
|
||||
The baud rate (in bits per second) used by the serial port connected with IQUART communication
|
||||
type: int32
|
||||
reboot_required: true
|
||||
default: 115200
|
||||
VTQ_NUM_CVS:
|
||||
description:
|
||||
short: The number of Vertiq IFCI parameters to use
|
||||
long: |
|
||||
The total number of IFCI control variables being used across all connected modules
|
||||
type: int32
|
||||
min: 0
|
||||
max: 16
|
||||
reboot_required: true
|
||||
default: 0
|
||||
VTQ_DISARM_TRIG:
|
||||
description:
|
||||
short: The triggered behavior sent to the motors on PX4 disarm
|
||||
long: |
|
||||
The behavior triggered when the flight controller disarms. You have the option to trigger your motors' disarm behaviors, set all motors to coast,
|
||||
or set a predefined throttle setpoint
|
||||
type: enum
|
||||
values:
|
||||
0: Send Explicit Disarm
|
||||
1: Coast Motors
|
||||
2: Set Predefined Velocity Setpoint
|
||||
default: 0
|
||||
VTQ_DISARM_VELO:
|
||||
description:
|
||||
short: Velocity sent when DISARM_TRIGGER is Set Predefined Velocity Setpoint
|
||||
long: This is the velocity that will be sent to all motors when PX4 is disarmed and DISARM_TRIGGER is Set Predefined Velocity Setpoint
|
||||
type: int32
|
||||
min: 0
|
||||
max: 100
|
||||
default: 0
|
||||
VTQ_ARM_BEHAVE:
|
||||
description:
|
||||
short: The triggered behavior on PX4 arm
|
||||
long: |
|
||||
The behavior triggered when the flight controller arms. You have the option to use your motors' arming behaviors, or to force all of your motors to arm
|
||||
type: enum
|
||||
values:
|
||||
0: Use Motor Arm Behavior
|
||||
1: Send Explicit Arm Command
|
||||
default: 0
|
||||
VTQ_TRGT_MOD_ID:
|
||||
description:
|
||||
short: The Module ID of the module you would like to communicate with
|
||||
long: |
|
||||
This is the value used as the target module ID of all configuration parameters (not operational parameters). The Vertiq module with the
|
||||
module ID matching this value will react to all get and set requests from PX4. Any Vertiq client made with dynamic object IDs should
|
||||
use this value to instantiate itself.
|
||||
type: int32
|
||||
default: 0
|
||||
VTQ_REDO_READ:
|
||||
description:
|
||||
short: Reinitialize the target module's values into the PX4 parameters
|
||||
long: |
|
||||
Setting this value to true will reinitialize PX4's IQUART connected parameters to the value stored on the currently targeted motor.
|
||||
This is especially useful if your flight controller powered on before your connected modules
|
||||
type: boolean
|
||||
default: 0
|
||||
VTQ_THROTTLE_CVI:
|
||||
description:
|
||||
short: Module Param - The module's Throttle Control Value Index
|
||||
long: |
|
||||
This represents the Control Value Index where the targeted module will look for throttle commands
|
||||
type: int32
|
||||
min: 0
|
||||
max: 255
|
||||
default: 0
|
||||
VTQ_CONTROL_MODE:
|
||||
description:
|
||||
short: Module Param - The module's control mechanism
|
||||
long: |
|
||||
PWM Mode: Commands a fraction of battery voltage. This changes as the battery voltage changes.
|
||||
This is the least safe mode because the upper throttle limit is determined by the battery voltage.
|
||||
Voltage Mode: Commands a voltage. The motor will behave the same way throughout the life of a battery, assuming the commanded voltage is less than the battery voltage.
|
||||
You must set the MAX_VOLTS parameter.
|
||||
Velocity Mode: Closed-loop, commands a velocity. The controller will adjust the applied voltage so that the motor spins at the commanded velocity.
|
||||
This mode has faster reaction times. Only use this if you know the properties of your propeller. You must set the MAX_VELOCITY parameter.
|
||||
type: enum
|
||||
values:
|
||||
0: PWM
|
||||
1: Voltage
|
||||
2: Velocity
|
||||
default: 0
|
||||
VTQ_MAX_VELOCITY:
|
||||
description:
|
||||
short: Module Param - Maximum velocity when CONTROL_MODE is set to Velocity
|
||||
long: |
|
||||
Only relevant in Velocity Mode. This is the velocity the controller will command at full throttle.
|
||||
type: float
|
||||
default: 0
|
||||
VTQ_MAX_VOLTS:
|
||||
description:
|
||||
short: Module Param - Maximum voltage when CONTROL_MODE is set to Voltage
|
||||
long: |
|
||||
Only relevant in Voltage Mode. This is the voltage the controller will command at full throttle.
|
||||
type: float
|
||||
default: 0
|
||||
VTQ_MOTOR_DIR:
|
||||
description:
|
||||
short: Module Param - The direction that the module should spin
|
||||
long: |
|
||||
Set the targeted motor's spinning direction (clockwise vs. counter clockwise) and flight mode (2D non-reversible vs. 3D reversible)
|
||||
type: enum
|
||||
values:
|
||||
0: Unconfigured
|
||||
1: 3D Counter Clockwise
|
||||
2: 3D Clockwise
|
||||
3: 2D Counter Clockwise
|
||||
4: 2D Clockwise
|
||||
default: 0
|
||||
VTQ_FC_DIR:
|
||||
description:
|
||||
short: Module Param - If the flight controller uses 2D or 3D communication
|
||||
long: |
|
||||
The FC and the ESC must agree upon the meaning of the signal coming out of the ESC. When FCs are in 3D mode
|
||||
they re-map negative signals. This parameter keeps the FC and ESC in agreement.
|
||||
type: enum
|
||||
values:
|
||||
0: 2D
|
||||
1: 3D
|
||||
default: 0
|
||||
VTQ_ZERO_ANGLE:
|
||||
description:
|
||||
short: Module Param - The encoder angle at which theta is zero
|
||||
long: |
|
||||
The encoder angle at which theta is zero. Adjust this number to change the location of 0 phase when pulsing.
|
||||
type: float
|
||||
default: 0
|
||||
VTQ_VELO_CUTOFF:
|
||||
description:
|
||||
short: Module Param - The minimum velocity required to allow pulsing
|
||||
long: |
|
||||
This is the velocity at which pulsing is allowed. Any velocity between VELOCITY_CUTOFF and -VELOCITY_CUTOFF will not pulse.
|
||||
type: float
|
||||
default: 0
|
||||
VTQ_TQUE_OFF_ANG:
|
||||
description:
|
||||
short: Module Param - Offsets pulse angle to allow for mechanical properties
|
||||
long: |
|
||||
This offsets where the pulse starts around the motor to allow for propeller mechanical properties.
|
||||
type: float
|
||||
default: 0
|
||||
VTQ_PULSE_V_MODE:
|
||||
description:
|
||||
short: Module Param - 0 = Supply Voltage Mode, 1 = Voltage Limit Mode
|
||||
long: |
|
||||
Supply Voltage Mode means that the maximum voltage applied to pulsing is the supplied voltage. Voltage Limit Mode
|
||||
indicates that PULSE_VOLT_LIM is the maximum allowed voltage to apply towards pulsing.
|
||||
type: enum
|
||||
values:
|
||||
0: Supply Voltage Mode
|
||||
1: Voltage Limit Mode
|
||||
default: 0
|
||||
VTQ_PULSE_V_LIM:
|
||||
description:
|
||||
short: Module Param - Max pulsing voltage limit when in Voltage Limit Mode
|
||||
long: |
|
||||
This sets the max pulsing voltage limit when in Voltage Limit Mode.
|
||||
type: float
|
||||
default: 0
|
||||
VTQ_X_CVI:
|
||||
description:
|
||||
short: Module Param - CVI for the X rectangular coordinate
|
||||
long: |
|
||||
This represents the Control Value Index where the targeted module will look for the X rectangular coordinate.
|
||||
type: int32
|
||||
min: 0
|
||||
max: 255
|
||||
default: 0
|
||||
VTQ_Y_CVI:
|
||||
description:
|
||||
short: Module Param - CVI for the Y rectangular coordinate
|
||||
long: |
|
||||
This represents the Control Value Index where the targeted module will look for the Y rectangular coordinate.
|
||||
type: int32
|
||||
min: 0
|
||||
max: 255
|
||||
default: 0
|
||||
VTQ_TELEM_IDS_1:
|
||||
description:
|
||||
short: Module IDs [0, 31] that you would like to request telemetry from
|
||||
long: |
|
||||
The module IDs [0, 31] that should be asked for telemetry. The data received from these IDs will be published via the esc_status topic.
|
||||
type: bitmask
|
||||
bit:
|
||||
0: Module ID 0
|
||||
1: Module ID 1
|
||||
2: Module ID 2
|
||||
3: Module ID 3
|
||||
4: Module ID 4
|
||||
5: Module ID 5
|
||||
6: Module ID 6
|
||||
7: Module ID 7
|
||||
8: Module ID 8
|
||||
9: Module ID 9
|
||||
10: Module ID 10
|
||||
11: Module ID 11
|
||||
12: Module ID 12
|
||||
13: Module ID 13
|
||||
14: Module ID 14
|
||||
15: Module ID 15
|
||||
16: Module ID 16
|
||||
17: Module ID 17
|
||||
18: Module ID 18
|
||||
19: Module ID 19
|
||||
20: Module ID 20
|
||||
21: Module ID 21
|
||||
22: Module ID 22
|
||||
23: Module ID 23
|
||||
24: Module ID 24
|
||||
25: Module ID 25
|
||||
26: Module ID 26
|
||||
27: Module ID 27
|
||||
28: Module ID 28
|
||||
29: Module ID 29
|
||||
30: Module ID 30
|
||||
31: Module ID 31
|
||||
reboot_required: true
|
||||
default: 0
|
||||
VTQ_TELEM_IDS_2:
|
||||
description:
|
||||
short: Module IDs [32, 62] that you would like to request telemetry from
|
||||
long: |
|
||||
The module IDs [32, 62] that should be asked for telemetry. The data received from these IDs will be published via the esc_status topic.
|
||||
type: bitmask
|
||||
bit:
|
||||
32: Module ID 32
|
||||
33: Module ID 33
|
||||
34: Module ID 34
|
||||
35: Module ID 35
|
||||
36: Module ID 36
|
||||
37: Module ID 37
|
||||
38: Module ID 38
|
||||
39: Module ID 39
|
||||
40: Module ID 40
|
||||
41: Module ID 41
|
||||
42: Module ID 42
|
||||
43: Module ID 43
|
||||
44: Module ID 44
|
||||
45: Module ID 45
|
||||
46: Module ID 46
|
||||
47: Module ID 47
|
||||
48: Module ID 48
|
||||
49: Module ID 49
|
||||
50: Module ID 50
|
||||
51: Module ID 51
|
||||
52: Module ID 52
|
||||
53: Module ID 53
|
||||
54: Module ID 54
|
||||
55: Module ID 55
|
||||
56: Module ID 56
|
||||
57: Module ID 57
|
||||
58: Module ID 58
|
||||
59: Module ID 59
|
||||
60: Module ID 60
|
||||
61: Module ID 61
|
||||
62: Module ID 62
|
||||
reboot_required: true
|
||||
default: 0
|
||||
65
src/drivers/actuators/vertiq_io/vertiq_client_manager.cpp
Normal file
65
src/drivers/actuators/vertiq_io/vertiq_client_manager.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Copyright (c) 2024 PX4 Development Team. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name PX4 nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
#include "vertiq_client_manager.hpp"
|
||||
|
||||
VertiqClientManager::VertiqClientManager(VertiqSerialInterface *serial_interface) :
|
||||
_serial_interface(serial_interface)
|
||||
{
|
||||
}
|
||||
|
||||
void VertiqClientManager::HandleClientCommunication()
|
||||
{
|
||||
//Called periodically in the main loop to handle all communications not handled directly by
|
||||
//parameter setting
|
||||
//Update our serial tx before we take in the rx
|
||||
_serial_interface->ProcessSerialTx();
|
||||
|
||||
//Update our serial rx for all clients
|
||||
_serial_interface->ProcessSerialRx(_client_array, _clients_in_use);
|
||||
}
|
||||
|
||||
void VertiqClientManager::AddNewClient(ClientAbstract *client)
|
||||
{
|
||||
if (_clients_in_use < MAXIMUM_NUMBER_OF_CLIENTS) {
|
||||
_client_array[_clients_in_use] = client;
|
||||
_clients_in_use++;
|
||||
|
||||
} else {
|
||||
PX4_INFO("Could not add this client. Maximum number exceeded");
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t VertiqClientManager::GetNumberOfClients()
|
||||
{
|
||||
return _clients_in_use;
|
||||
}
|
||||
89
src/drivers/actuators/vertiq_io/vertiq_client_manager.hpp
Normal file
89
src/drivers/actuators/vertiq_io/vertiq_client_manager.hpp
Normal file
@ -0,0 +1,89 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Copyright (c) 2024 PX4 Development Team. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name PX4 nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
#ifndef VERTIQ_CLLIENT_MANAGER_HPP
|
||||
#define VERTIQ_CLLIENT_MANAGER_HPP
|
||||
|
||||
#include <drivers/device/device.h>
|
||||
#include <lib/led/led.h>
|
||||
#include <lib/mixer_module/mixer_module.hpp>
|
||||
#include <lib/perf/perf_counter.h>
|
||||
|
||||
#include <parameters/param.h>
|
||||
|
||||
#include <px4_log.h>
|
||||
#include <px4_platform_common/module.h>
|
||||
|
||||
#include "vertiq_serial_interface.hpp"
|
||||
|
||||
static const uint8_t _kBroadcastID = 63;
|
||||
|
||||
class VertiqClientManager
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new VertiqClientManager object. It is responsible for accepting new Vertiq client objects and handling
|
||||
* all of their communication processing
|
||||
*
|
||||
* @param serial_interface A pointer to a VertiqSerialInterface object
|
||||
*/
|
||||
VertiqClientManager(VertiqSerialInterface *serial_interface);
|
||||
|
||||
/**
|
||||
* @brief Handle the IQUART interface. Make sure that we update TX and RX buffers
|
||||
*/
|
||||
void HandleClientCommunication();
|
||||
|
||||
/**
|
||||
* @brief Adds a new client to our array of Clients
|
||||
*/
|
||||
void AddNewClient(ClientAbstract *client);
|
||||
|
||||
/**
|
||||
* @brief Returns the number of clients added to our Clients array
|
||||
*
|
||||
* @return The value _clients_in_use
|
||||
*/
|
||||
uint8_t GetNumberOfClients();
|
||||
|
||||
private:
|
||||
//We need a serial handler in order to talk over the serial port
|
||||
VertiqSerialInterface *_serial_interface;
|
||||
|
||||
//Some constants to help us out
|
||||
static const uint8_t MAXIMUM_NUMBER_OF_CLIENTS =
|
||||
35; //These are clients that are used for module control/telemetry. They have a static Module ID
|
||||
ClientAbstract *_client_array[MAXIMUM_NUMBER_OF_CLIENTS];
|
||||
uint8_t _clients_in_use = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
147
src/drivers/actuators/vertiq_io/vertiq_configuration_handler.cpp
Normal file
147
src/drivers/actuators/vertiq_io/vertiq_configuration_handler.cpp
Normal file
@ -0,0 +1,147 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Copyright (c) 2024 PX4 Development Team. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name PX4 nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
#include "vertiq_configuration_handler.hpp"
|
||||
|
||||
VertiqConfigurationHandler::VertiqConfigurationHandler(VertiqSerialInterface *ser,
|
||||
VertiqClientManager *client_manager) :
|
||||
_serial_interface(ser),
|
||||
_client_manager(client_manager)
|
||||
{
|
||||
}
|
||||
|
||||
void VertiqConfigurationHandler::InitConfigurationClients(uint8_t object_id)
|
||||
{
|
||||
_object_id_now = object_id; //Make sure we store the initial object ID
|
||||
|
||||
_prop_input_parser_client = new EscPropellerInputParserClient(object_id);
|
||||
_client_manager->AddNewClient(_prop_input_parser_client);
|
||||
|
||||
#ifdef CONFIG_USE_IFCI_CONFIGURATION
|
||||
_ifci_client = new IQUartFlightControllerInterfaceClient(object_id);
|
||||
_client_manager->AddNewClient(_ifci_client);
|
||||
#endif //CONFIG_USE_IFCI_CONFIGURATION
|
||||
|
||||
#ifdef CONFIG_USE_PULSING_CONFIGURATION
|
||||
_voltage_superposition_client = new VoltageSuperPositionClient(object_id);
|
||||
_client_manager->AddNewClient(_voltage_superposition_client);
|
||||
|
||||
_pulsing_rectangular_input_parser_client = new PulsingRectangularInputParserClient(object_id);
|
||||
_client_manager->AddNewClient(_pulsing_rectangular_input_parser_client);
|
||||
#endif //CONFIG_USE_PULSING_CONFIGURATION
|
||||
}
|
||||
|
||||
void VertiqConfigurationHandler::InitClientEntryWrappers()
|
||||
{
|
||||
AddNewClientEntry<float, float>(param_find("VTQ_MAX_VELOCITY"), &(_prop_input_parser_client->velocity_max_));
|
||||
AddNewClientEntry<float, float>(param_find("VTQ_MAX_VOLTS"), &(_prop_input_parser_client->volts_max_));
|
||||
AddNewClientEntry<uint8_t, int32_t>(param_find("VTQ_CONTROL_MODE"), &(_prop_input_parser_client->mode_));
|
||||
AddNewClientEntry<uint8_t, int32_t>(param_find("VTQ_MOTOR_DIR"), &(_prop_input_parser_client->sign_));
|
||||
AddNewClientEntry<uint8_t, int32_t>(param_find("VTQ_FC_DIR"), &(_prop_input_parser_client->flip_negative_));
|
||||
|
||||
#ifdef CONFIG_USE_IFCI_CONFIGURATION
|
||||
AddNewClientEntry<uint8_t, int32_t>(param_find("VTQ_THROTTLE_CVI"), &(_ifci_client->throttle_cvi_));
|
||||
#endif //CONFIG_USE_IFCI_CONFIGURATION
|
||||
|
||||
#ifdef CONFIG_USE_PULSING_CONFIGURATION
|
||||
AddNewClientEntry<uint8_t, int32_t> (param_find("VTQ_PULSE_V_MODE"),
|
||||
&(_pulsing_rectangular_input_parser_client->pulsing_voltage_mode_));
|
||||
AddNewClientEntry<uint8_t, int32_t>(param_find("VTQ_X_CVI"), &(_ifci_client->x_cvi_));
|
||||
AddNewClientEntry<uint8_t, int32_t>(param_find("VTQ_Y_CVI"), &(_ifci_client->y_cvi_));
|
||||
AddNewClientEntry<float, float>(param_find("VTQ_ZERO_ANGLE"), &(_voltage_superposition_client->zero_angle_));
|
||||
AddNewClientEntry<float, float>(param_find("VTQ_VELO_CUTOFF"),
|
||||
&(_voltage_superposition_client->velocity_cutoff_));
|
||||
AddNewClientEntry<float, float>(param_find("VTQ_TQUE_OFF_ANG"),
|
||||
&(_voltage_superposition_client->propeller_torque_offset_angle_));
|
||||
AddNewClientEntry<float, float>(param_find("VTQ_PULSE_V_LIM"),
|
||||
&(_pulsing_rectangular_input_parser_client->pulsing_voltage_limit_));
|
||||
#endif //CONFIG_USE_PULSING_CONFIGURATION
|
||||
}
|
||||
|
||||
void VertiqConfigurationHandler::UpdateClientsToNewObjId(uint8_t new_object_id)
|
||||
{
|
||||
_object_id_now = new_object_id;
|
||||
|
||||
DestroyAndRecreateClient<EscPropellerInputParserClient>(_prop_input_parser_client, new_object_id);
|
||||
|
||||
#ifdef CONFIG_USE_IFCI_CONFIGURATION
|
||||
DestroyAndRecreateClient<IQUartFlightControllerInterfaceClient>(_ifci_client, new_object_id);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USE_PULSING_CONFIGURATION
|
||||
DestroyAndRecreateClient<VoltageSuperPositionClient>(_voltage_superposition_client, new_object_id);
|
||||
DestroyAndRecreateClient<PulsingRectangularInputParserClient>(_pulsing_rectangular_input_parser_client, new_object_id);
|
||||
#endif
|
||||
}
|
||||
|
||||
void VertiqConfigurationHandler::MarkConfigurationEntriesForRefresh()
|
||||
{
|
||||
for (uint8_t i = 0; i < _added_configuration_entry_wrappers; i++) {
|
||||
_configuration_entry_wrappers[i]->SetNeedsInit();
|
||||
}
|
||||
}
|
||||
|
||||
void VertiqConfigurationHandler::UpdateIquartConfigParams()
|
||||
{
|
||||
for (uint8_t i = 0; i < _added_configuration_entry_wrappers; i++) {
|
||||
_configuration_entry_wrappers[i]->SendGet(_serial_interface);
|
||||
//Ensure that these get messages get out
|
||||
_client_manager->HandleClientCommunication();
|
||||
}
|
||||
|
||||
//Now go ahead and grab responses, and update everyone to be on the same page, but do it quickly.
|
||||
CoordinateIquartWithPx4Params();
|
||||
}
|
||||
|
||||
void VertiqConfigurationHandler::CoordinateIquartWithPx4Params(hrt_abstime timeout)
|
||||
{
|
||||
//Get the start time and the end time
|
||||
hrt_abstime time_now = hrt_absolute_time();
|
||||
hrt_abstime end_time = time_now + timeout;
|
||||
|
||||
while (time_now < end_time) {
|
||||
for (uint8_t i = 0; i < _added_configuration_entry_wrappers; i++) {
|
||||
_configuration_entry_wrappers[i]->Update(_serial_interface);
|
||||
}
|
||||
|
||||
//Update the time
|
||||
time_now = hrt_absolute_time();
|
||||
|
||||
//Update our serial rx
|
||||
_client_manager->HandleClientCommunication();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t VertiqConfigurationHandler::GetObjectIdNow()
|
||||
{
|
||||
return _object_id_now;
|
||||
}
|
||||
182
src/drivers/actuators/vertiq_io/vertiq_configuration_handler.hpp
Normal file
182
src/drivers/actuators/vertiq_io/vertiq_configuration_handler.hpp
Normal file
@ -0,0 +1,182 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Copyright (c) 2024 PX4 Development Team. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name PX4 nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
#ifndef VERTIQ_CONFIGURATION_CLIENT_HANDLER_HPP
|
||||
#define VERTIQ_CONFIGURATION_CLIENT_HANDLER_HPP
|
||||
|
||||
/**
|
||||
Vertiq modules communicate through a system of clients and entries. Clients contain entries, and entries
|
||||
specifiy some sort of Vertiq module parameter or control. Entries can be gotten (its value returned to the requester), set (the
|
||||
module's internal value set to the value requested by the user), and saved (writes the currently stored module value to its persistent memory).
|
||||
|
||||
This class exists to coordinate PX4 parameters with Vertiq entries
|
||||
*/
|
||||
|
||||
#include <lib/mixer_module/mixer_module.hpp>
|
||||
|
||||
#include "entry_wrapper.hpp"
|
||||
#include "vertiq_client_manager.hpp"
|
||||
#include "iq-module-communication-cpp/inc/esc_propeller_input_parser_client.hpp"
|
||||
|
||||
#include "iq-module-communication-cpp/inc/arming_handler_client.hpp"
|
||||
#include "iq-module-communication-cpp/inc/iquart_flight_controller_interface_client.hpp"
|
||||
|
||||
#ifdef CONFIG_USE_PULSING_CONFIGURATION
|
||||
#include "iq-module-communication-cpp/inc/voltage_superposition_client.hpp"
|
||||
#include "iq-module-communication-cpp/inc/pulsing_rectangular_input_parser_client.hpp"
|
||||
#endif //CONFIG_USE_PULSING_CONFIGURATION
|
||||
|
||||
class VertiqConfigurationHandler
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a new Vertiq Configuration Handler object
|
||||
*
|
||||
* @param ser A pointer to our serial interface
|
||||
* @param client_manager A pointer to our client manager
|
||||
*/
|
||||
VertiqConfigurationHandler(VertiqSerialInterface *ser, VertiqClientManager *client_manager);
|
||||
|
||||
/**
|
||||
* @brief Initialize all of our client entry wrappers
|
||||
*
|
||||
*/
|
||||
void InitClientEntryWrappers();
|
||||
|
||||
/**
|
||||
* @brief Set all of the IQUART configuration init flags to true
|
||||
*/
|
||||
void MarkConfigurationEntriesForRefresh();
|
||||
|
||||
/**
|
||||
* @brief Send a Get command to all of the parameters involved in IQUART configuration, and make sure the PX4 parameters and module values agree
|
||||
*/
|
||||
void UpdateIquartConfigParams();
|
||||
|
||||
/**
|
||||
* @brief Until the timeout is reached, keep trying to update the PX4 parameters to match, as appropraite, the value on the module. This can
|
||||
* mean either setting the PX4 parameter to match the motor or vice versa
|
||||
*
|
||||
* @param timeout The maximum amount of time we can keep trying to get responses
|
||||
*/
|
||||
void CoordinateIquartWithPx4Params(hrt_abstime timeout = 100_ms);
|
||||
|
||||
/**
|
||||
* @brief Gives access to the object ID currently being used
|
||||
*
|
||||
* @return The value stored in _object_id_now
|
||||
*/
|
||||
uint8_t GetObjectIdNow();
|
||||
|
||||
/**
|
||||
* @brief When the target module ID parameter changes, we need to delete and remake all of our configuration clients in order to make sure that they're talking to the
|
||||
* correct motor.
|
||||
*
|
||||
* @param new_object_id The new target module ID that we should use to instantiate our new clients
|
||||
*/
|
||||
void UpdateClientsToNewObjId(uint8_t new_object_id);
|
||||
|
||||
/**
|
||||
* @brief Initialize all of the Vertiq Clients that we want to use
|
||||
*
|
||||
* @param object_id The object ID with which to initialize our clients
|
||||
*/
|
||||
void InitConfigurationClients(uint8_t object_id);
|
||||
|
||||
/**
|
||||
* @brief Delete and recreate a client with a new object ID
|
||||
*
|
||||
* @tparam client_type The actual type of Client you want to delete and remake
|
||||
* @param client A pointer ref to the client being deleted
|
||||
* @param object_id The object id we're making the new client with
|
||||
*/
|
||||
template <typename client_type>
|
||||
void DestroyAndRecreateClient(client_type *&client, uint8_t object_id)
|
||||
{
|
||||
if (client) {
|
||||
delete client;
|
||||
client = new client_type(object_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates and adds a new entry wrapper object to our array of entry wrappers
|
||||
*
|
||||
* @param px4_param A parameter stored in PX4. This can be found with the param_find function
|
||||
* @param entry A pointer to a Vertiq client entry
|
||||
*/
|
||||
template <typename iquart_data_type, typename px4_data_type>
|
||||
void AddNewClientEntry(param_t px4_param, ClientEntryAbstract *entry)
|
||||
{
|
||||
if (_added_configuration_entry_wrappers < MAX_CLIENT_ENTRIES) {
|
||||
_configuration_entry_wrappers[_added_configuration_entry_wrappers] = new EntryWrapper<iquart_data_type, px4_data_type>;
|
||||
_configuration_entry_wrappers[_added_configuration_entry_wrappers]->ConfigureStruct(px4_param, entry);
|
||||
_added_configuration_entry_wrappers++;
|
||||
|
||||
} else {
|
||||
PX4_INFO("Could not add this entry. Maximum number exceeded");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
uint8_t _object_id_now; //The object ID we're targeting right now
|
||||
VertiqSerialInterface *_serial_interface; //We need a serial handler in order to talk over the serial port
|
||||
VertiqClientManager *_client_manager;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//Vertiq Client information
|
||||
//Known Configuration Clients can be created as pointers to certain types of clients
|
||||
EscPropellerInputParserClient *_prop_input_parser_client;
|
||||
|
||||
#ifdef CONFIG_USE_IFCI_CONFIGURATION
|
||||
//Make all of the clients that we need to talk to the IFCI config params
|
||||
IQUartFlightControllerInterfaceClient *_ifci_client;
|
||||
#endif //CONFIG_USE_IFCI_CONFIGURATION
|
||||
|
||||
#ifdef CONFIG_USE_PULSING_CONFIGURATION
|
||||
VoltageSuperPositionClient *_voltage_superposition_client;
|
||||
PulsingRectangularInputParserClient *_pulsing_rectangular_input_parser_client;
|
||||
#endif //CONFIG_USE_PULSING_CONFIGURATION
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//Vertiq Client Entry information
|
||||
static const uint8_t MAX_CLIENT_ENTRIES = 40;
|
||||
AbstractEntryWrapper *_configuration_entry_wrappers[MAX_CLIENT_ENTRIES];
|
||||
uint8_t _added_configuration_entry_wrappers = 0;
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
};
|
||||
|
||||
#endif //VERTIQ_CONFIGURATION_CLIENT_HANDLER_HPP
|
||||
391
src/drivers/actuators/vertiq_io/vertiq_io.cpp
Normal file
391
src/drivers/actuators/vertiq_io/vertiq_io.cpp
Normal file
@ -0,0 +1,391 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Copyright (c) 2024 PX4 Development Team. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name PX4 nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
#include "vertiq_io.hpp"
|
||||
|
||||
#include <px4_platform_common/log.h>
|
||||
|
||||
px4::atomic_bool VertiqIo::_request_telemetry_init{false};
|
||||
|
||||
VertiqIo::VertiqIo(const char *port) :
|
||||
OutputModuleInterface(MODULE_NAME, px4::wq_configurations::hp_default),
|
||||
_serial_interface(),
|
||||
_client_manager(&_serial_interface),
|
||||
_telem_manager(&_client_manager),
|
||||
_configuration_handler(&_serial_interface, &_client_manager),
|
||||
_broadcast_prop_motor_control(_kBroadcastID),
|
||||
_broadcast_arming_handler(_kBroadcastID),
|
||||
_operational_ifci(_kBroadcastID)
|
||||
{
|
||||
// store port name
|
||||
strncpy(_port, port, sizeof(_port) - 1);
|
||||
|
||||
// enforce null termination
|
||||
_port[sizeof(_port) - 1] = '\0';
|
||||
|
||||
//Make sure we get the correct initial values for our parameters
|
||||
updateParams();
|
||||
|
||||
_configuration_handler.InitConfigurationClients((uint8_t)_param_vertiq_target_module_id.get());
|
||||
_configuration_handler.InitClientEntryWrappers();
|
||||
|
||||
_client_manager.AddNewClient(&_operational_ifci);
|
||||
_client_manager.AddNewClient(&_broadcast_arming_handler);
|
||||
_client_manager.AddNewClient(&_broadcast_prop_motor_control);
|
||||
}
|
||||
|
||||
VertiqIo::~VertiqIo()
|
||||
{
|
||||
//Be inactive
|
||||
stop();
|
||||
|
||||
//Free our counters/timers
|
||||
perf_free(_loop_perf);
|
||||
perf_free(_loop_interval_perf);
|
||||
}
|
||||
|
||||
//called by our task_spawn function
|
||||
bool VertiqIo::init()
|
||||
{
|
||||
_serial_interface.InitSerial(_port, _param_vertiq_baud.get());
|
||||
|
||||
#ifdef CONFIG_USE_IFCI_CONFIGURATION
|
||||
//Grab the number of IFCI control values the user wants to use
|
||||
_cvs_in_use = (uint8_t)_param_vertiq_number_of_cvs.get();
|
||||
|
||||
//Grab the bitmask that we're going to use to decide who we get telemetry from
|
||||
_telemetry_ids_1 = (uint32_t)_param_vertiq_telem_ids_1.get();
|
||||
_telemetry_ids_2 = (uint32_t)_param_vertiq_telem_ids_2.get();
|
||||
_telem_bitmask = ((uint64_t)(_telemetry_ids_2) << 32) | _telemetry_ids_1;
|
||||
|
||||
_transmission_message.num_cvs = _cvs_in_use;
|
||||
#endif
|
||||
|
||||
//Initialize our telemetry handler
|
||||
_telem_manager.Init(_telem_bitmask, (uint8_t)_param_vertiq_target_module_id.get());
|
||||
_telem_manager.StartPublishing(&_esc_status_pub);
|
||||
|
||||
//Make sure we get our thread into execution
|
||||
ScheduleNow();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VertiqIo::start()
|
||||
{
|
||||
ScheduleNow();
|
||||
}
|
||||
|
||||
void VertiqIo::stop()
|
||||
{
|
||||
ScheduleClear();
|
||||
}
|
||||
|
||||
|
||||
//This is the same as a while(1) loop
|
||||
void VertiqIo::Run()
|
||||
{
|
||||
//Start the loop timer
|
||||
//Increment our loop counter
|
||||
perf_begin(_loop_perf);
|
||||
perf_count(_loop_interval_perf);
|
||||
|
||||
//Handle IQUART reception and transmission
|
||||
_client_manager.HandleClientCommunication();
|
||||
|
||||
// If we're supposed to ask for telemetry from someone
|
||||
if (_telem_bitmask) {
|
||||
//Grab the next ID, and if it's real get it ready to send out
|
||||
uint16_t next_telem = _telem_manager.UpdateTelemetry();
|
||||
|
||||
if (next_telem != _impossible_module_id) {
|
||||
_transmission_message.telem_byte = next_telem;
|
||||
}
|
||||
}
|
||||
|
||||
//Get the most up to date version of our parameters/check if they've changed
|
||||
parameters_update();
|
||||
|
||||
//Make sure we also update the mixing output to get the most up to date configuration
|
||||
_mixing_output.update();
|
||||
_mixing_output.updateSubscriptions(true);
|
||||
|
||||
//Go ahead and check to see if our actuator test has gotten anything new
|
||||
if (_actuator_test_sub.updated()) {
|
||||
_actuator_test_sub.copy(&_actuator_test);
|
||||
|
||||
//Our test is active if anyone is giving us commands through the actuator test
|
||||
_actuator_test_active = _actuator_test.action == actuator_test_s::ACTION_DO_CONTROL;
|
||||
}
|
||||
|
||||
//stop our timer
|
||||
perf_end(_loop_perf);
|
||||
}
|
||||
|
||||
void VertiqIo::parameters_update()
|
||||
{
|
||||
//If someone has changed any parameter in our module. Checked at 1Hz
|
||||
if (_parameter_update_sub.updated()) {
|
||||
//Grab the changed parameter with copy (which lowers the "changed" flag)
|
||||
parameter_update_s param_update;
|
||||
_parameter_update_sub.copy(¶m_update);
|
||||
|
||||
// If any parameter updated, call updateParams() to check if
|
||||
// this class attributes need updating (and do so).
|
||||
updateParams();
|
||||
|
||||
//If the target module ID changed, we need to update all of our parameters
|
||||
if (_param_vertiq_target_module_id.get() != _configuration_handler.GetObjectIdNow()) {
|
||||
_configuration_handler.UpdateClientsToNewObjId(_param_vertiq_target_module_id.get());
|
||||
|
||||
//Make sure we start a new read!
|
||||
_param_vertiq_trigger_read.set(true);
|
||||
_param_vertiq_trigger_read.commit_no_notification();
|
||||
}
|
||||
|
||||
//If you're set to re-read from the motor, mark all of the IQUART parameters for reinitialization, reset the trigger, and then update the IFCI params
|
||||
if (_param_vertiq_trigger_read.get()) {
|
||||
_configuration_handler.MarkConfigurationEntriesForRefresh();
|
||||
_param_vertiq_trigger_read.set(false);
|
||||
_param_vertiq_trigger_read.commit_no_notification();
|
||||
}
|
||||
|
||||
_configuration_handler.UpdateIquartConfigParams();
|
||||
}
|
||||
}
|
||||
|
||||
void VertiqIo::OutputControls(uint16_t outputs[MAX_ACTUATORS])
|
||||
{
|
||||
//Put the mixer outputs into the output message
|
||||
for (uint8_t i = 0; i < _transmission_message.num_cvs; i++) {
|
||||
_transmission_message.commands[i] = outputs[i];
|
||||
}
|
||||
|
||||
_operational_ifci.PackageIfciCommandsForTransmission(&_transmission_message, _output_message, &_output_len);
|
||||
_operational_ifci.packed_command_.set(*_serial_interface.GetIquartInterface(), _output_message, _output_len);
|
||||
_serial_interface.ProcessSerialTx();
|
||||
}
|
||||
|
||||
bool VertiqIo::updateOutputs(bool stop_motors, uint16_t outputs[MAX_ACTUATORS], unsigned num_outputs,
|
||||
unsigned num_control_groups_updated)
|
||||
{
|
||||
#ifdef CONFIG_USE_IFCI_CONFIGURATION
|
||||
|
||||
if (_mixing_output.armed().armed) {
|
||||
|
||||
if (_param_vertiq_arm_behavior.get() == FORCE_ARMING && _send_forced_arm) {
|
||||
_broadcast_arming_handler.motor_armed_.set(*_serial_interface.GetIquartInterface(), 1);
|
||||
_send_forced_arm = false;
|
||||
}
|
||||
|
||||
OutputControls(outputs);
|
||||
|
||||
//We want to make sure that we send a valid telem request only once to ensure that we're not getting extraneous responses.
|
||||
//So, here we'll set the telem request ID to something that no one will respond to. Another function will take charge of setting it to a
|
||||
//proper value when necessary
|
||||
_transmission_message.telem_byte = _impossible_module_id;
|
||||
|
||||
} else if (_actuator_test_active) {
|
||||
OutputControls(outputs);
|
||||
|
||||
} else {
|
||||
//Put the modules into coast
|
||||
switch (_param_vertiq_disarm_behavior.get()) {
|
||||
case TRIGGER_MOTOR_DISARM:
|
||||
_broadcast_arming_handler.motor_armed_.set(*_serial_interface.GetIquartInterface(), 0);
|
||||
break;
|
||||
|
||||
case COAST_MOTOR:
|
||||
_broadcast_prop_motor_control.ctrl_coast_.set(*_serial_interface.GetIquartInterface());
|
||||
break;
|
||||
|
||||
case SEND_PREDEFINED_VELOCITY:
|
||||
_broadcast_prop_motor_control.ctrl_velocity_.set(*_serial_interface.GetIquartInterface(),
|
||||
_param_vertiq_disarm_velocity.get());
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!_send_forced_arm) {
|
||||
_send_forced_arm = true;
|
||||
}
|
||||
|
||||
_actuator_test_active = false;
|
||||
}
|
||||
|
||||
//Publish our esc status to uORB
|
||||
_esc_status_pub.publish(_telem_manager.GetEscStatus());
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void VertiqIo::print_info()
|
||||
{
|
||||
perf_print_counter(_loop_perf);
|
||||
perf_print_counter(_loop_interval_perf);
|
||||
|
||||
_mixing_output.printStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Local functions in support of the shell command.
|
||||
*/
|
||||
namespace vertiq_namespace
|
||||
{
|
||||
|
||||
VertiqIo *g_dev{nullptr};
|
||||
|
||||
int start(const char *port);
|
||||
int status();
|
||||
int stop();
|
||||
int usage();
|
||||
|
||||
int
|
||||
start(const char *port)
|
||||
{
|
||||
if (g_dev != nullptr) {
|
||||
PX4_ERR("already started");
|
||||
return PX4_OK;
|
||||
}
|
||||
|
||||
// Instantiate the driver.
|
||||
g_dev = new VertiqIo(port);
|
||||
|
||||
if (g_dev == nullptr) {
|
||||
PX4_ERR("driver start failed");
|
||||
return PX4_ERROR;
|
||||
}
|
||||
|
||||
if (!g_dev->init()) {
|
||||
PX4_ERR("driver start failed");
|
||||
delete g_dev;
|
||||
g_dev = nullptr;
|
||||
return PX4_ERROR;
|
||||
}
|
||||
|
||||
return PX4_OK;
|
||||
}
|
||||
|
||||
int
|
||||
status()
|
||||
{
|
||||
if (g_dev == nullptr) {
|
||||
PX4_ERR("driver not running");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("state @ %p\n", g_dev);
|
||||
g_dev->print_info();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int stop()
|
||||
{
|
||||
if (g_dev != nullptr) {
|
||||
PX4_INFO("stopping driver");
|
||||
delete g_dev;
|
||||
g_dev = nullptr;
|
||||
PX4_INFO("driver stopped");
|
||||
|
||||
} else {
|
||||
PX4_ERR("driver not running");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return PX4_OK;
|
||||
}
|
||||
|
||||
int
|
||||
usage()
|
||||
{
|
||||
PRINT_MODULE_USAGE_NAME("vertiq_io", "driver");
|
||||
PRINT_MODULE_USAGE_COMMAND("start");
|
||||
PRINT_MODULE_USAGE_ARG("<device>", "UART device", false);
|
||||
PRINT_MODULE_USAGE_DEFAULT_COMMANDS();
|
||||
|
||||
return 0;
|
||||
return PX4_OK;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" __EXPORT int vertiq_io_main(int argc, char *argv[])
|
||||
{
|
||||
int ch = 0;
|
||||
const char *device_path = nullptr;
|
||||
int myoptind = 1;
|
||||
const char *myoptarg = nullptr;
|
||||
|
||||
while ((ch = px4_getopt(argc, argv, "d:", &myoptind, &myoptarg)) != EOF) {
|
||||
switch (ch) {
|
||||
|
||||
case 'd':
|
||||
device_path = myoptarg;
|
||||
break;
|
||||
|
||||
default:
|
||||
PX4_WARN("Unknown option!");
|
||||
return PX4_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (myoptind >= argc) {
|
||||
PX4_ERR("unrecognized command");
|
||||
return vertiq_namespace::usage();
|
||||
}
|
||||
|
||||
if (!device_path) {
|
||||
PX4_ERR("Missing device");
|
||||
return PX4_ERROR;
|
||||
}
|
||||
|
||||
if (!strcmp(argv[myoptind], "start")) {
|
||||
if (strcmp(device_path, "") != 0) {
|
||||
return vertiq_namespace::start(device_path);
|
||||
|
||||
} else {
|
||||
PX4_WARN("Please specify device path!");
|
||||
return vertiq_namespace::usage();
|
||||
}
|
||||
|
||||
} else if (!strcmp(argv[myoptind], "stop")) {
|
||||
return vertiq_namespace::stop();
|
||||
|
||||
} else if (!strcmp(argv[myoptind], "status")) {
|
||||
return vertiq_namespace::status();
|
||||
}
|
||||
|
||||
return vertiq_namespace::usage();
|
||||
}
|
||||
207
src/drivers/actuators/vertiq_io/vertiq_io.hpp
Normal file
207
src/drivers/actuators/vertiq_io/vertiq_io.hpp
Normal file
@ -0,0 +1,207 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Copyright (c) 2024 PX4 Development Team. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name PX4 nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <drivers/device/device.h>
|
||||
#include <lib/led/led.h>
|
||||
#include <lib/mixer_module/mixer_module.hpp>
|
||||
#include <lib/perf/perf_counter.h>
|
||||
|
||||
#include <px4_log.h>
|
||||
#include <px4_platform_common/module.h>
|
||||
#include <px4_platform_common/getopt.h>
|
||||
|
||||
#include <uORB/topics/vehicle_control_mode.h>
|
||||
#include <uORB/topics/manual_control_setpoint.h>
|
||||
#include <uORB/topics/actuator_outputs.h>
|
||||
#include <uORB/topics/led_control.h>
|
||||
#include <uORB/topics/esc_status.h>
|
||||
#include <uORB/topics/actuator_test.h>
|
||||
|
||||
#include "vertiq_telemetry_manager.hpp"
|
||||
#include "vertiq_client_manager.hpp"
|
||||
#include "vertiq_serial_interface.hpp"
|
||||
#include "vertiq_configuration_handler.hpp"
|
||||
|
||||
#include "iq-module-communication-cpp/inc/propeller_motor_control_client.hpp"
|
||||
#include "iq-module-communication-cpp/inc/brushless_drive_client.hpp"
|
||||
#include "iq-module-communication-cpp/inc/arming_handler_client.hpp"
|
||||
|
||||
#include "iq-module-communication-cpp/inc/esc_propeller_input_parser_client.hpp"
|
||||
#include "iq-module-communication-cpp/inc/iquart_flight_controller_interface_client.hpp"
|
||||
|
||||
#ifdef CONFIG_USE_PULSING_CONFIGURATION
|
||||
#include "iq-module-communication-cpp/inc/voltage_superposition_client.hpp"
|
||||
#include "iq-module-communication-cpp/inc/pulsing_rectangular_input_parser_client.hpp"
|
||||
#endif //CONFIG_USE_PULSING_CONFIGURATION
|
||||
|
||||
enum DISARM_BEHAVIORS {TRIGGER_MOTOR_DISARM, COAST_MOTOR, SEND_PREDEFINED_VELOCITY};
|
||||
enum ARM_BEHAVIORS {USE_MOTOR_ARMING, FORCE_ARMING};
|
||||
|
||||
class VertiqIo : public OutputModuleInterface
|
||||
{
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Create a new VertiqIo object
|
||||
*/
|
||||
VertiqIo(const char *port);
|
||||
|
||||
/**
|
||||
* @brief destruct a VertiqIo object
|
||||
*/
|
||||
~VertiqIo();
|
||||
|
||||
/**
|
||||
* @brief initialize the VertiqIo object. This will be called by the task_spawn function. Makes sure that the thread gets scheduled.
|
||||
*/
|
||||
bool init();
|
||||
|
||||
/**
|
||||
* @brief Print information about how to use our module
|
||||
*
|
||||
*/
|
||||
void print_info();
|
||||
|
||||
/** @see OutputModuleInterface */
|
||||
bool updateOutputs(bool stop_motors, uint16_t outputs[MAX_ACTUATORS],
|
||||
unsigned num_outputs, unsigned num_control_groups_updated) override;
|
||||
|
||||
/**
|
||||
* @brief Used to package and transmit controls via IQUART
|
||||
* @param outputs The output throttles calculated by the mixer
|
||||
*/
|
||||
void OutputControls(uint16_t outputs[MAX_ACTUATORS]);
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Check for parameter changes and update them if needed.
|
||||
*/
|
||||
void parameters_update();
|
||||
void Run() override;
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
char _port[20] {};
|
||||
|
||||
perf_counter_t _loop_perf{perf_alloc(PC_ELAPSED, MODULE_NAME": cycle")};
|
||||
perf_counter_t _loop_interval_perf{perf_alloc(PC_INTERVAL, MODULE_NAME": output update interval")};
|
||||
|
||||
static const uint8_t MAX_SUPPORTABLE_IFCI_CVS = 16;
|
||||
|
||||
static px4::atomic_bool
|
||||
_request_telemetry_init; //Determines whether or not we should initialize or re-initialize the serial connection
|
||||
|
||||
MixingOutput _mixing_output{"VTQ_IO", MAX_SUPPORTABLE_IFCI_CVS, *this, MixingOutput::SchedulingPolicy::Auto, false, false, 0};
|
||||
|
||||
static char _telemetry_device[20]; //The name of the device we're connecting to. this will be something like /dev/ttyS3
|
||||
|
||||
VertiqSerialInterface _serial_interface; //We need a serial handler in order to talk over the serial port
|
||||
VertiqClientManager _client_manager; //We need someone who can manage our clients
|
||||
VertiqTelemetryManager _telem_manager; //We need a telemetry handler
|
||||
VertiqConfigurationHandler _configuration_handler;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//Vertiq client information
|
||||
|
||||
/**
|
||||
Vertiq clients are used to communicate with various parameters available on the module. Our C++ library provides
|
||||
simple objects used to interact with these clients.
|
||||
|
||||
An example of a Vertiq client is documented here https://iqmotion.readthedocs.io/en/latest/modules/vertiq_2306_2200.html#propeller-motor-control. In this case,
|
||||
Propeller Motor Control is the client, and its entries are specified in the message table (https://iqmotion.readthedocs.io/en/latest/modules/vertiq_2306_2200.html#id6).
|
||||
You can find the C++ representation in ./src/drivers/actuators/vertiq_io/iq-module-communication-cpp/inc/propeller_motor_control_client.hpp
|
||||
*/
|
||||
|
||||
//Known Clients can be created as concrete objects
|
||||
PropellerMotorControlClient _broadcast_prop_motor_control;
|
||||
ArmingHandlerClient _broadcast_arming_handler;
|
||||
IQUartFlightControllerInterfaceClient _operational_ifci;
|
||||
IFCIPackedMessage _transmission_message;
|
||||
static const uint16_t MAX_IFCI_MESSAGE = 40; //Up to 16 2 byte commands, one telemetry byte, plus 7 IQUART added bytes
|
||||
uint8_t _output_message[MAX_IFCI_MESSAGE];
|
||||
uint8_t _output_len;
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
uint8_t _cvs_in_use = 0; //Store the number of control variables that we're using
|
||||
|
||||
//Store the telemetry bitmask for who we want to get telemetry from
|
||||
uint64_t _telem_bitmask = 0;
|
||||
uint32_t _telemetry_ids_1 = 0;
|
||||
uint32_t _telemetry_ids_2 = 0;
|
||||
|
||||
bool _send_forced_arm = true;
|
||||
bool _actuator_test_active = false;
|
||||
|
||||
uORB::Publication<esc_status_s> _esc_status_pub{ORB_ID(esc_status)}; //We want to publish our ESC Status to anyone who will listen
|
||||
|
||||
// Subscriptions
|
||||
uORB::SubscriptionInterval _parameter_update_sub{ORB_ID(parameter_update), 1_s};
|
||||
|
||||
//We need to know what's going on with the actuator test to make sure we handle it properly
|
||||
uORB::Subscription _actuator_test_sub{ORB_ID(actuator_test)};
|
||||
actuator_test_s _actuator_test;
|
||||
|
||||
//We need to bring in the parameters that we define in module.yaml in order to view them in the
|
||||
//control station, as well as to use them in the firmware
|
||||
DEFINE_PARAMETERS(
|
||||
(ParamInt<px4::params::VTQ_BAUD>) _param_vertiq_baud,
|
||||
(ParamInt<px4::params::VTQ_TRGT_MOD_ID>) _param_vertiq_target_module_id,
|
||||
(ParamBool<px4::params::VTQ_REDO_READ>) _param_vertiq_trigger_read,
|
||||
(ParamInt<px4::params::VTQ_CONTROL_MODE>) _param_vertiq_control_mode,
|
||||
(ParamFloat<px4::params::VTQ_MAX_VELOCITY>) _param_vertiq_max_velo,
|
||||
(ParamFloat<px4::params::VTQ_MAX_VOLTS>) _param_vertiq_max_volts,
|
||||
(ParamInt<px4::params::VTQ_MOTOR_DIR>) _param_vertiq_motor_direction,
|
||||
(ParamInt<px4::params::VTQ_FC_DIR>) _param_vertiq_fc_direction
|
||||
#ifdef CONFIG_USE_IFCI_CONFIGURATION
|
||||
, (ParamInt<px4::params::VTQ_NUM_CVS>) _param_vertiq_number_of_cvs
|
||||
, (ParamInt<px4::params::VTQ_DISARM_VELO>) _param_vertiq_disarm_velocity
|
||||
, (ParamInt<px4::params::VTQ_DISARM_TRIG>) _param_vertiq_disarm_behavior
|
||||
, (ParamInt<px4::params::VTQ_ARM_BEHAVE>) _param_vertiq_arm_behavior
|
||||
, (ParamInt<px4::params::VTQ_THROTTLE_CVI>) _param_vertiq_throttle_cvi
|
||||
, (ParamInt<px4::params::VTQ_TELEM_IDS_1>) _param_vertiq_telem_ids_1
|
||||
, (ParamInt<px4::params::VTQ_TELEM_IDS_2>) _param_vertiq_telem_ids_2
|
||||
#endif //CONFIG_USE_IFCI_CONFIGURATION
|
||||
|
||||
#ifdef CONFIG_USE_PULSING_CONFIGURATION
|
||||
, (ParamInt<px4::params::VTQ_PULSE_V_MODE>) _param_vertiq_pulse_volt_mode
|
||||
, (ParamInt<px4::params::VTQ_X_CVI>) _param_vertiq_pulse_x_cvi
|
||||
, (ParamInt<px4::params::VTQ_Y_CVI>) _param_vertiq_pulse_y_cvi
|
||||
, (ParamFloat<px4::params::VTQ_ZERO_ANGLE>) _param_vertiq_pulse_zero_angle
|
||||
, (ParamFloat<px4::params::VTQ_VELO_CUTOFF>) _param_vertiq_pulse_velo_cutoff
|
||||
, (ParamFloat<px4::params::VTQ_TQUE_OFF_ANG>) _param_vertiq_pulse_torque_offset_angle
|
||||
, (ParamFloat<px4::params::VTQ_PULSE_V_LIM>) _param_vertiq_pulse_voltage_limit
|
||||
#endif //CONFIG_USE_PULSING_CONFIGURATION
|
||||
)
|
||||
};
|
||||
245
src/drivers/actuators/vertiq_io/vertiq_serial_interface.cpp
Normal file
245
src/drivers/actuators/vertiq_io/vertiq_serial_interface.cpp
Normal file
@ -0,0 +1,245 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Copyright (c) 2024 PX4 Development Team. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name PX4 nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
#include "vertiq_serial_interface.hpp"
|
||||
|
||||
VertiqSerialInterface::VertiqSerialInterface()
|
||||
{}
|
||||
|
||||
void VertiqSerialInterface::DeinitSerial()
|
||||
{
|
||||
if (_uart_fd >= 0) {
|
||||
close(_uart_fd);
|
||||
_uart_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int VertiqSerialInterface::InitSerial(const char *uart_device, unsigned baud)
|
||||
{
|
||||
//Make sure we're starting clean
|
||||
DeinitSerial();
|
||||
|
||||
//Populate our version of the uart device name
|
||||
strncpy(_port_in_use, uart_device, sizeof(_port_in_use));
|
||||
|
||||
//Open up the port with read/write permissions and O_NOCTTY which is, "/* Required by POSIX */"
|
||||
_uart_fd = ::open(_port_in_use, O_RDWR | O_NOCTTY);
|
||||
|
||||
if (_uart_fd < 0) {
|
||||
PX4_ERR("failed to open serial port: %s err: %d", uart_device, errno);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
PX4_INFO("Opened serial port successfully");
|
||||
|
||||
//Now that we've opened the port, fully configure it for baud/bit num
|
||||
return ConfigureSerialPeripheral(baud);
|
||||
}
|
||||
|
||||
int VertiqSerialInterface::ConfigureSerialPeripheral(unsigned baud)
|
||||
{
|
||||
int speed;
|
||||
|
||||
switch (baud) {
|
||||
case 9600:
|
||||
speed = B9600;
|
||||
break;
|
||||
|
||||
case 19200:
|
||||
speed = B19200;
|
||||
break;
|
||||
|
||||
case 38400:
|
||||
speed = B38400;
|
||||
break;
|
||||
|
||||
case 57600:
|
||||
speed = B57600;
|
||||
break;
|
||||
|
||||
case 115200:
|
||||
speed = B115200;
|
||||
break;
|
||||
|
||||
case 230400:
|
||||
speed = B230400;
|
||||
break;
|
||||
|
||||
case 460800:
|
||||
speed = B460800;
|
||||
break;
|
||||
|
||||
case 500000:
|
||||
speed = B500000;
|
||||
break;
|
||||
|
||||
case 921600:
|
||||
speed = B921600;
|
||||
break;
|
||||
|
||||
case 1000000:
|
||||
speed = B1000000;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct termios uart_config;
|
||||
|
||||
int termios_state;
|
||||
|
||||
/* fill the struct for the new configuration */
|
||||
tcgetattr(_uart_fd, &uart_config);
|
||||
|
||||
//
|
||||
// Input flags - Turn off input processing
|
||||
//
|
||||
// convert break to null byte, no CR to NL translation,
|
||||
// no NL to CR translation, don't mark parity errors or breaks
|
||||
// no input parity check, don't strip high bit off,
|
||||
// no XON/XOFF software flow control
|
||||
//
|
||||
uart_config.c_iflag &= ~(IGNBRK | BRKINT | ICRNL |
|
||||
INLCR | PARMRK | INPCK | ISTRIP | IXON);
|
||||
//
|
||||
// Output flags - Turn off output processing
|
||||
//
|
||||
// no CR to NL translation, no NL to CR-NL translation,
|
||||
// no NL to CR translation, no column 0 CR suppression,
|
||||
// no Ctrl-D suppression, no fill characters, no case mapping,
|
||||
// no local output processing
|
||||
//
|
||||
// config.c_oflag &= ~(OCRNL | ONLCR | ONLRET |
|
||||
// ONOCR | ONOEOT| OFILL | OLCUC | OPOST);
|
||||
uart_config.c_oflag = 0;
|
||||
|
||||
//
|
||||
// No line processing
|
||||
//
|
||||
// echo off, echo newline off, canonical mode off,
|
||||
// extended input processing off, signal chars off
|
||||
//
|
||||
uart_config.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
|
||||
|
||||
/* no parity, one stop bit, disable flow control */
|
||||
uart_config.c_cflag &= ~(CSTOPB | PARENB | CRTSCTS);
|
||||
|
||||
/* set baud rate */
|
||||
if ((termios_state = cfsetispeed(&uart_config, speed)) < 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if ((termios_state = cfsetospeed(&uart_config, speed)) < 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if ((termios_state = tcsetattr(_uart_fd, TCSANOW, &uart_config)) < 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
close(_uart_fd);
|
||||
_uart_fd = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool VertiqSerialInterface::CheckForRx()
|
||||
{
|
||||
// read from the uart. This must be non-blocking, so check first if there is data available
|
||||
_bytes_available = 0;
|
||||
int ret = ioctl(_uart_fd, FIONREAD, (unsigned long)&_bytes_available);
|
||||
|
||||
if (ret != 0) {
|
||||
PX4_ERR("Reading error");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return _bytes_available > 0;
|
||||
}
|
||||
|
||||
uint8_t *VertiqSerialInterface::ReadAndSetRxBytes()
|
||||
{
|
||||
//Read the bytes available for us
|
||||
read(_uart_fd, _rx_buf, _bytes_available);
|
||||
|
||||
//Put the data into our IQUART handler
|
||||
_iquart_interface.SetRxBytes(_rx_buf, _bytes_available);
|
||||
return _rx_buf;
|
||||
}
|
||||
|
||||
void VertiqSerialInterface::ProcessSerialRx(ClientAbstract **client_array, uint8_t number_of_clients)
|
||||
{
|
||||
//Make sure we can actually talk
|
||||
ReOpenSerial();
|
||||
|
||||
//We have bytes
|
||||
if (CheckForRx()) {
|
||||
uint8_t *data_ptr = ReadAndSetRxBytes();
|
||||
|
||||
//While we've got packets to look at, give the packet to each of the clients so that each
|
||||
//can decide what to do with it
|
||||
while (_iquart_interface.PeekPacket(&data_ptr, &_bytes_available) == 1) {
|
||||
|
||||
for (uint8_t i = 0; i < number_of_clients; i++) {
|
||||
client_array[i]->ReadMsg(data_ptr, _bytes_available);
|
||||
}
|
||||
|
||||
_iquart_interface.DropPacket();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void VertiqSerialInterface::ProcessSerialTx()
|
||||
{
|
||||
//Make sure we can actually talk
|
||||
ReOpenSerial();
|
||||
|
||||
//while there's stuff to write, write it
|
||||
//Clients are responsible for adding TX messages to the buffer through get/set/save commands
|
||||
while (_iquart_interface.GetTxBytes(_tx_buf, _bytes_available)) {
|
||||
write(_uart_fd, _tx_buf, _bytes_available);
|
||||
}
|
||||
}
|
||||
|
||||
GenericInterface *VertiqSerialInterface::GetIquartInterface()
|
||||
{
|
||||
return &_iquart_interface;
|
||||
}
|
||||
|
||||
void VertiqSerialInterface::ReOpenSerial()
|
||||
{
|
||||
if (_uart_fd < 0) {
|
||||
_uart_fd = open(_port_in_use, O_RDWR | O_NOCTTY);
|
||||
}
|
||||
}
|
||||
132
src/drivers/actuators/vertiq_io/vertiq_serial_interface.hpp
Normal file
132
src/drivers/actuators/vertiq_io/vertiq_serial_interface.hpp
Normal file
@ -0,0 +1,132 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Copyright (c) 2024 PX4 Development Team. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name PX4 nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <px4_log.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "iq-module-communication-cpp/inc/generic_interface.hpp"
|
||||
#include "iq-module-communication-cpp/inc/propeller_motor_control_client.hpp"
|
||||
|
||||
class VertiqSerialInterface
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new Vertiq Serial Interface object
|
||||
*
|
||||
*/
|
||||
VertiqSerialInterface();
|
||||
|
||||
/**
|
||||
* @brief Initialize our serial peripheral
|
||||
*/
|
||||
int InitSerial(const char *uart_device, unsigned baud);
|
||||
|
||||
/**
|
||||
* Turn off and close the serial connection
|
||||
*/
|
||||
void DeinitSerial();
|
||||
|
||||
/**
|
||||
* set the Baudrate
|
||||
* @param baud
|
||||
* @return 0 on success, <0 on error
|
||||
*/
|
||||
int ConfigureSerialPeripheral(unsigned baud);
|
||||
|
||||
/**
|
||||
* @brief Check to see if there are any valid IQUART packets for us to read
|
||||
*
|
||||
* @return true If there is a packet
|
||||
* @return false If there is not a packet
|
||||
*/
|
||||
bool CheckForRx();
|
||||
|
||||
/**
|
||||
* @brief Read a packet from our packet finder and return a pointer to the beginning of the data
|
||||
*
|
||||
* @return uint8_t* A pointer to the start of the packet
|
||||
*/
|
||||
uint8_t *ReadAndSetRxBytes();
|
||||
|
||||
/**
|
||||
* @brief Send the data we got to all of our clients in order to keep everyone up to date with responses
|
||||
*
|
||||
* @param client_array A pointer to our array of clients
|
||||
* @param number_of_clients The number of clients in the array
|
||||
* @return int
|
||||
*/
|
||||
void ProcessSerialRx(ClientAbstract **client_array, uint8_t number_of_clients);
|
||||
|
||||
/**
|
||||
* @brief check to see if there is any data that we need to transmit over serial
|
||||
*/
|
||||
void ProcessSerialTx();
|
||||
|
||||
/**
|
||||
* @brief give access to our iquart interface so that others can use it
|
||||
* @return a pointer to our _iquart_itnerface object
|
||||
*/
|
||||
GenericInterface *GetIquartInterface();
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Ensures that the serial port is open. Opens it if not
|
||||
*
|
||||
*/
|
||||
void ReOpenSerial();
|
||||
|
||||
GenericInterface _iquart_interface;
|
||||
|
||||
uint8_t _bytes_available;
|
||||
|
||||
//Buffers for data to transmit or that we're receiving
|
||||
uint8_t _rx_buf[256];
|
||||
uint8_t _tx_buf[256];
|
||||
|
||||
//The port that we're using for communication
|
||||
int _uart_fd{-1};
|
||||
char _port_in_use[20] {};
|
||||
|
||||
#if ! defined(__PX4_QURT)
|
||||
struct termios _orig_cfg;
|
||||
struct termios _cfg;
|
||||
#endif
|
||||
int _speed = -1;
|
||||
|
||||
|
||||
};
|
||||
202
src/drivers/actuators/vertiq_io/vertiq_telemetry_manager.cpp
Normal file
202
src/drivers/actuators/vertiq_io/vertiq_telemetry_manager.cpp
Normal file
@ -0,0 +1,202 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Copyright (c) 2024 PX4 Development Team. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name PX4 nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include "vertiq_telemetry_manager.hpp"
|
||||
|
||||
VertiqTelemetryManager::VertiqTelemetryManager(VertiqClientManager *client_manager) :
|
||||
_client_manager(client_manager),
|
||||
_telem_state(UNPAUSED)
|
||||
{
|
||||
}
|
||||
|
||||
void VertiqTelemetryManager::Init(uint64_t telem_bitmask, uint8_t module_id)
|
||||
{
|
||||
//On init, make sure to set our bitmask, and then go ahead and find the front and back 1s
|
||||
_telem_bitmask = telem_bitmask;
|
||||
FindTelemetryModuleIds();
|
||||
|
||||
_telem_interface = new IQUartFlightControllerInterfaceClient(module_id);
|
||||
_client_manager->AddNewClient(_telem_interface);
|
||||
}
|
||||
|
||||
void VertiqTelemetryManager::FindTelemetryModuleIds()
|
||||
{
|
||||
uint64_t temp_bitmask = _telem_bitmask;
|
||||
|
||||
//We have a uint64 with bit positions representing module ids. If you see a 1, that's a module ID we need to get telemetry from
|
||||
//Keep shifting values, and see who's a 1
|
||||
for (uint8_t i = 0; i < MAX_SUPPORTABLE_MODULE_IDS; i++) {
|
||||
//We're only going to keep track of up to MAX_ESC_STATUS_ENTRIES
|
||||
if (temp_bitmask & 0x0000000000000001 && (_number_of_module_ids_for_telem < MAX_ESC_STATUS_ENTRIES)) {
|
||||
//we found one, and have spcae for it. Put it in our array
|
||||
_module_ids_in_use[_number_of_module_ids_for_telem] = i;
|
||||
_number_of_module_ids_for_telem++;
|
||||
}
|
||||
|
||||
temp_bitmask = temp_bitmask >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
void VertiqTelemetryManager::StartPublishing(uORB::Publication<esc_status_s> *esc_status_pub)
|
||||
{
|
||||
//Initialize our ESC status publishing
|
||||
_esc_status.timestamp = hrt_absolute_time();
|
||||
_esc_status.counter = 0;
|
||||
_esc_status.esc_count = _number_of_module_ids_for_telem;
|
||||
_esc_status.esc_connectiontype = esc_status_s::ESC_CONNECTION_TYPE_SERIAL;
|
||||
_esc_status.esc_armed_flags = 0;
|
||||
_esc_status.esc_online_flags = 0;
|
||||
|
||||
for (unsigned i = 0; i < _number_of_module_ids_for_telem; i++) {
|
||||
_esc_status.esc[i].timestamp = 0;
|
||||
_esc_status.esc[i].esc_address = 0;
|
||||
_esc_status.esc[i].esc_rpm = 0;
|
||||
_esc_status.esc[i].esc_state = 0;
|
||||
_esc_status.esc[i].esc_cmdcount = 0;
|
||||
_esc_status.esc[i].esc_voltage = 0;
|
||||
_esc_status.esc[i].esc_current = 0;
|
||||
_esc_status.esc[i].esc_temperature = 0;
|
||||
_esc_status.esc[i].esc_errorcount = 0;
|
||||
_esc_status.esc[i].failures = 0;
|
||||
_esc_status.esc[i].esc_power = 0;
|
||||
}
|
||||
|
||||
//Start advertising to the world
|
||||
esc_status_pub->advertise();
|
||||
}
|
||||
|
||||
uint16_t VertiqTelemetryManager::UpdateTelemetry()
|
||||
{
|
||||
bool got_reply = false;
|
||||
|
||||
//Get the current time to check for timeout
|
||||
hrt_abstime time_now = hrt_absolute_time();
|
||||
|
||||
//We timed out for this request if the time since the last request going out is greater than our timeout period
|
||||
bool timed_out = (time_now - _time_of_last_telem_request) > _telem_timeout;
|
||||
|
||||
//We got a telemetry response
|
||||
if (_telem_interface->telemetry_.IsFresh()) {
|
||||
//grab the data
|
||||
IFCITelemetryData telem_response = _telem_interface->telemetry_.get_reply();
|
||||
|
||||
// also update our internal report for logging
|
||||
_esc_status.esc[_current_module_id_target_index].esc_address = _module_ids_in_use[_number_of_module_ids_for_telem];
|
||||
_esc_status.esc[_current_module_id_target_index].timestamp = time_now;
|
||||
_esc_status.esc[_current_module_id_target_index].esc_rpm = telem_response.speed * 60.0f * M_1_PI_F *
|
||||
0.5f; //We get back rad/s, convert to rpm
|
||||
_esc_status.esc[_current_module_id_target_index].esc_voltage = telem_response.voltage * 0.01;
|
||||
_esc_status.esc[_current_module_id_target_index].esc_current = telem_response.current * 0.01;
|
||||
_esc_status.esc[_current_module_id_target_index].esc_power =
|
||||
_esc_status.esc[_current_module_id_target_index].esc_voltage *
|
||||
_esc_status.esc[_current_module_id_target_index].esc_current;
|
||||
_esc_status.esc[_current_module_id_target_index].esc_temperature = telem_response.mcu_temp *
|
||||
0.01; //"If you ask other escs for their temp, they're giving you the micro temp, so go with that"
|
||||
_esc_status.esc[_current_module_id_target_index].esc_state = 0; //not implemented
|
||||
_esc_status.esc[_current_module_id_target_index].esc_cmdcount = 0; //not implemented
|
||||
_esc_status.esc[_current_module_id_target_index].failures = 0; //not implemented
|
||||
|
||||
//Update the overall _esc_status timestamp and our counter
|
||||
_esc_status.timestamp = time_now;
|
||||
_esc_status.counter++;
|
||||
|
||||
_esc_status.esc_armed_flags |= (0x01 << _current_module_id_target_index);
|
||||
_esc_status.esc_online_flags |= (0x01 << _current_module_id_target_index);
|
||||
|
||||
got_reply = true;
|
||||
}
|
||||
|
||||
//If we got a new response or if we ran out of time to get a response from this motor move on
|
||||
if (got_reply || timed_out) {
|
||||
//We can only be fully paused once the last telemetry attempt is done
|
||||
if (_telem_state == PAUSE_REQUESTED) {
|
||||
_telem_state = PAUSED;
|
||||
}
|
||||
|
||||
_time_of_last_telem_request = hrt_absolute_time();
|
||||
|
||||
uint16_t next_telem = FindNextMotorForTelemetry();
|
||||
|
||||
if (next_telem != _impossible_module_id) {
|
||||
//We need to update the module ID we're going to listen to. So, kill the old one, and make it anew.
|
||||
delete _telem_interface;
|
||||
_telem_interface = new IQUartFlightControllerInterfaceClient(next_telem);
|
||||
}
|
||||
|
||||
//update the telem target
|
||||
return next_telem;
|
||||
}
|
||||
|
||||
//Still waiting for a response or a timeout
|
||||
return _impossible_module_id;
|
||||
}
|
||||
|
||||
uint16_t VertiqTelemetryManager::FindNextMotorForTelemetry()
|
||||
{
|
||||
//If we're paused, we're just going to spit back an impossible module ID. Otherwise, go ahead and find the next target
|
||||
if (_telem_state == UNPAUSED) {
|
||||
//If our current index is the last module ID we've found, then go back to the beginning
|
||||
//otherwise just increment
|
||||
if (_current_module_id_target_index >= _number_of_module_ids_for_telem - 1) {
|
||||
_current_module_id_target_index = 0;
|
||||
|
||||
} else {
|
||||
_current_module_id_target_index++;
|
||||
}
|
||||
|
||||
//Return the next target
|
||||
return _module_ids_in_use[_current_module_id_target_index];
|
||||
}
|
||||
|
||||
return _impossible_module_id;
|
||||
}
|
||||
|
||||
esc_status_s VertiqTelemetryManager::GetEscStatus()
|
||||
{
|
||||
return _esc_status;
|
||||
}
|
||||
|
||||
void VertiqTelemetryManager::PauseTelemetry()
|
||||
{
|
||||
_telem_state = PAUSE_REQUESTED;
|
||||
}
|
||||
|
||||
void VertiqTelemetryManager::UnpauseTelemetry()
|
||||
{
|
||||
_telem_state = UNPAUSED;
|
||||
}
|
||||
|
||||
vertiq_telemetry_pause_states VertiqTelemetryManager::GetTelemetryPauseState()
|
||||
{
|
||||
return _telem_state;
|
||||
}
|
||||
152
src/drivers/actuators/vertiq_io/vertiq_telemetry_manager.hpp
Normal file
152
src/drivers/actuators/vertiq_io/vertiq_telemetry_manager.hpp
Normal file
@ -0,0 +1,152 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* Copyright (c) 2024 PX4 Development Team. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name PX4 nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef VERTIQ_TELEMETRY_MANAGER_HPP
|
||||
#define VERTIQ_TELEMETRY_MANAGER_HPP
|
||||
|
||||
#include <drivers/device/device.h>
|
||||
#include <lib/led/led.h>
|
||||
#include <lib/mixer_module/mixer_module.hpp>
|
||||
#include <lib/perf/perf_counter.h>
|
||||
|
||||
#include <px4_log.h>
|
||||
#include <px4_platform_common/module.h>
|
||||
|
||||
#include <uORB/topics/vehicle_control_mode.h>
|
||||
#include <uORB/topics/manual_control_setpoint.h>
|
||||
#include <uORB/topics/actuator_outputs.h>
|
||||
#include <uORB/topics/led_control.h>
|
||||
#include <uORB/topics/esc_status.h>
|
||||
#include <uORB/topics/actuator_test.h>
|
||||
|
||||
#include "vertiq_client_manager.hpp"
|
||||
|
||||
#include "iq-module-communication-cpp/inc/iquart_flight_controller_interface_client.hpp"
|
||||
|
||||
enum vertiq_telemetry_pause_states {
|
||||
PAUSED,
|
||||
PAUSE_REQUESTED,
|
||||
UNPAUSED
|
||||
};
|
||||
|
||||
static const uint8_t _impossible_module_id = 255;
|
||||
|
||||
class VertiqTelemetryManager
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a new Vertiq Telemetry Manager object
|
||||
*
|
||||
* @param client_manager A pointer to the client manager so that we can add clients to it
|
||||
*/
|
||||
VertiqTelemetryManager(VertiqClientManager *client_manager);
|
||||
|
||||
/**
|
||||
* @brief Initialize the telemetry manager with the bitmask set in the PX4 parameters
|
||||
* @param telem_bitmask The bitmask set in the PX4 parameters as VERTIQ_TEL_MSK
|
||||
* @param module_id The target module ID at the time of init
|
||||
*/
|
||||
void Init(uint64_t telem_bitmask, uint8_t module_id);
|
||||
|
||||
/**
|
||||
* @brief Start publishing our esc status to the uORB topic
|
||||
*
|
||||
* @param esc_status_pub A pointer to our Publication
|
||||
*/
|
||||
void StartPublishing(uORB::Publication<esc_status_s> *esc_status_pub);
|
||||
|
||||
/**
|
||||
* @brief Part of initialization. Find the first and last positions that indicate modules to grab telemetry from
|
||||
*/
|
||||
void FindTelemetryModuleIds();
|
||||
|
||||
/**
|
||||
* @brief Attempt to grab the telemetry response from the currently targeted module. Handles
|
||||
* updating when to start requesting telemetry from the next module.
|
||||
*
|
||||
*/
|
||||
uint16_t UpdateTelemetry();
|
||||
|
||||
/**
|
||||
* @brief Determing the next module to request telemetry from
|
||||
*
|
||||
*/
|
||||
uint16_t FindNextMotorForTelemetry();
|
||||
|
||||
/**
|
||||
* @brief return access to our esc_status_s handler
|
||||
*
|
||||
* @return _esc_status: our instance of an esc_status_s
|
||||
*/
|
||||
esc_status_s GetEscStatus();
|
||||
|
||||
/**
|
||||
* @brief Stops the telemetry manager from calculating the next target for telemetry
|
||||
*
|
||||
*/
|
||||
void PauseTelemetry();
|
||||
|
||||
/**
|
||||
* @brief Resumes looking for the next telemetry target
|
||||
*
|
||||
*/
|
||||
void UnpauseTelemetry();
|
||||
|
||||
/**
|
||||
* @brief Returns the paused state of the telemetry manager
|
||||
*
|
||||
*/
|
||||
vertiq_telemetry_pause_states GetTelemetryPauseState();
|
||||
|
||||
private:
|
||||
VertiqClientManager *_client_manager;
|
||||
|
||||
vertiq_telemetry_pause_states _telem_state; //Keep track of whether or not we've paused telemetry
|
||||
IQUartFlightControllerInterfaceClient *_telem_interface; //Used for reading responses from our telemetry targets
|
||||
esc_status_s _esc_status; //We want to publish our ESC Status to anyone who will listen
|
||||
static const uint8_t MAX_SUPPORTABLE_MODULE_IDS = 63; //[0, 62] //The max number of module IDs that we can support
|
||||
static const uint8_t MAX_ESC_STATUS_ENTRIES =
|
||||
8; //The max number of esc status entries we can keep track of (per the esc_status_s type)
|
||||
|
||||
//We need a way to store the module IDs that we're supposed to ask for telemetry from. We can have as many as 63.
|
||||
uint8_t _module_ids_in_use[MAX_ESC_STATUS_ENTRIES];
|
||||
uint8_t _number_of_module_ids_for_telem = 0;
|
||||
uint8_t _current_module_id_target_index = 0;
|
||||
|
||||
uint64_t _telem_bitmask = 0; //Store the telemetry bitmask for who we want to get telemetry from
|
||||
static const hrt_abstime _telem_timeout = 50_ms; //The amount of time (in ms) that we'll wait for a telemetry response
|
||||
hrt_abstime _time_of_last_telem_request = 0; //The system time the last time that we got telemetry
|
||||
};
|
||||
|
||||
#endif
|
||||
Loading…
x
Reference in New Issue
Block a user