VOXL2 specific drivers, modules, and miscellaneous support files (#22588)

This commit is contained in:
Eric Katzfey
2024-01-18 12:14:17 -05:00
committed by GitHub
parent b60e73c76f
commit 2b69a3d290
96 changed files with 8266 additions and 678 deletions
-5
View File
@@ -1,5 +0,0 @@
menuconfig DRIVERS_ACTUATORS_MODAL_IO
bool "modal_io"
default n
---help---
Enable support for modal_io
@@ -1,26 +0,0 @@
module_name: MODAL IO Output
actuator_output:
show_subgroups_if: 'MODAL_IO_CONFIG>0'
config_parameters:
- param: 'MODAL_IO_CONFIG'
label: 'Configure'
function: 'enable'
- param: 'MODAL_IO_BAUD'
label: 'Bitrate'
- param: 'MODAL_IO_RPM_MIN'
label: 'RPM Min'
- param: 'MODAL_IO_RPM_MAX'
label: 'RPM Max'
- param: 'MODAL_IO_SDIR1'
label: 'ESC 1 Spin Direction'
- param: 'MODAL_IO_SDIR2'
label: 'ESC 2 Spin Direction'
- param: 'MODAL_IO_SDIR3'
label: 'ESC 3 Spin Direction'
- param: 'MODAL_IO_SDIR4'
label: 'ESC 4 Spin Direction'
output_groups:
- param_prefix: MODAL_IO
group_label: 'ESCs'
channel_label: 'ESC'
num_channels: 4
@@ -32,20 +32,21 @@
############################################################################
px4_add_module(
MODULE drivers__actuators__modal_io
MAIN modal_io
MODULE drivers__actuators__voxl_esc
MAIN voxl_esc
SRCS
crc16.c
crc16.h
modal_io_serial.cpp
modal_io_serial.hpp
modal_io.cpp
modal_io.hpp
voxl_esc_serial.cpp
voxl_esc_serial.hpp
voxl_esc.cpp
voxl_esc.hpp
qc_esc_packet_types.h
qc_esc_packet.c
qc_esc_packet.h
DEPENDS
battery
px4_work_queue
mixer_module
MODULE_CONFIG
+5
View File
@@ -0,0 +1,5 @@
menuconfig DRIVERS_ACTUATORS_VOXL_ESC
bool "voxl_esc"
default n
---help---
Enable support for voxl_esc
@@ -0,0 +1,41 @@
module_name: VOXL ESC Output
actuator_output:
show_subgroups_if: 'VOXL_ESC_CONFIG>0'
config_parameters:
- param: 'VOXL_ESC_CONFIG'
label: 'Configure'
function: 'enable'
- param: 'VOXL_ESC_BAUD'
label: 'Bitrate'
- param: 'VOXL_ESC_RPM_MIN'
label: 'RPM Min'
- param: 'VOXL_ESC_RPM_MAX'
label: 'RPM Max'
- param: 'VOXL_ESC_SDIR1'
label: 'ESC 1 Spin Direction'
- param: 'VOXL_ESC_SDIR2'
label: 'ESC 2 Spin Direction'
- param: 'VOXL_ESC_SDIR3'
label: 'ESC 3 Spin Direction'
- param: 'VOXL_ESC_SDIR4'
label: 'ESC 4 Spin Direction'
output_groups:
- param_prefix: VOXL_ESC
group_label: 'ESCs'
channel_label: 'ESC'
num_channels: 4
parameters:
- group: ModalAI Custom Configuration
definitions:
MODALAI_CONFIG:
description:
short: Custom configuration for ModalAI drones
long: |
This can be set to indicate that drone behavior
needs to be changed to match a custom setting
type: int32
reboot_required: true
num_instances: 1
instance_start: 1
default: 0
@@ -119,36 +119,60 @@ int32_t qc_esc_create_pwm_packet4_fb(int16_t pwm0, int16_t pwm1, int16_t pwm2, i
}
int32_t qc_esc_create_rpm_packet4(int16_t rpm0, int16_t rpm1, int16_t rpm2, int16_t rpm3,
int32_t qc_esc_create_rpm_packet4(int32_t rpm0, int32_t rpm1, int32_t rpm2, int32_t rpm3,
uint8_t led0, uint8_t led1, uint8_t led2, uint8_t led3,
uint8_t *out, uint16_t out_size)
uint8_t *out, uint16_t out_size, uint8_t ext_rpm)
{
return qc_esc_create_rpm_packet4_fb(rpm0, rpm1, rpm2, rpm3, led0, led1, led2, led3, -1, out, out_size);
return qc_esc_create_rpm_packet4_fb(rpm0, rpm1, rpm2, rpm3, led0, led1, led2, led3, -1, out, out_size, ext_rpm);
}
int32_t qc_esc_create_rpm_packet4_fb(int16_t rpm0, int16_t rpm1, int16_t rpm2, int16_t rpm3,
int32_t qc_esc_create_rpm_packet4_fb(int32_t rpm0, int32_t rpm1, int32_t rpm2, int32_t rpm3,
uint8_t led0, uint8_t led1, uint8_t led2, uint8_t led3,
int32_t fb_id, uint8_t *out, uint16_t out_size)
int32_t fb_id, uint8_t *out, uint16_t out_size, uint8_t ext_rpm)
{
uint16_t data[5];
int16_t data[5];
uint16_t leds = 0;
uint8_t cmd = ESC_PACKET_TYPE_RPM_CMD;
int32_t max = ext_rpm > 0 ? ESC_RPM_MAX_EXT : ESC_RPM_MAX;
int32_t min = ext_rpm > 0 ? ESC_RPM_MIN_EXT : ESC_RPM_MIN;
// Limit RPMs to prevent overflow when converting to int16_t
if (rpm0 > max) { rpm0 = max; } if (rpm0 < min) { rpm0 = min; }
if (rpm1 > max) { rpm1 = max; } if (rpm1 < min) { rpm1 = min; }
if (rpm2 > max) { rpm2 = max; } if (rpm2 < min) { rpm2 = min; }
if (rpm3 > max) { rpm3 = max; } if (rpm3 < min) { rpm3 = min; }
if (fb_id != -1) { fb_id = fb_id % 4; }
//least significant bit is used for feedback request
rpm0 &= ~(0x0001); rpm1 &= ~(0x0001); rpm2 &= ~(0x0001); rpm3 &= ~(0x0001);
if (fb_id == 0) { rpm0 |= 0x0001; } if (fb_id == 1) { rpm1 |= 0x0001; }
if (fb_id == 2) { rpm2 |= 0x0001; } if (fb_id == 3) { rpm3 |= 0x0001; }
leds |= led0 & 0b00000111;
leds |= led0 & 0b00000111;
leds |= (led1 & 0b00000111) << 3;
leds |= ((uint16_t)(led2 & 0b00000111)) << 6;
leds |= ((uint16_t)(led3 & 0b00000111)) << 9;
data[0] = rpm0; data[1] = rpm1; data[2] = rpm2; data[3] = rpm3; data[4] = leds;
return qc_esc_create_packet(ESC_PACKET_TYPE_RPM_CMD, (uint8_t *) & (data[0]), 10, out, out_size);
if (ext_rpm > 0) {
cmd = ESC_PACKET_TYPE_RPM_DIV2_CMD;
data[0] = ((rpm0 / 4) * 2);
data[1] = ((rpm1 / 4) * 2);
data[2] = ((rpm2 / 4) * 2);
data[3] = ((rpm3 / 4) * 2);
data[4] = leds;
} else {
data[0] = rpm0; data[1] = rpm1; data[2] = rpm2; data[3] = rpm3; data[4] = leds;
}
//least significant bit is used for feedback request
data[0] &= ~(0x0001); data[1] &= ~(0x0001); data[2] &= ~(0x0001); data[3] &= ~(0x0001);
if (fb_id == 0) { data[0] |= 0x0001; } if (fb_id == 1) { data[1] |= 0x0001; }
if (fb_id == 2) { data[2] |= 0x0001; } if (fb_id == 3) { data[3] |= 0x0001; }
return qc_esc_create_packet(cmd, (uint8_t *) & (data[0]), 10, out, out_size);
}
int32_t qc_esc_create_packet(uint8_t type, uint8_t *data, uint16_t size, uint8_t *out, uint16_t out_size)
@@ -52,6 +52,11 @@ extern "C" {
#define QC_ESC_LED_GREEN_ON 2
#define QC_ESC_LED_BLUE_ON 4
// Define RPM command max and min values
#define ESC_RPM_MAX INT16_MAX-1 // 32k
#define ESC_RPM_MIN INT16_MIN+1 // -32k
#define ESC_RPM_MAX_EXT UINT16_MAX-5 // 65k
#define ESC_RPM_MIN_EXT -UINT16_MAX+5 // -65k
// Header of the packet. Each packet must start with this header
#define ESC_PACKET_HEADER 0xAF
@@ -142,6 +147,18 @@ typedef struct {
} __attribute__((__packed__)) QC_ESC_FB_RESPONSE_V2;
// Definition of the feedback response packet from ESC, which contains battery voltage and total current
typedef struct {
uint8_t header;
uint8_t length;
uint8_t type;
uint8_t id; //ESC Id (could be used as system ID elsewhere)
uint16_t voltage; //Input voltage (Millivolts)
int16_t current; //Total Current (8mA resolution)
uint16_t crc;
} __attribute__((__packed__)) QC_ESC_FB_POWER_STATUS;
//-------------------------------------------------------------------------
//Below are functions for generating packets that would be outgoing to ESCs
//-------------------------------------------------------------------------
@@ -197,15 +214,15 @@ int32_t qc_esc_create_pwm_packet4_fb(int16_t pwm0, int16_t pwm1, int16_t pwm2, i
// Create a packet for sending closed-loop RPM command and LED command to 4 ESCs without requesting any feedback
// Return value is the length of generated packet (if positive), otherwise error code
int32_t qc_esc_create_rpm_packet4(int16_t rpm0, int16_t rpm1, int16_t rpm2, int16_t rpm3,
int32_t qc_esc_create_rpm_packet4(int32_t rpm0, int32_t rpm1, int32_t rpm2, int32_t rpm3,
uint8_t led0, uint8_t led1, uint8_t led2, uint8_t led3,
uint8_t *out, uint16_t out_size);
uint8_t *out, uint16_t out_size, uint8_t ext_rpm);
// Create a packet for sending closed-loop RPM command and LED command to 4 ESCs, also request feedback from one ESC (with id=fb_id)
// Create a packet for sending closed-loop RPM command (32766 or 65530 max RPM) and LED command to 4 ESCs, also request feedback from one ESC (with id=fb_id)
// Return value is the length of generated packet (if positive), otherwise error code
int32_t qc_esc_create_rpm_packet4_fb(int16_t rpm0, int16_t rpm1, int16_t rpm2, int16_t rpm3,
int32_t qc_esc_create_rpm_packet4_fb(int32_t rpm0, int32_t rpm1, int32_t rpm2, int32_t rpm3,
uint8_t led0, uint8_t led1, uint8_t led2, uint8_t led3,
int32_t fb_id, uint8_t *out, uint16_t out_size);
int32_t fb_id, uint8_t *out, uint16_t out_size, uint8_t ext_rpm);
//-------------------------------------------------------------------------
@@ -46,6 +46,7 @@
#define ESC_PACKET_TYPE_SOUND_CMD 3
#define ESC_PACKET_TYPE_STEP_CMD 4
#define ESC_PACKET_TYPE_LED_CMD 5
#define ESC_PACKET_TYPE_RPM_DIV2_CMD 7
#define ESC_PACKET_TYPE_RESET_CMD 10
#define ESC_PACKET_TYPE_SET_ID_CMD 11
#define ESC_PACKET_TYPE_SET_DIR_CMD 12
@@ -69,5 +70,6 @@
#define ESC_PACKET_TYPE_TUNE_CONFIG 114
#define ESC_PACKET_TYPE_FB_RESPONSE 128
#define ESC_PACKET_TYPE_VERSION_EXT_RESPONSE 131
#define ESC_PACKET_TYPE_FB_POWER_STATUS 132
#endif
@@ -35,33 +35,32 @@
#include <px4_platform_common/getopt.h>
#include "modal_io.hpp"
#include "modal_io_serial.hpp"
// utility for running on VOXL and using driver as a bridge
#define MODAL_IO_VOXL_BRIDGE_PORT "/dev/ttyS4"
#include "voxl_esc.hpp"
#include "voxl_esc_serial.hpp"
// future use:
#define MODALAI_PUBLISH_ESC_STATUS 0
const char *_device;
ModalIo::ModalIo() :
OutputModuleInterface(MODULE_NAME, px4::serial_port_to_wq(MODAL_IO_DEFAULT_PORT)),
VoxlEsc::VoxlEsc() :
OutputModuleInterface(MODULE_NAME, px4::serial_port_to_wq(VOXL_ESC_DEFAULT_PORT)),
_mixing_output{"VOXL_ESC", VOXL_ESC_OUTPUT_CHANNELS, *this, MixingOutput::SchedulingPolicy::Auto, false, false},
_cycle_perf(perf_alloc(PC_ELAPSED, MODULE_NAME": cycle")),
_output_update_perf(perf_alloc(PC_INTERVAL, MODULE_NAME": output update interval"))
_output_update_perf(perf_alloc(PC_INTERVAL, MODULE_NAME": output update interval")),
_battery(1, nullptr, _battery_report_interval, battery_status_s::BATTERY_SOURCE_POWER_MODULE)
{
_device = MODAL_IO_DEFAULT_PORT;
_device = VOXL_ESC_DEFAULT_PORT;
_mixing_output.setAllFailsafeValues(0);
_mixing_output.setAllDisarmedValues(0);
_esc_status.timestamp = hrt_absolute_time();
_esc_status.counter = 0;
_esc_status.esc_count = MODAL_IO_OUTPUT_CHANNELS;
_esc_status.esc_count = VOXL_ESC_OUTPUT_CHANNELS;
_esc_status.esc_connectiontype = esc_status_s::ESC_CONNECTION_TYPE_SERIAL;
for (unsigned i = 0; i < MODAL_IO_OUTPUT_CHANNELS; i++) {
for (unsigned i = 0; i < VOXL_ESC_OUTPUT_CHANNELS; i++) {
_esc_status.esc[i].timestamp = 0;
_esc_status.esc[i].esc_address = 0;
_esc_status.esc[i].esc_rpm = 0;
@@ -75,15 +74,12 @@ ModalIo::ModalIo() :
_esc_status.esc[i].esc_power = 0;
}
_esc_status_pub.advertise();
qc_esc_packet_init(&_fb_packet);
qc_esc_packet_init(&_uart_bridge_packet);
_fb_idx = 0;
}
ModalIo::~ModalIo()
VoxlEsc::~VoxlEsc()
{
_outputs_on = false;
@@ -92,16 +88,11 @@ ModalIo::~ModalIo()
_uart_port = nullptr;
}
if (_uart_port_bridge) {
_uart_port_bridge->uart_close();
_uart_port_bridge = nullptr;
}
perf_free(_cycle_perf);
perf_free(_output_update_perf);
}
int ModalIo::init()
int VoxlEsc::init()
{
/* Getting initial parameter values */
@@ -111,10 +102,15 @@ int ModalIo::init()
return ret;
}
_uart_port = new ModalIoSerial();
_uart_port_bridge = new ModalIoSerial();
_uart_port = new VoxlEscSerial();
memset(&_esc_chans, 0x00, sizeof(_esc_chans));
for (int esc_id = 0; esc_id < VOXL_ESC_OUTPUT_CHANNELS; ++esc_id) {
_version_info[esc_id].sw_version = UINT16_MAX;
_version_info[esc_id].hw_version = UINT16_MAX;
_version_info[esc_id].id = esc_id;
}
//get_instance()->ScheduleOnInterval(10000); //100hz
ScheduleNow();
@@ -122,81 +118,82 @@ int ModalIo::init()
return 0;
}
int ModalIo::load_params(modal_io_params_t *params, ch_assign_t *map)
int VoxlEsc::load_params(voxl_esc_params_t *params, ch_assign_t *map)
{
int ret = PX4_OK;
// initialize out
for (int i = 0; i < MODAL_IO_OUTPUT_CHANNELS; i++) {
for (int i = 0; i < VOXL_ESC_OUTPUT_CHANNELS; i++) {
params->function_map[i] = (int)OutputFunction::Disabled;
params->direction_map[i] = 0;
params->motor_map[i] = 0;
}
param_get(param_find("MODAL_IO_CONFIG"), &params->config);
param_get(param_find("MODAL_IO_MODE"), &params->mode);
param_get(param_find("MODAL_IO_BAUD"), &params->baud_rate);
param_get(param_find("VOXL_ESC_CONFIG"), &params->config);
param_get(param_find("VOXL_ESC_MODE"), &params->mode);
param_get(param_find("VOXL_ESC_BAUD"), &params->baud_rate);
param_get(param_find("MODAL_IO_T_PERC"), &params->turtle_motor_percent);
param_get(param_find("MODAL_IO_T_DEAD"), &params->turtle_motor_deadband);
param_get(param_find("MODAL_IO_T_EXPO"), &params->turtle_motor_expo);
param_get(param_find("MODAL_IO_T_MINF"), &params->turtle_stick_minf);
param_get(param_find("MODAL_IO_T_COSP"), &params->turtle_cosphi);
param_get(param_find("VOXL_ESC_T_PERC"), &params->turtle_motor_percent);
param_get(param_find("VOXL_ESC_T_DEAD"), &params->turtle_motor_deadband);
param_get(param_find("VOXL_ESC_T_EXPO"), &params->turtle_motor_expo);
param_get(param_find("VOXL_ESC_T_MINF"), &params->turtle_stick_minf);
param_get(param_find("VOXL_ESC_T_COSP"), &params->turtle_cosphi);
param_get(param_find("MODAL_IO_FUNC1"), &params->function_map[0]);
param_get(param_find("MODAL_IO_FUNC2"), &params->function_map[1]);
param_get(param_find("MODAL_IO_FUNC3"), &params->function_map[2]);
param_get(param_find("MODAL_IO_FUNC4"), &params->function_map[3]);
param_get(param_find("VOXL_ESC_FUNC1"), &params->function_map[0]);
param_get(param_find("VOXL_ESC_FUNC2"), &params->function_map[1]);
param_get(param_find("VOXL_ESC_FUNC3"), &params->function_map[2]);
param_get(param_find("VOXL_ESC_FUNC4"), &params->function_map[3]);
param_get(param_find("MODAL_IO_SDIR1"), &params->direction_map[0]);
param_get(param_find("MODAL_IO_SDIR2"), &params->direction_map[1]);
param_get(param_find("MODAL_IO_SDIR3"), &params->direction_map[2]);
param_get(param_find("MODAL_IO_SDIR4"), &params->direction_map[3]);
param_get(param_find("VOXL_ESC_SDIR1"), &params->direction_map[0]);
param_get(param_find("VOXL_ESC_SDIR2"), &params->direction_map[1]);
param_get(param_find("VOXL_ESC_SDIR3"), &params->direction_map[2]);
param_get(param_find("VOXL_ESC_SDIR4"), &params->direction_map[3]);
param_get(param_find("MODAL_IO_RPM_MIN"), &params->rpm_min);
param_get(param_find("MODAL_IO_RPM_MAX"), &params->rpm_max);
param_get(param_find("VOXL_ESC_RPM_MIN"), &params->rpm_min);
param_get(param_find("VOXL_ESC_RPM_MAX"), &params->rpm_max);
param_get(param_find("MODAL_IO_VLOG"), &params->verbose_logging);
param_get(param_find("VOXL_ESC_VLOG"), &params->verbose_logging);
param_get(param_find("VOXL_ESC_PUB_BST"), &params->publish_battery_status);
if (params->rpm_min >= params->rpm_max) {
PX4_ERR("Invalid parameter MODAL_IO_RPM_MIN. Please verify parameters.");
PX4_ERR("Invalid parameter VOXL_ESC_RPM_MIN. Please verify parameters.");
params->rpm_min = 0;
ret = PX4_ERROR;
}
if (params->turtle_motor_percent < 0 || params->turtle_motor_percent > 100) {
PX4_ERR("Invalid parameter MODAL_IO_T_PERC. Please verify parameters.");
PX4_ERR("Invalid parameter VOXL_ESC_T_PERC. Please verify parameters.");
params->turtle_motor_percent = 0;
ret = PX4_ERROR;
}
if (params->turtle_motor_deadband < 0 || params->turtle_motor_deadband > 100) {
PX4_ERR("Invalid parameter MODAL_IO_T_DEAD. Please verify parameters.");
PX4_ERR("Invalid parameter VOXL_ESC_T_DEAD. Please verify parameters.");
params->turtle_motor_deadband = 0;
ret = PX4_ERROR;
}
if (params->turtle_motor_expo < 0 || params->turtle_motor_expo > 100) {
PX4_ERR("Invalid parameter MODAL_IO_T_EXPO. Please verify parameters.");
PX4_ERR("Invalid parameter VOXL_ESC_T_EXPO. Please verify parameters.");
params->turtle_motor_expo = 0;
ret = PX4_ERROR;
}
if (params->turtle_stick_minf < 0.0f || params->turtle_stick_minf > 100.0f) {
PX4_ERR("Invalid parameter MODAL_IO_T_MINF. Please verify parameters.");
PX4_ERR("Invalid parameter VOXL_ESC_T_MINF. Please verify parameters.");
params->turtle_stick_minf = 0.0f;
ret = PX4_ERROR;
}
if (params->turtle_cosphi < 0.0f || params->turtle_cosphi > 100.0f) {
PX4_ERR("Invalid parameter MODAL_IO_T_COSP. Please verify parameters.");
PX4_ERR("Invalid parameter VOXL_ESC_T_COSP. Please verify parameters.");
params->turtle_cosphi = 0.0f;
ret = PX4_ERROR;
}
for (int i = 0; i < MODAL_IO_OUTPUT_CHANNELS; i++) {
for (int i = 0; i < VOXL_ESC_OUTPUT_CHANNELS; i++) {
if (params->function_map[i] < (int)OutputFunction::Motor1 || params->function_map[i] > (int)OutputFunction::Motor4) {
PX4_ERR("Invalid parameter MODAL_IO_FUNCX. Only supports motors 1-4. Please verify parameters.");
PX4_ERR("Invalid parameter VOXL_ESC_FUNCX. Only supports motors 1-4. Please verify parameters.");
params->function_map[i] = 0;
ret = PX4_ERROR;
@@ -210,11 +207,11 @@ int ModalIo::load_params(modal_io_params_t *params, ch_assign_t *map)
}
}
for (int i = 0; i < MODAL_IO_OUTPUT_CHANNELS; i++) {
if (params->motor_map[i] == MODAL_IO_OUTPUT_DISABLED ||
params->motor_map[i] < -(MODAL_IO_OUTPUT_CHANNELS) ||
params->motor_map[i] > MODAL_IO_OUTPUT_CHANNELS) {
PX4_ERR("Invalid parameter MODAL_IO_MOTORX. Please verify parameters.");
for (int i = 0; i < VOXL_ESC_OUTPUT_CHANNELS; i++) {
if (params->motor_map[i] == VOXL_ESC_OUTPUT_DISABLED ||
params->motor_map[i] < -(VOXL_ESC_OUTPUT_CHANNELS) ||
params->motor_map[i] > VOXL_ESC_OUTPUT_CHANNELS) {
PX4_ERR("Invalid parameter VOXL_ESC_MOTORX. Please verify parameters.");
params->motor_map[i] = 0;
ret = PX4_ERROR;
}
@@ -227,7 +224,7 @@ int ModalIo::load_params(modal_io_params_t *params, ch_assign_t *map)
return ret;
}
int ModalIo::task_spawn(int argc, char *argv[])
int VoxlEsc::task_spawn(int argc, char *argv[])
{
int myoptind = 0;
int ch;
@@ -244,7 +241,7 @@ int ModalIo::task_spawn(int argc, char *argv[])
}
}
ModalIo *instance = new ModalIo();
VoxlEsc *instance = new VoxlEsc();
if (instance) {
_object.store(instance);
@@ -267,14 +264,31 @@ int ModalIo::task_spawn(int argc, char *argv[])
return PX4_ERROR;
}
int ModalIo::flush_uart_rx()
int VoxlEsc::flush_uart_rx()
{
while (_uart_port->uart_read(_read_buf, sizeof(_read_buf)) > 0) {}
return 0;
}
int ModalIo::read_response(Command *out_cmd)
bool VoxlEsc::check_versions_updated()
{
for (int esc_id = 0; esc_id < VOXL_ESC_OUTPUT_CHANNELS; ++esc_id) {
if (_version_info[esc_id].sw_version == UINT16_MAX) { return false; }
}
// PX4_INFO("Got all ESC Version info!");
_extended_rpm = true;
_need_version_info = false;
for (int esc_id = 0; esc_id < VOXL_ESC_OUTPUT_CHANNELS; ++esc_id) {
if (_version_info[esc_id].sw_version < VOXL_ESC_EXT_RPM) { _extended_rpm = false; }
}
return true;
}
int VoxlEsc::read_response(Command *out_cmd)
{
px4_usleep(_current_cmd.resp_delay_us);
@@ -297,7 +311,7 @@ int ModalIo::read_response(Command *out_cmd)
return 0;
}
int ModalIo::parse_response(uint8_t *buf, uint8_t len, bool print_feedback)
int VoxlEsc::parse_response(uint8_t *buf, uint8_t len, bool print_feedback)
{
hrt_abstime tnow = hrt_absolute_time();
@@ -311,13 +325,13 @@ int ModalIo::parse_response(uint8_t *buf, uint8_t len, bool print_feedback)
uint8_t packet_size = qc_esc_packet_get_size(&_fb_packet);
if (packet_type == ESC_PACKET_TYPE_FB_RESPONSE && packet_size == sizeof(QC_ESC_FB_RESPONSE_V2)) {
//PX4_INFO("Got feedback V2 packet!");
// PX4_INFO("Got feedback V2 packet!");
QC_ESC_FB_RESPONSE_V2 fb;
memcpy(&fb, _fb_packet.buffer, packet_size);
uint32_t id = (fb.id_state & 0xF0) >> 4; //ID of the ESC based on hardware address
if (id < MODAL_IO_OUTPUT_CHANNELS) {
if (id < VOXL_ESC_OUTPUT_CHANNELS) {
int motor_idx = _output_map[id].number - 1; // mapped motor id.. user defined mapping is 1-4, array is 0-3
@@ -335,9 +349,9 @@ int ModalIo::parse_response(uint8_t *buf, uint8_t len, bool print_feedback)
_esc_chans[id].power_applied = fb.power;
_esc_chans[id].state = fb.id_state & 0x0F;
_esc_chans[id].cmd_counter = fb.cmd_counter;
_esc_chans[id].voltage = fb.voltage * 0.001;
_esc_chans[id].current = fb.current * 0.008;
_esc_chans[id].temperature = fb.temperature * 0.01;
_esc_chans[id].voltage = fb.voltage * 0.001f;
_esc_chans[id].current = fb.current * 0.008f;
_esc_chans[id].temperature = fb.temperature * 0.01f;
_esc_chans[id].feedback_time = tnow;
// also update our internal report for logging
@@ -382,6 +396,13 @@ int ModalIo::parse_response(uint8_t *buf, uint8_t len, bool print_feedback)
else if (packet_type == ESC_PACKET_TYPE_VERSION_RESPONSE && packet_size == sizeof(QC_ESC_VERSION_INFO)) {
QC_ESC_VERSION_INFO ver;
memcpy(&ver, _fb_packet.buffer, packet_size);
if (_need_version_info) {
memcpy(&_version_info[ver.id], &ver, sizeof(QC_ESC_VERSION_INFO));
check_versions_updated();
break;
}
PX4_INFO("ESC ID: %i", ver.id);
PX4_INFO("HW Version: %i", ver.hw_version);
PX4_INFO("SW Version: %i", ver.sw_version);
@@ -400,83 +421,60 @@ int ModalIo::parse_response(uint8_t *buf, uint8_t len, bool print_feedback)
PX4_INFO("\tFirmware : version %4d, hash %.12s", ver.sw_version, ver.firmware_git_version);
PX4_INFO("\tBootloader : version %4d, hash %.12s", ver.bootloader_version, ver.bootloader_git_version);
} else if (packet_type == ESC_PACKET_TYPE_FB_POWER_STATUS && packet_size == sizeof(QC_ESC_FB_POWER_STATUS)) {
QC_ESC_FB_POWER_STATUS packet;
memcpy(&packet, _fb_packet.buffer, packet_size);
float voltage = packet.voltage * 0.001f; // Voltage is reported at 1 mV resolution
float current = packet.current * 0.008f; // Total current is reported at 8mA resolution
// Limit the frequency of battery status reports
if (_parameters.publish_battery_status) {
_battery.setConnected(true);
_battery.updateVoltage(voltage);
_battery.updateCurrent(current);
hrt_abstime current_time = hrt_absolute_time();
if ((current_time - _last_battery_report_time) >= _battery_report_interval) {
_last_battery_report_time = current_time;
_battery.updateAndPublishBatteryStatus(current_time);
}
}
}
} else { //parser error
switch (ret) {
case ESC_ERROR_BAD_CHECKSUM:
_rx_crc_error_count++;
//PX4_INFO("BAD ESC packet checksum");
// PX4_INFO("BAD ESC packet checksum");
break;
case ESC_ERROR_BAD_LENGTH:
//PX4_INFO("BAD ESC packet length");
// PX4_INFO("BAD ESC packet length");
break;
}
}
}
/*
if (len < 4 || buf[0] != ESC_PACKET_HEADER) {
return -1;
}
switch (buf[2]) {
case ESC_PACKET_TYPE_VERSION_RESPONSE:
if (len != sizeof(QC_ESC_VERSION_INFO)) {
return -1;
} else {
QC_ESC_VERSION_INFO ver;
memcpy(&ver, buf, len);
PX4_INFO("ESC ID: %i", ver.id);
PX4_INFO("HW Version: %i", ver.hw_version);
PX4_INFO("SW Version: %i", ver.sw_version);
PX4_INFO("Unique ID: %i", ver.unique_id);
}
break;
case ESC_PACKET_TYPE_FB_RESPONSE:
if (len != sizeof(QC_ESC_FB_RESPONSE)) {
return -1;
} else {
QC_ESC_FB_RESPONSE fb;
memcpy(&fb, buf, len);
uint8_t id = (fb.state & 0xF0) >> 4;
if (id < MODAL_IO_OUTPUT_CHANNELS) {
_esc_chans[id].rate_meas = fb.rpm;
_esc_chans[id].state = fb.state & 0x0F;
_esc_chans[id].cmd_counter = fb.cmd_counter;
_esc_chans[id].voltage = 9.0 + fb.voltage / 34.0;
}
}
break;
default:
return -1;
}
*/
return 0;
}
int ModalIo::check_for_esc_timeout()
int VoxlEsc::check_for_esc_timeout()
{
hrt_abstime tnow = hrt_absolute_time();
for (int i = 0; i < MODAL_IO_OUTPUT_CHANNELS; i++) {
for (int i = 0; i < VOXL_ESC_OUTPUT_CHANNELS; i++) {
// PX4 motor indexed user defined mapping is 1-4, we want to use in bitmask (0-3)
uint8_t motor_idx = _output_map[i].number - 1;
if (motor_idx < MODAL_IO_OUTPUT_CHANNELS) {
if (motor_idx < VOXL_ESC_OUTPUT_CHANNELS) {
// we are using PX4 motor index in the bitmask
if (_esc_status.esc_online_flags & (1 << motor_idx)) {
// using index i here for esc_chans enumeration stored in ESC ID order
if ((tnow - _esc_chans[i].feedback_time) > MODAL_IO_DISCONNECT_TIMEOUT_US) {
if ((tnow - _esc_chans[i].feedback_time) > VOXL_ESC_DISCONNECT_TIMEOUT_US) {
// stale data, assume offline and clear armed
_esc_status.esc_online_flags &= ~(1 << motor_idx);
_esc_status.esc_armed_flags &= ~(1 << motor_idx);
@@ -489,7 +487,7 @@ int ModalIo::check_for_esc_timeout()
}
int ModalIo::send_cmd_thread_safe(Command *cmd)
int VoxlEsc::send_cmd_thread_safe(Command *cmd)
{
cmd->id = _cmd_id++;
_pending_cmd.store(cmd);
@@ -504,7 +502,7 @@ int ModalIo::send_cmd_thread_safe(Command *cmd)
int ModalIo::custom_command(int argc, char *argv[])
int VoxlEsc::custom_command(int argc, char *argv[])
{
int myoptind = 0;
int ch;
@@ -530,7 +528,7 @@ int ModalIo::custom_command(int argc, char *argv[])
/* start the FMU if not running */
if (!strcmp(verb, "start")) {
if (!is_running()) {
return ModalIo::task_spawn(argc, argv);
return VoxlEsc::task_spawn(argc, argv);
}
}
@@ -593,7 +591,7 @@ int ModalIo::custom_command(int argc, char *argv[])
}
if (!strcmp(verb, "reset")) {
if (esc_id < MODAL_IO_OUTPUT_CHANNELS) {
if (esc_id < VOXL_ESC_OUTPUT_CHANNELS) {
PX4_INFO("Reset ESC: %i", esc_id);
cmd.len = qc_esc_create_reset_packet(esc_id, cmd.buf, sizeof(cmd.buf));
cmd.response = false;
@@ -605,7 +603,7 @@ int ModalIo::custom_command(int argc, char *argv[])
}
} else if (!strcmp(verb, "version")) {
if (esc_id < MODAL_IO_OUTPUT_CHANNELS) {
if (esc_id < VOXL_ESC_OUTPUT_CHANNELS) {
PX4_INFO("Request version for ESC: %i", esc_id);
cmd.len = qc_esc_create_version_request_packet(esc_id, cmd.buf, sizeof(cmd.buf));
cmd.response = true;
@@ -618,7 +616,7 @@ int ModalIo::custom_command(int argc, char *argv[])
}
} else if (!strcmp(verb, "version-ext")) {
if (esc_id < MODAL_IO_OUTPUT_CHANNELS) {
if (esc_id < VOXL_ESC_OUTPUT_CHANNELS) {
PX4_INFO("Request extended version for ESC: %i", esc_id);
cmd.len = qc_esc_create_extended_version_request_packet(esc_id, cmd.buf, sizeof(cmd.buf));
cmd.response = true;
@@ -631,7 +629,7 @@ int ModalIo::custom_command(int argc, char *argv[])
}
} else if (!strcmp(verb, "tone")) {
if (esc_id < MODAL_IO_OUTPUT_CHANNELS) {
if (esc_id < VOXL_ESC_OUTPUT_CHANNELS) {
PX4_INFO("Request tone for ESC mask: %i", esc_id);
cmd.len = qc_esc_create_sound_packet(period, duration, power, esc_id, cmd.buf, sizeof(cmd.buf));
cmd.response = false;
@@ -660,12 +658,12 @@ int ModalIo::custom_command(int argc, char *argv[])
}
} else if (!strcmp(verb, "rpm")) {
if (esc_id < MODAL_IO_OUTPUT_CHANNELS) {
if (esc_id < VOXL_ESC_OUTPUT_CHANNELS) {
PX4_INFO("Request RPM for ESC ID: %i - RPM: %i", esc_id, rate);
int16_t rate_req[MODAL_IO_OUTPUT_CHANNELS] = {0, 0, 0, 0};
int16_t rate_req[VOXL_ESC_OUTPUT_CHANNELS] = {0, 0, 0, 0};
uint8_t id_fb = 0;
if (esc_id == 0xFF) {
if (esc_id == 0xFF) { //WARNING: this condition is not possible due to check 'if (esc_id < VOXL_ESC_OUTPUT_CHANNELS)'.
rate_req[0] = rate;
rate_req[1] = rate;
rate_req[2] = rate;
@@ -686,7 +684,8 @@ int ModalIo::custom_command(int argc, char *argv[])
0,
id_fb,
cmd.buf,
sizeof(cmd.buf));
sizeof(cmd.buf),
get_instance()->_extended_rpm);
cmd.response = true;
cmd.repeats = repeat_count;
@@ -705,12 +704,12 @@ int ModalIo::custom_command(int argc, char *argv[])
}
} else if (!strcmp(verb, "pwm")) {
if (esc_id < MODAL_IO_OUTPUT_CHANNELS) {
if (esc_id < VOXL_ESC_OUTPUT_CHANNELS) {
PX4_INFO("Request PWM for ESC ID: %i - PWM: %i", esc_id, rate);
int16_t rate_req[MODAL_IO_OUTPUT_CHANNELS] = {0, 0, 0, 0};
int16_t rate_req[VOXL_ESC_OUTPUT_CHANNELS] = {0, 0, 0, 0};
uint8_t id_fb = 0;
if (esc_id == 0xFF) {
if (esc_id == 0xFF) { //WARNING: this condition is not possible due to check 'if (esc_id < VOXL_ESC_OUTPUT_CHANNELS)'.
rate_req[0] = rate;
rate_req[1] = rate;
rate_req[2] = rate;
@@ -745,7 +744,7 @@ int ModalIo::custom_command(int argc, char *argv[])
return get_instance()->send_cmd_thread_safe(&cmd);
} else {
print_usage("Invalid ESC mask, use 1-15");
print_usage("Invalid ESC ID, use 0-3");
return 0;
}
}
@@ -753,7 +752,7 @@ int ModalIo::custom_command(int argc, char *argv[])
return print_usage("unknown command");
}
int ModalIo::update_params()
int VoxlEsc::update_params()
{
int ret = PX4_ERROR;
@@ -772,7 +771,7 @@ int ModalIo::update_params()
return ret;
}
void ModalIo::update_leds(vehicle_control_mode_s mode, led_control_s control)
void VoxlEsc::update_leds(vehicle_control_mode_s mode, led_control_s control)
{
int i = 0;
uint8_t led_mask = _led_rsc.led_mask;
@@ -872,12 +871,12 @@ void ModalIo::update_leds(vehicle_control_mode_s mode, led_control_s control)
}
}
for (i = 0; i < MODAL_IO_OUTPUT_CHANNELS; i++) {
for (i = 0; i < VOXL_ESC_OUTPUT_CHANNELS; i++) {
_esc_chans[i].led = led_mask;
}
}
void ModalIo::mix_turtle_mode(uint16_t outputs[MAX_ACTUATORS])
void VoxlEsc::mix_turtle_mode(uint16_t outputs[MAX_ACTUATORS])
{
bool use_pitch = true;
bool use_roll = true;
@@ -1052,10 +1051,13 @@ void ModalIo::mix_turtle_mode(uint16_t outputs[MAX_ACTUATORS])
}
/* OutputModuleInterface */
bool ModalIo::updateOutputs(bool stop_motors, uint16_t outputs[MAX_ACTUATORS],
bool VoxlEsc::updateOutputs(bool stop_motors, uint16_t outputs[MAX_ACTUATORS],
unsigned num_outputs, unsigned num_control_groups_updated)
{
if (num_outputs != MODAL_IO_OUTPUT_CHANNELS) {
//in Run() we call _mixing_output.update(), which calls MixingOutput::limitAndUpdateOutputs which calls _interface.updateOutputs (this function)
//So, if Run() is blocked by a custom command, this function will not be called until Run is running again
if (num_outputs != VOXL_ESC_OUTPUT_CHANNELS) {
return false;
}
@@ -1064,11 +1066,18 @@ bool ModalIo::updateOutputs(bool stop_motors, uint16_t outputs[MAX_ACTUATORS],
mix_turtle_mode(outputs);
}
for (int i = 0; i < MODAL_IO_OUTPUT_CHANNELS; i++) {
for (int i = 0; i < VOXL_ESC_OUTPUT_CHANNELS; i++) {
if (!_outputs_on || stop_motors) {
_esc_chans[i].rate_req = 0;
} else {
if (_extended_rpm) {
if (outputs[i] > VOXL_ESC_RPM_MAX_EXT) { outputs[i] = VOXL_ESC_RPM_MAX_EXT; }
} else {
if (outputs[i] > VOXL_ESC_RPM_MAX) { outputs[i] = VOXL_ESC_RPM_MAX; }
}
if (!_turtle_mode_en) {
_esc_chans[i].rate_req = outputs[i] * _output_map[i].direction;
@@ -1079,6 +1088,7 @@ bool ModalIo::updateOutputs(bool stop_motors, uint16_t outputs[MAX_ACTUATORS],
}
}
Command cmd;
cmd.len = qc_esc_create_rpm_packet4_fb(_esc_chans[0].rate_req,
_esc_chans[1].rate_req,
@@ -1090,24 +1100,24 @@ bool ModalIo::updateOutputs(bool stop_motors, uint16_t outputs[MAX_ACTUATORS],
_esc_chans[3].led,
_fb_idx,
cmd.buf,
sizeof(cmd.buf));
sizeof(cmd.buf),
_extended_rpm);
if (_uart_port->uart_write(cmd.buf, cmd.len) != cmd.len) {
PX4_ERR("Failed to send packet");
return false;
}
// round robin
_fb_idx = (_fb_idx + 1) % MODAL_IO_OUTPUT_CHANNELS;
// increment ESC id from which to request feedback in round robin order
_fb_idx = (_fb_idx + 1) % VOXL_ESC_OUTPUT_CHANNELS;
/*
* Here we parse the feedback response. Rarely the packet is mangled
* but this means we simply miss a feedback response and will come back
* around in roughly 8ms for another... so don't freak out and keep on
* trucking I say
* Here we read and parse response from ESCs. Since the latest command has just been sent out,
* the response packet we may read here is probabaly from previous iteration, but it is totally ok.
* uart_read is non-blocking and we will just parse whatever bytes came in up until this point
*/
int res = _uart_port->uart_read(_read_buf, sizeof(_read_buf));
if (res > 0) {
@@ -1135,13 +1145,29 @@ bool ModalIo::updateOutputs(bool stop_motors, uint16_t outputs[MAX_ACTUATORS],
_esc_status_pub.publish(_esc_status);
// If any extra external modal io data has been received then
// send it over as well
while (_voxl2_io_data_sub.updated()) {
buffer128_s io_data{};
_voxl2_io_data_sub.copy(&io_data);
// PX4_INFO("Got Modal IO data: %u bytes", io_data.len);
// PX4_INFO(" 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x",
// io_data.data[0], io_data.data[1], io_data.data[2], io_data.data[3],
// io_data.data[4], io_data.data[5], io_data.data[6], io_data.data[7]);
if (_uart_port->uart_write(io_data.data, io_data.len) != io_data.len) {
PX4_ERR("Failed to send modal io data to esc");
return false;
}
}
perf_count(_output_update_perf);
return true;
}
void ModalIo::Run()
void VoxlEsc::Run()
{
if (should_exit()) {
ScheduleClear();
@@ -1164,23 +1190,22 @@ void ModalIo::Run()
}
}
/*
for (int ii=0; ii<9; ii++)
{
const char * test_str = "Hello World!";
_uart_port_bridge->uart_write((char*)test_str,12);
px4_usleep(10000);
/* Get ESC FW version info */
if (_need_version_info) {
for (uint8_t esc_id = 0; esc_id < VOXL_ESC_OUTPUT_CHANNELS; ++esc_id) {
Command cmd;
cmd.len = qc_esc_create_version_request_packet(esc_id, cmd.buf, sizeof(cmd.buf));
if (_uart_port->uart_write(cmd.buf, cmd.len) == cmd.len) {
if (read_response(&_current_cmd) != 0) { PX4_ERR("Failed to parse version request response packet!"); }
} else {
PX4_ERR("Failed to send version request packet!");
}
}
*/
/*
uint8_t echo_buf[16];
int bytes_read = _uart_port_bridge->uart_read(echo_buf,sizeof(echo_buf));
if (bytes_read > 0)
_uart_port_bridge->uart_write(echo_buf,bytes_read);
*/
}
_mixing_output.update();
_mixing_output.update(); //calls MixingOutput::limitAndUpdateOutputs which calls updateOutputs in this module
/* update output status if armed */
_outputs_on = _mixing_output.armed().armed;
@@ -1222,16 +1247,16 @@ void ModalIo::Run()
if (!_outputs_on) {
float setpoint = MODAL_IO_MODE_DISABLED_SETPOINT;
float setpoint = VOXL_ESC_MODE_DISABLED_SETPOINT;
if (_parameters.mode == MODAL_IO_MODE_TURTLE_AUX1) {
if (_parameters.mode == VOXL_ESC_MODE_TURTLE_AUX1) {
setpoint = _manual_control_setpoint.aux1;
} else if (_parameters.mode == MODAL_IO_MODE_TURTLE_AUX2) {
} else if (_parameters.mode == VOXL_ESC_MODE_TURTLE_AUX2) {
setpoint = _manual_control_setpoint.aux2;
}
if (setpoint > MODAL_IO_MODE_THRESHOLD) {
if (setpoint > VOXL_ESC_MODE_THRESHOLD) {
_turtle_mode_en = true;
} else {
@@ -1240,68 +1265,6 @@ void ModalIo::Run()
}
}
if (_parameters.mode == MODAL_IO_MODE_UART_BRIDGE) {
if (!_uart_port_bridge->is_open()) {
if (_uart_port_bridge->uart_open(MODAL_IO_VOXL_BRIDGE_PORT, 230400) == PX4_OK) {
PX4_INFO("Opened UART ESC Bridge device");
} else {
PX4_ERR("Failed openening UART ESC Bridge device");
return;
}
}
//uart passthrough test code
//run 9 times because i just don't know how to change update rate of the module from 10hz to 100hz..
for (int ii = 0; ii < 9; ii++) {
uint8_t uart_buf[128];
int bytes_read = _uart_port_bridge->uart_read(uart_buf, sizeof(uart_buf));
if (bytes_read > 0) {
_uart_port->uart_write(uart_buf, bytes_read);
for (int i = 0; i < bytes_read; i++) {
int16_t ret = qc_esc_packet_process_char(uart_buf[i], &_uart_bridge_packet);
if (ret > 0) {
//PX4_INFO("got packet of length %i",ret);
uint8_t packet_type = qc_esc_packet_get_type(&_uart_bridge_packet);
//uint8_t packet_size = qc_esc_packet_get_size(&_uart_bridge_packet);
//if we received a command for ESC to reset, most likely firmware update is coming, switch to bootloader baud rate
if (packet_type == ESC_PACKET_TYPE_RESET_CMD) {
int bootloader_baud_rate = 230400;
if (_uart_port->uart_get_baud() != bootloader_baud_rate) {
px4_usleep(5000);
_uart_port->uart_set_baud(bootloader_baud_rate);
}
} else {
if (_uart_port->uart_get_baud() != _parameters.baud_rate) {
px4_usleep(5000);
_uart_port->uart_set_baud(_parameters.baud_rate); //restore normal baud rate
}
}
}
}
}
bytes_read = _uart_port->uart_read(uart_buf, sizeof(uart_buf));
if (bytes_read > 0) {
_uart_port_bridge->uart_write(uart_buf, bytes_read);
}
px4_usleep(10000);
}
}
} else {
if (_uart_port_bridge->is_open()) {
PX4_INFO("Closed UART ESC Bridge device");
_uart_port_bridge->uart_close();
}
}
if (!_outputs_on) {
@@ -1364,7 +1327,7 @@ void ModalIo::Run()
}
int ModalIo::print_usage(const char *reason)
int VoxlEsc::print_usage(const char *reason)
{
if (reason) {
PX4_WARN("%s\n", reason);
@@ -1384,7 +1347,7 @@ $ todo
)DESCR_STR");
PRINT_MODULE_USAGE_NAME("modal_io", "driver");
PRINT_MODULE_USAGE_NAME("voxl_esc", "driver");
PRINT_MODULE_USAGE_COMMAND_DESCR("start", "Start the task");
PRINT_MODULE_USAGE_COMMAND_DESCR("reset", "Send reset request to ESC");
@@ -1422,7 +1385,7 @@ $ todo
return 0;
}
int ModalIo::print_status()
int VoxlEsc::print_status()
{
PX4_INFO("Max update rate: %i Hz", _current_update_rate);
PX4_INFO("Outputs on: %s", _outputs_on ? "yes" : "no");
@@ -1431,31 +1394,31 @@ int ModalIo::print_status()
PX4_INFO("");
PX4_INFO("Params: MODAL_IO_CONFIG: %" PRId32, _parameters.config);
PX4_INFO("Params: MODAL_IO_BAUD: %" PRId32, _parameters.baud_rate);
PX4_INFO("Params: VOXL_ESC_CONFIG: %" PRId32, _parameters.config);
PX4_INFO("Params: VOXL_ESC_BAUD: %" PRId32, _parameters.baud_rate);
PX4_INFO("Params: MODAL_IO_FUNC1: %" PRId32, _parameters.function_map[0]);
PX4_INFO("Params: MODAL_IO_FUNC2: %" PRId32, _parameters.function_map[1]);
PX4_INFO("Params: MODAL_IO_FUNC3: %" PRId32, _parameters.function_map[2]);
PX4_INFO("Params: MODAL_IO_FUNC4: %" PRId32, _parameters.function_map[3]);
PX4_INFO("Params: VOXL_ESC_FUNC1: %" PRId32, _parameters.function_map[0]);
PX4_INFO("Params: VOXL_ESC_FUNC2: %" PRId32, _parameters.function_map[1]);
PX4_INFO("Params: VOXL_ESC_FUNC3: %" PRId32, _parameters.function_map[2]);
PX4_INFO("Params: VOXL_ESC_FUNC4: %" PRId32, _parameters.function_map[3]);
PX4_INFO("Params: MODAL_IO_SDIR1: %" PRId32, _parameters.direction_map[0]);
PX4_INFO("Params: MODAL_IO_SDIR2: %" PRId32, _parameters.direction_map[1]);
PX4_INFO("Params: MODAL_IO_SDIR3: %" PRId32, _parameters.direction_map[2]);
PX4_INFO("Params: MODAL_IO_SDIR4: %" PRId32, _parameters.direction_map[3]);
PX4_INFO("Params: VOXL_ESC_SDIR1: %" PRId32, _parameters.direction_map[0]);
PX4_INFO("Params: VOXL_ESC_SDIR2: %" PRId32, _parameters.direction_map[1]);
PX4_INFO("Params: VOXL_ESC_SDIR3: %" PRId32, _parameters.direction_map[2]);
PX4_INFO("Params: VOXL_ESC_SDIR4: %" PRId32, _parameters.direction_map[3]);
PX4_INFO("Params: MODAL_IO_RPM_MIN: %" PRId32, _parameters.rpm_min);
PX4_INFO("Params: MODAL_IO_RPM_MAX: %" PRId32, _parameters.rpm_max);
PX4_INFO("Params: VOXL_ESC_RPM_MIN: %" PRId32, _parameters.rpm_min);
PX4_INFO("Params: VOXL_ESC_RPM_MAX: %" PRId32, _parameters.rpm_max);
PX4_INFO("");
for( int i = 0; i < MODAL_IO_OUTPUT_CHANNELS; i++){
for( int i = 0; i < VOXL_ESC_OUTPUT_CHANNELS; i++){
PX4_INFO("-- ID: %i", i);
PX4_INFO(" Motor: %i", _output_map[i].number);
PX4_INFO(" Direction: %i", _output_map[i].direction);
PX4_INFO(" State: %i", _esc_chans[i].state);
PX4_INFO(" Requested: %i RPM", _esc_chans[i].rate_req);
PX4_INFO(" Measured: %i RPM", _esc_chans[i].rate_meas);
PX4_INFO(" Requested: %" PRIi32 " RPM", _esc_chans[i].rate_req);
PX4_INFO(" Measured: %" PRIi32 " RPM", _esc_chans[i].rate_meas);
PX4_INFO(" Command Counter: %i", _esc_chans[i].cmd_counter);
PX4_INFO(" Voltage: %f VDC", (double)_esc_chans[i].voltage);
PX4_INFO("");
@@ -1469,9 +1432,9 @@ int ModalIo::print_status()
return 0;
}
extern "C" __EXPORT int modal_io_main(int argc, char *argv[]);
extern "C" __EXPORT int voxl_esc_main(int argc, char *argv[]);
int modal_io_main(int argc, char *argv[])
int voxl_esc_main(int argc, char *argv[])
{
return ModalIo::main(argc, argv);
return VoxlEsc::main(argc, argv);
}
@@ -41,23 +41,26 @@
#include <px4_log.h>
#include <px4_platform_common/module.h>
#include <battery/battery.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 <uORB/topics/buffer128.h>
#include "modal_io_serial.hpp"
#include "voxl_esc_serial.hpp"
#include "qc_esc_packet.h"
#include "qc_esc_packet_types.h"
class ModalIo : public ModuleBase<ModalIo>, public OutputModuleInterface
class VoxlEsc : public ModuleBase<VoxlEsc>, public OutputModuleInterface
{
public:
ModalIo();
virtual ~ModalIo();
VoxlEsc();
virtual ~VoxlEsc();
/** @see ModuleBase */
static int task_spawn(int argc, char *argv[]);
@@ -100,56 +103,61 @@ public:
int send_cmd_thread_safe(Command *cmd);
private:
static constexpr uint32_t MODAL_IO_UART_CONFIG = 1;
static constexpr uint32_t MODAL_IO_DEFAULT_BAUD = 250000;
static constexpr uint16_t MODAL_IO_OUTPUT_CHANNELS = 4;
static constexpr uint16_t MODAL_IO_OUTPUT_DISABLED = 0;
static constexpr uint32_t VOXL_ESC_UART_CONFIG = 1;
static constexpr uint32_t VOXL_ESC_DEFAULT_BAUD = 250000;
static constexpr uint16_t VOXL_ESC_OUTPUT_CHANNELS = 4;
static constexpr uint16_t VOXL_ESC_OUTPUT_DISABLED = 0;
static constexpr uint32_t MODAL_IO_WRITE_WAIT_US = 200;
static constexpr uint32_t MODAL_IO_DISCONNECT_TIMEOUT_US = 500000;
static constexpr uint32_t VOXL_ESC_WRITE_WAIT_US = 200;
static constexpr uint32_t VOXL_ESC_DISCONNECT_TIMEOUT_US = 500000;
static constexpr uint16_t DISARMED_VALUE = 0;
static constexpr uint16_t MODAL_IO_PWM_MIN = 0;
static constexpr uint16_t MODAL_IO_PWM_MAX = 800;
static constexpr uint16_t MODAL_IO_DEFAULT_RPM_MIN = 5000;
static constexpr uint16_t MODAL_IO_DEFAULT_RPM_MAX = 17000;
static constexpr uint16_t VOXL_ESC_PWM_MIN = 0;
static constexpr uint16_t VOXL_ESC_PWM_MAX = 800;
static constexpr uint16_t VOXL_ESC_DEFAULT_RPM_MIN = 5000;
static constexpr uint16_t VOXL_ESC_DEFAULT_RPM_MAX = 17000;
static constexpr float MODAL_IO_MODE_DISABLED_SETPOINT = -0.1f;
static constexpr float MODAL_IO_MODE_THRESHOLD = 0.0f;
static constexpr float VOXL_ESC_MODE_DISABLED_SETPOINT = -0.1f;
static constexpr float VOXL_ESC_MODE_THRESHOLD = 0.0f;
static constexpr uint32_t MODAL_IO_MODE = 0;
static constexpr uint32_t MODAL_IO_MODE_TURTLE_AUX1 = 1;
static constexpr uint32_t MODAL_IO_MODE_TURTLE_AUX2 = 2;
static constexpr uint32_t MODAL_IO_MODE_UART_BRIDGE = 3;
static constexpr uint32_t VOXL_ESC_MODE = 0;
static constexpr uint32_t VOXL_ESC_MODE_TURTLE_AUX1 = 1;
static constexpr uint32_t VOXL_ESC_MODE_TURTLE_AUX2 = 2;
//static constexpr uint16_t max_pwm(uint16_t pwm) { return math::min(pwm, MODAL_IO_PWM_MAX); }
//static constexpr uint16_t max_rpm(uint16_t rpm) { return math::min(rpm, MODAL_IO_RPM_MAX); }
static constexpr uint16_t VOXL_ESC_EXT_RPM = 39;
static constexpr uint16_t VOXL_ESC_RPM_MAX = INT16_MAX -
1; // 32K, Limit max standard range RPM to prevent overflow (rpm packet packing function accepts int32_t)
static constexpr uint16_t VOXL_ESC_RPM_MAX_EXT = UINT16_MAX -
5; // 65K, Limit max extended range RPM to prevent overflow (rpm packet packing function accepts int32_t)
ModalIoSerial *_uart_port;
ModalIoSerial *_uart_port_bridge;
//static constexpr uint16_t max_pwm(uint16_t pwm) { return math::min(pwm, VOXL_ESC_PWM_MAX); }
//static constexpr uint16_t max_rpm(uint16_t rpm) { return math::min(rpm, VOXL_ESC_RPM_MAX); }
VoxlEscSerial *_uart_port;
typedef struct {
int32_t config{MODAL_IO_UART_CONFIG};
int32_t mode{MODAL_IO_MODE};
int32_t config{VOXL_ESC_UART_CONFIG};
int32_t mode{VOXL_ESC_MODE};
int32_t turtle_motor_expo{35};
int32_t turtle_motor_deadband{20};
int32_t turtle_motor_percent{90};
float turtle_stick_minf{0.15f};
float turtle_cosphi{0.99f};
int32_t baud_rate{MODAL_IO_DEFAULT_BAUD};
int32_t rpm_min{MODAL_IO_DEFAULT_RPM_MIN};
int32_t rpm_max{MODAL_IO_DEFAULT_RPM_MAX};
int32_t function_map[MODAL_IO_OUTPUT_CHANNELS] {0, 0, 0, 0};
int32_t motor_map[MODAL_IO_OUTPUT_CHANNELS] {1, 2, 3, 4};
int32_t direction_map[MODAL_IO_OUTPUT_CHANNELS] {1, 1, 1, 1};
int32_t baud_rate{VOXL_ESC_DEFAULT_BAUD};
int32_t rpm_min{VOXL_ESC_DEFAULT_RPM_MIN};
int32_t rpm_max{VOXL_ESC_DEFAULT_RPM_MAX};
int32_t function_map[VOXL_ESC_OUTPUT_CHANNELS] {0, 0, 0, 0};
int32_t motor_map[VOXL_ESC_OUTPUT_CHANNELS] {1, 2, 3, 4};
int32_t direction_map[VOXL_ESC_OUTPUT_CHANNELS] {1, 1, 1, 1};
int32_t verbose_logging{0};
} modal_io_params_t;
int32_t publish_battery_status{0};
} voxl_esc_params_t;
struct EscChan {
int16_t rate_req;
int32_t rate_req;
uint8_t state;
uint16_t rate_meas;
uint32_t rate_meas;
uint8_t power_applied;
uint8_t led;
uint8_t cmd_counter;
@@ -167,14 +175,14 @@ private:
typedef struct {
led_control_s control{};
vehicle_control_mode_s mode{};
uint8_t led_mask;// TODO led_mask[MODAL_IO_OUTPUT_CHANNELS];
uint8_t led_mask;// TODO led_mask[VOXL_ESC_OUTPUT_CHANNELS];
bool breath_en;
uint8_t breath_counter;
bool test;
} led_rsc_t;
ch_assign_t _output_map[MODAL_IO_OUTPUT_CHANNELS] {{1, 1}, {2, 1}, {3, 1}, {4, 1}};
MixingOutput _mixing_output{"MODAL_IO", MODAL_IO_OUTPUT_CHANNELS, *this, MixingOutput::SchedulingPolicy::Auto, false, false};
ch_assign_t _output_map[VOXL_ESC_OUTPUT_CHANNELS] {{1, 1}, {2, 1}, {3, 1}, {4, 1}};
MixingOutput _mixing_output;
perf_counter_t _cycle_perf;
perf_counter_t _output_update_perf;
@@ -188,13 +196,19 @@ private:
uORB::Subscription _parameter_update_sub{ORB_ID(parameter_update)};
uORB::Subscription _actuator_test_sub{ORB_ID(actuator_test)};
uORB::Subscription _led_update_sub{ORB_ID(led_control)};
uORB::Subscription _voxl2_io_data_sub{ORB_ID(voxl2_io_data)};
uORB::Publication<actuator_outputs_s> _outputs_debug_pub{ORB_ID(actuator_outputs_debug)};
uORB::Publication<esc_status_s> _esc_status_pub{ORB_ID(esc_status)};
modal_io_params_t _parameters;
bool _extended_rpm{false};
bool _need_version_info{true};
QC_ESC_VERSION_INFO _version_info[4];
bool check_versions_updated();
voxl_esc_params_t _parameters;
int update_params();
int load_params(modal_io_params_t *params, ch_assign_t *map);
int load_params(voxl_esc_params_t *params, ch_assign_t *map);
bool _turtle_mode_en{false};
int32_t _rpm_turtle_min{0};
@@ -205,11 +219,10 @@ private:
Command _current_cmd;
px4::atomic<Command *> _pending_cmd{nullptr};
EscChan _esc_chans[MODAL_IO_OUTPUT_CHANNELS];
EscChan _esc_chans[VOXL_ESC_OUTPUT_CHANNELS];
Command _esc_cmd;
esc_status_s _esc_status;
EscPacket _fb_packet;
EscPacket _uart_bridge_packet;
led_rsc_t _led_rsc;
int _fb_idx;
@@ -219,6 +232,10 @@ private:
static const uint8_t READ_BUF_SIZE = 128;
uint8_t _read_buf[READ_BUF_SIZE];
Battery _battery;
static constexpr unsigned _battery_report_interval{100_ms};
hrt_abstime _last_battery_report_time;
void update_leds(vehicle_control_mode_s mode, led_control_s control);
int read_response(Command *out_cmd);
@@ -38,23 +38,23 @@
*
* @reboot_required true
*
* @group MODAL IO
* @group VOXL ESC
* @value 0 - Disabled
* @value 1 - VOXL ESC
* @min 0
* @max 1
*/
PARAM_DEFINE_INT32(MODAL_IO_CONFIG, 0);
PARAM_DEFINE_INT32(VOXL_ESC_CONFIG, 0);
/**
* UART ESC baud rate
*
* Default rate is 250Kbps, which is used in off-the-shelf MoadalAI ESC products.
*
* @group MODAL IO
* @group VOXL ESC
* @unit bit/s
*/
PARAM_DEFINE_INT32(MODAL_IO_BAUD, 250000);
PARAM_DEFINE_INT32(VOXL_ESC_BAUD, 250000);
/**
* Motor mappings for ModalAI ESC
@@ -68,33 +68,33 @@ PARAM_DEFINE_INT32(MODAL_IO_BAUD, 250000);
// The following are auto generated params from control allocator pattern, put here for reference
// Default ESC1 to motor2
//PARAM_DEFINE_INT32(MODAL_IO_FUNC1, 102);
//PARAM_DEFINE_INT32(VOXL_ESC_FUNC1, 102);
//PARAM_DEFINE_INT32(MODAL_IO_FUNC2, 103);
//PARAM_DEFINE_INT32(VOXL_ESC_FUNC2, 103);
//PARAM_DEFINE_INT32(MODAL_IO_FUNC3, 101);
//PARAM_DEFINE_INT32(VOXL_ESC_FUNC3, 101);
//PARAM_DEFINE_INT32(MODAL_IO_FUNC4, 104);
//PARAM_DEFINE_INT32(VOXL_ESC_FUNC4, 104);
/**
* UART ESC RPM Min
*
* Minimum RPM for ESC
*
* @group MODAL IO
* @group VOXL ESC
* @unit rpm
*/
PARAM_DEFINE_INT32(MODAL_IO_RPM_MIN, 5500);
PARAM_DEFINE_INT32(VOXL_ESC_RPM_MIN, 5500);
/**
* UART ESC RPM Max
*
* Maximum RPM for ESC
*
* @group MODAL IO
* @group VOXL ESC
* @unit rpm
*/
PARAM_DEFINE_INT32(MODAL_IO_RPM_MAX, 15000);
PARAM_DEFINE_INT32(VOXL_ESC_RPM_MAX, 15000);
/**
* UART ESC Mode
@@ -103,7 +103,7 @@ PARAM_DEFINE_INT32(MODAL_IO_RPM_MAX, 15000);
*
* @reboot_required true
*
* @group MODAL IO
* @group VOXL ESC
* @value 0 - None
* @value 1 - Turtle Mode enabled via AUX1
* @value 2 - Turtle Mode enabled via AUX2
@@ -111,108 +111,124 @@ PARAM_DEFINE_INT32(MODAL_IO_RPM_MAX, 15000);
* @min 0
* @max 2
*/
PARAM_DEFINE_INT32(MODAL_IO_MODE, 0);
PARAM_DEFINE_INT32(VOXL_ESC_MODE, 0);
/**
* UART ESC ID 1 Spin Direction Flag
*
* @group MODAL IO
* @group VOXL ESC
* @value 0 - Default
* @value 1 - Reverse
*/
PARAM_DEFINE_INT32(MODAL_IO_SDIR1, 0);
PARAM_DEFINE_INT32(VOXL_ESC_SDIR1, 0);
/**
* UART ESC ID 2 Spin Direction Flag
*
* @group MODAL IO
* @group VOXL ESC
* @value 0 - Default
* @value 1 - Reverse
*/
PARAM_DEFINE_INT32(MODAL_IO_SDIR2, 0);
PARAM_DEFINE_INT32(VOXL_ESC_SDIR2, 0);
/**
* UART ESC ID 3 Spin Direction Flag
*
* @group MODAL IO
* @group VOXL ESC
* @value 0 - Default
* @value 1 - Reverse
*/
PARAM_DEFINE_INT32(MODAL_IO_SDIR3, 0);
PARAM_DEFINE_INT32(VOXL_ESC_SDIR3, 0);
/**
* UART ESC ID 4 Spin Direction Flag
*
* @group MODAL IO
* @group VOXL ESC
* @value 0 - Default
* @value 1 - Reverse
*/
PARAM_DEFINE_INT32(MODAL_IO_SDIR4, 0);
PARAM_DEFINE_INT32(VOXL_ESC_SDIR4, 0);
/**
* UART ESC Turtle Mode Crash Flip Motor Percent
*
* @group MODAL IO
* @group VOXL ESC
* @min 1
* @max 100
* @decimal 10
* @increment 1
*/
PARAM_DEFINE_INT32(MODAL_IO_T_PERC, 90);
PARAM_DEFINE_INT32(VOXL_ESC_T_PERC, 90);
/**
* UART ESC Turtle Mode Crash Flip Motor Deadband
*
* @group MODAL IO
* @group VOXL ESC
* @min 0
* @max 100
* @decimal 10
* @increment 1
*/
PARAM_DEFINE_INT32(MODAL_IO_T_DEAD, 20);
PARAM_DEFINE_INT32(VOXL_ESC_T_DEAD, 20);
/**
* UART ESC Turtle Mode Crash Flip Motor STICK_MINF
*
* @group MODAL IO
* @group VOXL ESC
* @min 0.0
* @max 100.0
* @decimal 10
* @increment 1.0
*/
PARAM_DEFINE_FLOAT(MODAL_IO_T_MINF, 0.15);
PARAM_DEFINE_FLOAT(VOXL_ESC_T_MINF, 0.15);
/**
* UART ESC Turtle Mode Crash Flip Motor expo
*
* @group MODAL IO
* @group VOXL ESC
* @min 0
* @max 100
* @decimal 10
* @increment 1
*/
PARAM_DEFINE_INT32(MODAL_IO_T_EXPO, 35);
PARAM_DEFINE_INT32(VOXL_ESC_T_EXPO, 35);
/**
* UART ESC Turtle Mode Cosphi
*
* @group MODAL IO
* @group VOXL ESC
* @min 0.000
* @max 1.000
* @decimal 10
* @increment 0.001
*/
PARAM_DEFINE_FLOAT(MODAL_IO_T_COSP, 0.990);
PARAM_DEFINE_FLOAT(VOXL_ESC_T_COSP, 0.990);
/**
* UART ESC verbose logging
*
* @reboot_required true
*
* @group MODAL IO
* @group VOXL ESC
* @value 0 - Disabled
* @value 1 - Enabled
* @min 0
* @max 1
*/
PARAM_DEFINE_INT32(MODAL_IO_VLOG, 0);
PARAM_DEFINE_INT32(VOXL_ESC_VLOG, 0);
/**
* UART ESC Enable publishing of battery status
*
* Only applicable to ESCs that report total battery voltage and current
*
* @reboot_required true
*
* @group VOXL ESC
* @value 0 - Disabled
* @value 1 - Enabled
* @min 0
* @max 1
*/
PARAM_DEFINE_INT32(VOXL_ESC_PUB_BST, 1);
@@ -32,20 +32,20 @@
****************************************************************************/
#include "string.h"
#include "modal_io_serial.hpp"
#include "voxl_esc_serial.hpp"
ModalIoSerial::ModalIoSerial()
VoxlEscSerial::VoxlEscSerial()
{
}
ModalIoSerial::~ModalIoSerial()
VoxlEscSerial::~VoxlEscSerial()
{
if (_uart_fd >= 0) {
uart_close();
}
}
int ModalIoSerial::uart_open(const char *dev, speed_t speed)
int VoxlEscSerial::uart_open(const char *dev, speed_t speed)
{
if (_uart_fd >= 0) {
PX4_ERR("Port in use: %s (%i)", dev, errno);
@@ -110,7 +110,7 @@ int ModalIoSerial::uart_open(const char *dev, speed_t speed)
return 0;
}
int ModalIoSerial::uart_set_baud(speed_t speed)
int VoxlEscSerial::uart_set_baud(speed_t speed)
{
#ifndef __PX4_QURT
@@ -134,7 +134,7 @@ int ModalIoSerial::uart_set_baud(speed_t speed)
return -1;
}
int ModalIoSerial::uart_close()
int VoxlEscSerial::uart_close()
{
#ifndef __PX4_QURT
@@ -158,7 +158,7 @@ int ModalIoSerial::uart_close()
return 0;
}
int ModalIoSerial::uart_write(FAR void *buf, size_t len)
int VoxlEscSerial::uart_write(FAR void *buf, size_t len)
{
if (_uart_fd < 0 || buf == NULL) {
PX4_ERR("invalid state for writing or buffer");
@@ -172,7 +172,7 @@ int ModalIoSerial::uart_write(FAR void *buf, size_t len)
#endif
}
int ModalIoSerial::uart_read(FAR void *buf, size_t len)
int VoxlEscSerial::uart_read(FAR void *buf, size_t len)
{
if (_uart_fd < 0 || buf == NULL) {
PX4_ERR("invalid state for reading or buffer");
@@ -43,11 +43,11 @@
#define FAR
#endif
class ModalIoSerial
class VoxlEscSerial
{
public:
ModalIoSerial();
virtual ~ModalIoSerial();
VoxlEscSerial();
virtual ~VoxlEscSerial();
int uart_open(const char *dev, speed_t speed);
int uart_set_baud(speed_t speed);