split drivers/rc_input per protocol

This commit is contained in:
Daniel Agar
2022-12-23 18:49:16 -05:00
parent bb591c6c29
commit ad7b74847e
156 changed files with 3780 additions and 2967 deletions
+1 -1
View File
@@ -58,7 +58,7 @@
#include <lib/mixer_module/mixer_module.hpp>
#include <lib/parameters/param.h>
#include <lib/perf/perf_counter.h>
#include <lib/rc/dsm.h>
#include <lib/rc/dsm/dsm.h>
#include <lib/systemlib/mavlink_log.h>
#include <lib/button/ButtonPublisher.hpp>
+9 -3
View File
@@ -1,9 +1,15 @@
menu "RC"
menuconfig COMMON_RC
bool "Common RC"
bool "Common RC drivers"
default n
select DRIVERS_RC_CRSF_RC
select DRIVERS_RC_CRSF
select DRIVERS_RC_SPEKTRUM_DSM
select DRIVERS_RC_GHST_RC
#select DRIVERS_RC_PPM_IN
select DRIVERS_RC_SBUS
#select DRIVERS_RC_ST24_RC
#select DRIVERS_RC_SUMD_RC
---help---
Enable default set of magnetometer drivers
Enable default set of RC drivers
rsource "*/Kconfig"
endmenu
@@ -31,8 +31,8 @@
#
############################################################################
px4_add_module(
MODULE drivers__rc__crsf_rc
MAIN crsf_rc
MODULE drivers__rc__crsf
MAIN crsf
COMPILE_FLAGS
SRCS
CrsfRc.cpp
@@ -47,5 +47,5 @@ px4_add_module(
MODULE_CONFIG
module.yaml
DEPENDS
rc
px4_work_queue
)
@@ -500,12 +500,12 @@ int CrsfRc::print_status()
perf_print_counter(_cycle_interval_perf);
perf_print_counter(_publish_interval_perf);
PX4_INFO("Disposed bytes: %li", _packet_parser_statistics.disposed_bytes);
PX4_INFO("Valid known packet CRCs: %li", _packet_parser_statistics.crcs_valid_known_packets);
PX4_INFO("Valid unknown packet CRCs: %li", _packet_parser_statistics.crcs_valid_unknown_packets);
PX4_INFO("Invalid CRCs: %li", _packet_parser_statistics.crcs_invalid);
PX4_INFO("Invalid known packet sizes: %li", _packet_parser_statistics.invalid_known_packet_sizes);
PX4_INFO("Invalid unknown packet sizes: %li", _packet_parser_statistics.invalid_unknown_packet_sizes);
PX4_INFO("Disposed bytes: %" PRIu32, _packet_parser_statistics.disposed_bytes);
PX4_INFO("Valid known packet CRCs: %" PRIu32, _packet_parser_statistics.crcs_valid_known_packets);
PX4_INFO("Valid unknown packet CRCs: %" PRIu32, _packet_parser_statistics.crcs_valid_unknown_packets);
PX4_INFO("Invalid CRCs: %" PRIu32, _packet_parser_statistics.crcs_invalid);
PX4_INFO("Invalid known packet sizes: %" PRIu32, _packet_parser_statistics.invalid_known_packet_sizes);
PX4_INFO("Invalid unknown packet sizes: %" PRIu32, _packet_parser_statistics.invalid_unknown_packet_sizes);
return 0;
}
@@ -528,7 +528,7 @@ This module parses the CRSF RC uplink protocol and generates CRSF downlink telem
)DESCR_STR");
PRINT_MODULE_USAGE_NAME("crsf_rc", "driver");
PRINT_MODULE_USAGE_NAME("crsf", "driver");
PRINT_MODULE_USAGE_COMMAND("start");
PRINT_MODULE_USAGE_PARAM_STRING('d', "/dev/ttyS3", "<file:dev>", "RC device", true);
@@ -537,7 +537,7 @@ This module parses the CRSF RC uplink protocol and generates CRSF downlink telem
return 0;
}
extern "C" __EXPORT int crsf_rc_main(int argc, char *argv[])
extern "C" __EXPORT int crsf_main(int argc, char *argv[])
{
return CrsfRc::main(argc, argv);
}
@@ -1,5 +1,5 @@
menuconfig DRIVERS_RC_CRSF_RC
bool "crsf_rc"
menuconfig DRIVERS_RC_CRSF
bool "crsf"
default n
---help---
Enable support for crsf rc
@@ -1,6 +1,6 @@
module_name: CRSF RC Input Driver
serial_config:
- command: "crsf_rc start -d ${SERIAL_DEV}"
- command: "crsf start -d ${SERIAL_DEV}"
port_config_param:
name: RC_CRSF_PRT_CFG
group: Serial
+51
View File
@@ -0,0 +1,51 @@
############################################################################
#
# Copyright (c) 2015-2022 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.
#
############################################################################
if(PX4_TESTING AND (${PX4_PLATFORM} MATCHES "posix"))
add_subdirectory(test)
endif()
px4_add_module(
MODULE drivers__rc__ghst
MAIN rc_ghst
COMPILE_FLAGS
SRCS
RCInput.cpp
RCInput.hpp
ghst_parser.cpp
ghst_parser.hpp
MODULE_CONFIG
module.yaml
DEPENDS
px4_work_queue
)
+5
View File
@@ -0,0 +1,5 @@
menuconfig DRIVERS_RC_GHST_RC
bool "rc_ghst"
default n
---help---
Enable support for rc_ghst
+427
View File
@@ -0,0 +1,427 @@
/****************************************************************************
*
* Copyright (c) 2012-2022 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 "RCInput.hpp"
#include <termios.h>
using namespace time_literals;
RcGhst::RcGhst(const char *device) :
ModuleParams(nullptr),
ScheduledWorkItem(MODULE_NAME, px4::serial_port_to_wq(device))
{
// initialize raw_rc values and count
for (unsigned i = 0; i < input_rc_s::RC_INPUT_MAX_CHANNELS; i++) {
_raw_rc_values[i] = UINT16_MAX;
}
if (device) {
strncpy(_device, device, sizeof(_device) - 1);
_device[sizeof(_device) - 1] = '\0';
}
}
RcGhst::~RcGhst()
{
perf_free(_cycle_perf);
perf_free(_publish_interval_perf);
}
int RcGhst::init()
{
_rcs_fd = ::open(_device, O_RDWR | O_NONBLOCK);
if (_rcs_fd >= 0) {
ghst_config(_rcs_fd);
}
if (_rcs_fd < 0) {
return -errno;
}
if (board_rc_swap_rxtx(_device)) {
#if defined(TIOCSSWAP)
ioctl(_rcs_fd, TIOCSSWAP, SER_SWAP_ENABLED);
#endif // TIOCSSWAP
}
rc_io_invert(false);
return 0;
}
int RcGhst::task_spawn(int argc, char *argv[])
{
bool error_flag = false;
int myoptind = 1;
int ch;
const char *myoptarg = nullptr;
const char *device_name = nullptr;
#if defined(RC_SERIAL_PORT)
device_name = RC_SERIAL_PORT;
#endif // RC_SERIAL_PORT
while ((ch = px4_getopt(argc, argv, "d:", &myoptind, &myoptarg)) != EOF) {
switch (ch) {
case 'd':
device_name = myoptarg;
break;
case '?':
error_flag = true;
break;
default:
PX4_WARN("unrecognized flag");
error_flag = true;
break;
}
}
if (error_flag) {
return -1;
}
if (device_name && (access(device_name, R_OK | W_OK) == 0)) {
RcGhst *instance = new RcGhst(device_name);
if (instance == nullptr) {
PX4_ERR("alloc failed");
return PX4_ERROR;
}
_object.store(instance);
_task_id = task_id_is_work_queue;
instance->ScheduleOnInterval(_current_update_interval);
return PX4_OK;
} else {
if (device_name) {
PX4_ERR("invalid device (-d) %s", device_name);
} else {
PX4_INFO("valid device required");
}
}
return PX4_ERROR;
}
void RcGhst::fill_rc_in(uint16_t raw_rc_count_local,
uint16_t raw_rc_values_local[input_rc_s::RC_INPUT_MAX_CHANNELS],
hrt_abstime now, bool frame_drop, bool failsafe,
unsigned frame_drops, int rssi = -1)
{
// fill rc_in struct for publishing
_rc_in.channel_count = raw_rc_count_local;
if (_rc_in.channel_count > input_rc_s::RC_INPUT_MAX_CHANNELS) {
_rc_in.channel_count = input_rc_s::RC_INPUT_MAX_CHANNELS;
}
unsigned valid_chans = 0;
for (unsigned i = 0; i < _rc_in.channel_count; i++) {
_rc_in.values[i] = raw_rc_values_local[i];
if (raw_rc_values_local[i] != UINT16_MAX) {
valid_chans++;
}
// once filled, reset values back to default
_raw_rc_values[i] = UINT16_MAX;
}
_rc_in.timestamp = now;
_rc_in.timestamp_last_signal = _rc_in.timestamp;
_rc_in.rc_ppm_frame_length = 0;
if (valid_chans == 0) {
_rc_in.rssi = 0;
}
_rc_in.rc_failsafe = failsafe;
_rc_in.rc_lost = (valid_chans == 0);
_rc_in.rc_lost_frame_count = frame_drops;
_rc_in.rc_total_frame_count = 0;
}
void RcGhst::rc_io_invert(bool invert)
{
// First check if the board provides a board-specific inversion method (e.g. via GPIO),
// and if not use an IOCTL
if (!board_rc_invert_input(_device, invert)) {
#if defined(TIOCSINVERT)
if (invert) {
ioctl(_rcs_fd, TIOCSINVERT, SER_INVERT_ENABLED_RX | SER_INVERT_ENABLED_TX);
} else {
ioctl(_rcs_fd, TIOCSINVERT, 0);
}
#endif // TIOCSINVERT
}
}
void RcGhst::Run()
{
if (should_exit()) {
exit_and_cleanup();
return;
}
if (!_initialized) {
if (init() == PX4_OK) {
_initialized = true;
} else {
PX4_ERR("init failed");
exit_and_cleanup();
}
} else {
perf_begin(_cycle_perf);
const hrt_abstime cycle_timestamp = hrt_absolute_time();
bool rc_updated = false;
// This block scans for a supported serial RC input and locks onto the first one found
// Scan for 500 msec, then switch protocol
constexpr hrt_abstime rc_scan_max = 500_ms;
// TODO: needs work (poll _rcs_fd)
// int ret = poll(fds, sizeof(fds) / sizeof(fds[0]), 100);
// then update priority to SCHED_PRIORITY_FAST_DRIVER
// read all available data from the serial RC input UART
// read all available data from the serial RC input UART
int newBytes = ::read(_rcs_fd, &_rcs_buf[0], RC_MAX_BUFFER_SIZE);
if (newBytes > 0) {
_bytes_rx += newBytes;
}
const bool rc_scan_locked = _rc_scan_locked;
if (_rc_scan_begin == 0) {
_rc_scan_begin = cycle_timestamp;
// Configure serial port for GHST
ghst_config(_rcs_fd);
// flush serial buffer and any existing buffered data
tcflush(_rcs_fd, TCIOFLUSH);
memset(_rcs_buf, 0, sizeof(_rcs_buf));
} else if (_rc_scan_locked
|| cycle_timestamp - _rc_scan_begin < rc_scan_max) {
// parse new data
if (newBytes > 0) {
int8_t ghst_rssi = -1;
rc_updated = ghst_parse(cycle_timestamp, &_rcs_buf[0], newBytes, &_raw_rc_values[0], &ghst_rssi,
&_raw_rc_count, input_rc_s::RC_INPUT_MAX_CHANNELS);
if (rc_updated) {
// we have a new GHST frame. Publish it.
_rc_in.input_source = input_rc_s::RC_INPUT_SOURCE_PX4FMU_GHST;
fill_rc_in(_raw_rc_count, _raw_rc_values, cycle_timestamp, false, false, 0, ghst_rssi);
_rc_scan_locked = true;
// telemetry
if ((cycle_timestamp - _last_update) > (1_s / (UPDATE_RATE_HZ * NUM_DATA_TYPES))) {
switch (_next_type) {
case 0U:
send_battery_status();
break;
case 1U:
send_gps1_status();
break;
case 2U:
send_gps2_status();
break;
default:
break;
}
_last_update = cycle_timestamp;
_next_type = (_next_type + 1U) % NUM_DATA_TYPES;
}
}
}
}
perf_end(_cycle_perf);
if (rc_updated) {
perf_count(_publish_interval_perf);
_to_input_rc.publish(_rc_in);
} else if (!rc_updated && (hrt_elapsed_time(&_rc_in.timestamp_last_signal) > 1_s)) {
_rc_scan_locked = false;
}
if (!rc_scan_locked && _rc_scan_locked) {
PX4_INFO("RC input locked");
}
}
}
bool RcGhst::send_battery_status()
{
battery_status_s battery_status;
if (_battery_status_sub.update(&battery_status)) {
float voltage_in_10mV = battery_status.voltage_filtered_v * FACTOR_VOLTS_TO_10MV;
float current_in_10mA = battery_status.current_filtered_a * FACTOR_AMPS_TO_10MA;
float fuel_in_10mAh = battery_status.discharged_mah * FACTOR_MAH_TO_10MAH;
return ghst_send_telemetry_battery_status(_rcs_fd,
static_cast<uint16_t>(voltage_in_10mV),
static_cast<uint16_t>(current_in_10mA),
static_cast<uint16_t>(fuel_in_10mAh));
}
return false;
}
bool RcGhst::send_gps1_status()
{
sensor_gps_s vehicle_gps_position;
if (_vehicle_gps_position_sub.update(&vehicle_gps_position)) {
int32_t latitude = vehicle_gps_position.lat; // 1e-7 degrees
int32_t longitude = vehicle_gps_position.lon; // 1e-7 degrees
uint16_t altitude = vehicle_gps_position.alt / 1000; // mm -> m
return ghst_send_telemetry_gps1_status(_rcs_fd, latitude, longitude, altitude);
}
return false;
}
bool RcGhst::send_gps2_status()
{
sensor_gps_s vehicle_gps_position;
if (_vehicle_gps_position_sub.update(&vehicle_gps_position)) {
uint16_t ground_speed = (uint16_t)(vehicle_gps_position.vel_d_m_s / 3.6f * 10.f);
uint16_t ground_course = (uint16_t)(math::degrees(vehicle_gps_position.cog_rad) * 100.f);
uint8_t num_sats = vehicle_gps_position.satellites_used;
// TBD: Can these be computed in a RC telemetry driver?
uint16_t home_dist = 0;
uint16_t home_dir = 0;
uint8_t flags = 0;
return ghst_send_telemetry_gps2_status(_rcs_fd, ground_speed, ground_course, num_sats, home_dist, home_dir, flags);
}
return false;
}
int RcGhst::custom_command(int argc, char *argv[])
{
if (!is_running()) {
int ret = RcGhst::task_spawn(argc, argv);
if (ret) {
return ret;
}
}
return print_usage("unknown command");
}
int RcGhst::print_status()
{
PX4_INFO("Max update rate: %u Hz", 1000000 / _current_update_interval);
if (_device[0] != '\0') {
PX4_INFO("UART device: %s", _device);
PX4_INFO("UART RX bytes: %" PRIu32, _bytes_rx);
}
PX4_INFO("RC state: %s", _rc_scan_locked ? "found" : "searching for signal");
// PX4_INFO("GHST Telemetry: %s", _ghst_telemetry ? "yes" : "no");
perf_print_counter(_cycle_perf);
perf_print_counter(_publish_interval_perf);
if (hrt_elapsed_time(&_rc_in.timestamp) < 1_s) {
print_message(ORB_ID(input_rc), _rc_in);
}
return 0;
}
int RcGhst::print_usage(const char *reason)
{
if (reason) {
PX4_WARN("%s\n", reason);
}
PRINT_MODULE_DESCRIPTION(
R"DESCR_STR(
### Description
This module does the RC input parsing and auto-selecting the method. Supported methods are:
- GHST
)DESCR_STR");
PRINT_MODULE_USAGE_NAME("rc_ghst", "driver");
PRINT_MODULE_USAGE_COMMAND("start");
PRINT_MODULE_USAGE_PARAM_STRING('d', "/dev/ttyS3", "<file:dev>", "RC device", false);
PRINT_MODULE_USAGE_DEFAULT_COMMANDS();
return 0;
}
extern "C" __EXPORT int rc_ghst_main(int argc, char *argv[])
{
return RcGhst::main(argc, argv);
}
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2021 PX4 Development Team. All rights reserved.
* Copyright (c) 2012-2022 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
@@ -31,44 +31,83 @@
*
****************************************************************************/
/**
* @file ghst_telemetry.cpp
*
* IRC Ghost (Immersion RC Ghost) telemetry.
*
* @author Igor Misic <igy1000mb@gmail.com>
* @author Juraj Ciberlin <jciberlin1@gmail.com>
*/
#pragma once
#include <uORB/Subscription.hpp>
#include <uORB/topics/battery_status.h>
#include <uORB/topics/sensor_gps.h>
#include <drivers/drv_hrt.h>
#include <float.h>
/**
* High-level class that handles sending of GHST telemetry data
*/
class GHSTTelemetry
#include "ghst_parser.hpp"
#include <board_config.h>
#include <drivers/drv_hrt.h>
#include <lib/perf/perf_counter.h>
#include <px4_platform_common/px4_config.h>
#include <px4_platform_common/getopt.h>
#include <px4_platform_common/log.h>
#include <px4_platform_common/module.h>
#include <px4_platform_common/module_params.h>
#include <px4_platform_common/px4_work_queue/ScheduledWorkItem.hpp>
#include <uORB/PublicationMulti.hpp>
#include <uORB/Subscription.hpp>
#include <uORB/SubscriptionInterval.hpp>
#include <uORB/topics/battery_status.h>
#include <uORB/topics/input_rc.h>
#include <uORB/topics/sensor_gps.h>
class RcGhst : public ModuleBase<RcGhst>, public ModuleParams, public px4::ScheduledWorkItem
{
public:
/**
* @param uart_fd file descriptor for the UART to use. It is expected to be configured
* already.
*/
explicit GHSTTelemetry(int uart_fd);
RcGhst(const char *device);
virtual ~RcGhst();
~GHSTTelemetry() = default;
/** @see ModuleBase */
static int task_spawn(int argc, char *argv[]);
/**
* Send telemetry data. Call this regularly (i.e. at 100Hz), it will automatically
* limit the sending rate.
* @return true if new data sent
*/
bool update(const hrt_abstime &now);
/** @see ModuleBase */
static int custom_command(int argc, char *argv[]);
/** @see ModuleBase */
static int print_usage(const char *reason = nullptr);
/** @see ModuleBase::print_status() */
int print_status() override;
int init();
private:
void Run() override;
void fill_rc_in(uint16_t raw_rc_count_local,
uint16_t raw_rc_values_local[input_rc_s::RC_INPUT_MAX_CHANNELS],
hrt_abstime now, bool frame_drop, bool failsafe,
unsigned frame_drops, int rssi);
void rc_io_invert(bool invert);
hrt_abstime _rc_scan_begin{0};
bool _initialized{false};
bool _rc_scan_locked{false};
static constexpr unsigned _current_update_interval{4000}; // 250 Hz
input_rc_s _rc_in{};
uORB::PublicationMulti<input_rc_s> _to_input_rc{ORB_ID(input_rc)};
int _rcs_fd{-1};
char _device[20] {}; ///< device / serial port path
static constexpr size_t RC_MAX_BUFFER_SIZE{sizeof(ghst_frame_t)};
uint8_t _rcs_buf[RC_MAX_BUFFER_SIZE] {};
uint16_t _raw_rc_values[input_rc_s::RC_INPUT_MAX_CHANNELS] {};
uint16_t _raw_rc_count{};
perf_counter_t _cycle_perf{perf_alloc(PC_ELAPSED, MODULE_NAME": cycle time")};
perf_counter_t _publish_interval_perf{perf_alloc(PC_INTERVAL, MODULE_NAME": publish interval")};
uint32_t _bytes_rx{0};
// telemetry
bool send_battery_status();
bool send_gps1_status();
bool send_gps2_status();
@@ -76,9 +115,8 @@ private:
uORB::Subscription _vehicle_gps_position_sub{ORB_ID(vehicle_gps_position)};
uORB::Subscription _battery_status_sub{ORB_ID(battery_status)};
int _uart_fd;
hrt_abstime _last_update {0U};
uint32_t _next_type {0U};
hrt_abstime _last_update{};
uint32_t _next_type{};
static constexpr uint32_t NUM_DATA_TYPES {3U}; // number of different telemetry data types
static constexpr uint32_t UPDATE_RATE_HZ {10U}; // update rate [Hz]
@@ -87,5 +125,4 @@ private:
static constexpr float FACTOR_VOLTS_TO_10MV {100.0F};
static constexpr float FACTOR_AMPS_TO_10MA {100.0F};
static constexpr float FACTOR_MAH_TO_10MAH {0.1F};
};
@@ -53,15 +53,12 @@
#endif
#include <drivers/drv_hrt.h>
#include <limits.h>
#include <termios.h>
#include <string.h>
#include <unistd.h>
// TODO: include RSSI dBm to percentage conversion for ghost receiver
#include "spektrum_rssi.h"
#include "ghst.hpp"
#include "common_rc.h"
#include "ghst_parser.hpp"
#define MIN(a,b) (((a)<(b))?(a):(b))
@@ -79,12 +76,59 @@ enum class ghst_parser_state_t : uint8_t {
// only RSSI frame contains value of RSSI, if it is not received, send last received RSSI
static int8_t ghst_rssi = -1;
static ghst_frame_t &ghst_frame = rc_decode_buf.ghst_frame;
static ghst_frame_t ghst_frame;
static uint32_t current_frame_position = 0U;
static ghst_parser_state_t parser_state = ghst_parser_state_t::unsynced;
static uint16_t prev_rc_vals[GHST_MAX_NUM_CHANNELS];
static uint8_t crc8_dvb_s2(uint8_t crc, uint8_t a)
{
crc ^= a;
for (int i = 0; i < 8; ++i) {
if (crc & 0x80) {
crc = (crc << 1) ^ 0xD5;
} else {
crc = crc << 1;
}
}
return crc;
}
static uint8_t crc8_dvb_s2_buf(uint8_t *buf, int len)
{
uint8_t crc = 0;
for (int i = 0; i < len; ++i) {
crc = crc8_dvb_s2(crc, buf[i]);
}
return crc;
}
static constexpr int8_t lu_dbm_percent[] {
100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 99, 98, 98, 97, 96,
95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 79,
78, 77, 75, 74, 73, 71, 70, 68, 66, 65, 63, 61, 59, 57, 55, 52,
50, 47, 45, 42, 39, 35, 32, 28, 24, 19, 13, 7, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
};
#define MASK(n) (n >> (sizeof(n) * CHAR_BIT - 1))
/* constexpr-compatible version of abs() */
static constexpr unsigned c_abs(int n) { return (n + MASK(n)) ^ MASK(n); }
/* convert signed dbm (-92 to -42) to percentage */
static constexpr int8_t dbm_to_percent(int8_t dbm) { return lu_dbm_percent[c_abs(dbm)]; }
/**
* parse the current ghst_frame buffer
*/
@@ -104,14 +148,6 @@ int ghst_config(int uart_fd)
return ret_val;
}
/**
* Convert from RC to PWM value
* @param chan_value channel value in [172, 1811]
* @return PWM channel value in [1000, 2000]
*/
static uint16_t convert_channel_value(unsigned chan_value);
bool ghst_parse(const uint64_t now, const uint8_t *frame, unsigned len, uint16_t *values,
int8_t *rssi, uint16_t *num_values, uint16_t max_channels)
{
@@ -164,7 +200,12 @@ uint8_t ghst_frame_CRC(const ghst_frame_t &frame)
return crc;
}
static uint16_t convert_channel_value(unsigned int chan_value)
/**
* Convert from RC to PWM value
* @param chan_value channel value in [172, 1811]
* @return PWM channel value in [1000, 2000]
*/
static uint16_t convert_channel_value(unsigned chan_value)
{
/*
* RC PWM
@@ -297,7 +338,7 @@ static bool ghst_parse_buffer(uint16_t *values, int8_t *rssi, uint16_t *num_valu
} else if (ghst_frame.type == static_cast<uint8_t>(ghstFrameType::frameTypeRssi)) {
const ghstPayloadRssi_t *const rssiValues = (ghstPayloadRssi_t *)&ghst_frame.payload;
// TODO: call function for RSSI dBm to percentage conversion for ghost receiver
ghst_rssi = spek_dbm_to_percent(static_cast<int8_t>(rssiValues->rssidBm));
ghst_rssi = dbm_to_percent(static_cast<int8_t>(rssiValues->rssidBm));
} else {
GHST_DEBUG("Frame type: %u", ghst_frame.type);
@@ -428,4 +469,3 @@ bool ghst_send_telemetry_gps2_status(int uart_fd, uint16_t ground_speed, uint16_
return write(uart_fd, buf, offset) == offset;
}
+7
View File
@@ -0,0 +1,7 @@
module_name: RC GHST input driver
serial_config:
- command: "rc_ghst start -d ${SERIAL_DEV}"
port_config_param:
name: RC_GHST_PRT_CFG
group: Serial
#default: RC
@@ -1,6 +1,6 @@
############################################################################
#
# Copyright (c) 2016 PX4 Development Team. All rights reserved.
# Copyright (c) 2016-2022 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
@@ -32,10 +32,11 @@
############################################################################
px4_add_module(
MODULE drivers__snapdragon_spektrum_rc
MAIN spektrum_rc
MODULE drivers__rc__ghst__test
MAIN rc_ghst_test
COMPILE_FLAGS
-Wno-unused-result
SRCS
spektrum_rc.cpp
DEPENDS
rc
RcGhstTest.cpp
../ghst_parser.cpp
)
+123
View File
@@ -0,0 +1,123 @@
#include <unit_test.h>
#include <systemlib/err.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <drivers/drv_hrt.h>
#include "../ghst_parser.hpp"
#if defined(CONFIG_ARCH_BOARD_PX4_SITL)
#define TEST_DATA_PATH "./test_data/"
#else
#define TEST_DATA_PATH "/fs/microsd"
#endif
class RcGhstTest : public UnitTest
{
public:
bool run_tests() override;
private:
bool ghstTest();
};
bool RcGhstTest::run_tests()
{
ut_run_test(ghstTest);
return (_tests_failed == 0);
}
bool RcGhstTest::ghstTest()
{
const char *filepath = TEST_DATA_PATH "ghst_rc_channels.txt";
FILE *fp = fopen(filepath, "rt");
ut_test(fp);
int uart_fd = -1;
const int line_size = 500;
char line[line_size];
bool has_decoded_values = false;
const int max_channels = 16;
uint16_t rc_values[max_channels];
uint16_t num_values = 0;
int line_counter = 1;
int8_t ghst_rssi = -1;
ghst_config(uart_fd);
while (fgets(line, line_size, fp) != nullptr) {
if (strncmp(line, "INPUT ", 6) == 0) {
if (has_decoded_values) {
PX4_ERR("Parser decoded values that are not in the test file (line=%i)", line_counter);
return false;
}
// read the values
const char *file_buffer = line + 6;
int frame_len = 0;
uint8_t frame[300];
int offset;
int number;
while (sscanf(file_buffer, "%x, %n", &number, &offset) > 0) {
frame[frame_len++] = number;
file_buffer += offset;
}
// Pipe the data into the parser
hrt_abstime now = hrt_absolute_time();
bool result = ghst_parse(now, frame, frame_len, rc_values, &ghst_rssi, &num_values, max_channels);
if (result) {
has_decoded_values = true;
}
} else if (strncmp(line, "DECODED ", 8) == 0) {
if (!has_decoded_values) {
PX4_ERR("Test file contains decoded values but the parser did not decode anything (line=%i)", line_counter);
return false;
}
// read the values
const char *file_buffer = line + 8;
int offset;
int expected_rc_value;
int expected_num_channels = 0;
while (sscanf(file_buffer, "%x, %n", &expected_rc_value, &offset) > 0) {
// allow a small difference
if (abs(expected_rc_value - (int)rc_values[expected_num_channels]) > 10) {
PX4_ERR("File line: %i, channel: %i", line_counter, expected_num_channels);
ut_compare("Wrong decoded channel", expected_rc_value, rc_values[expected_num_channels]);
}
file_buffer += offset;
++expected_num_channels;
}
if (expected_num_channels != num_values) {
PX4_ERR("File line: %d", line_counter);
ut_compare("Unexpected number of decoded channels", expected_num_channels, num_values);
}
has_decoded_values = false;
}
++line_counter;
}
return true;
}
ut_declare_test_c(rc_ghst_test_main, RcGhstTest)
@@ -1,6 +1,6 @@
############################################################################
#
# Copyright (c) 2015 PX4 Development Team. All rights reserved.
# Copyright (c) 2015-2022 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
@@ -31,15 +31,14 @@
#
############################################################################
px4_add_module(
MODULE drivers__rc_input
MAIN rc_input
MODULE drivers__rc__ppm_in
MAIN ppm_in
COMPILE_FLAGS
SRCS
RCInput.cpp
crsf_telemetry.cpp
ghst_telemetry.cpp
RCInput.hpp
MODULE_CONFIG
module.yaml
DEPENDS
rc
px4_work_queue
)
+5
View File
@@ -0,0 +1,5 @@
menuconfig DRIVERS_RC_PPM_IN
bool "ppm_in"
default n
---help---
Enable support for ppm_in
+316
View File
@@ -0,0 +1,316 @@
/****************************************************************************
*
* Copyright (c) 2012-2022 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 "RCInput.hpp"
using namespace time_literals;
RcPpm::RcPpm() :
ModuleParams(nullptr),
ScheduledWorkItem(MODULE_NAME, px4::wq_configurations::hp_default)
{
// initialize raw_rc values and count
for (unsigned i = 0; i < input_rc_s::RC_INPUT_MAX_CHANNELS; i++) {
_raw_rc_values[i] = UINT16_MAX;
}
}
RcPpm::~RcPpm()
{
perf_free(_cycle_perf);
perf_free(_publish_interval_perf);
}
int RcPpm::init()
{
#ifdef GPIO_PPM_IN
// disable CPPM input by mapping it away from the timer capture input
px4_arch_unconfiggpio(GPIO_PPM_IN);
#endif // GPIO_PPM_IN
return 0;
}
int RcPpm::task_spawn(int argc, char *argv[])
{
RcPpm *instance = new RcPpm();
if (instance == nullptr) {
PX4_ERR("alloc failed");
return PX4_ERROR;
}
_object.store(instance);
_task_id = task_id_is_work_queue;
instance->ScheduleOnInterval(_current_update_interval);
return PX4_OK;
}
void RcPpm::fill_rc_in(uint16_t raw_rc_count_local,
uint16_t raw_rc_values_local[input_rc_s::RC_INPUT_MAX_CHANNELS],
hrt_abstime now, bool frame_drop, bool failsafe,
unsigned frame_drops, int rssi = -1)
{
// fill rc_in struct for publishing
_rc_in.channel_count = raw_rc_count_local;
if (_rc_in.channel_count > input_rc_s::RC_INPUT_MAX_CHANNELS) {
_rc_in.channel_count = input_rc_s::RC_INPUT_MAX_CHANNELS;
}
unsigned valid_chans = 0;
for (unsigned i = 0; i < _rc_in.channel_count; i++) {
_rc_in.values[i] = raw_rc_values_local[i];
if (raw_rc_values_local[i] != UINT16_MAX) {
valid_chans++;
}
// once filled, reset values back to default
_raw_rc_values[i] = UINT16_MAX;
}
_rc_in.timestamp = now;
_rc_in.timestamp_last_signal = _rc_in.timestamp;
_rc_in.rc_ppm_frame_length = 0;
/* fake rssi if no value was provided */
if (rssi == -1) {
if ((_param_rc_rssi_pwm_chan.get() > 0) && (_param_rc_rssi_pwm_chan.get() < _rc_in.channel_count)) {
const int32_t rssi_pwm_chan = _param_rc_rssi_pwm_chan.get();
const int32_t rssi_pwm_min = _param_rc_rssi_pwm_min.get();
const int32_t rssi_pwm_max = _param_rc_rssi_pwm_max.get();
// get RSSI from input channel
int rc_rssi = ((_rc_in.values[rssi_pwm_chan - 1] - rssi_pwm_min) * 100) / (rssi_pwm_max - rssi_pwm_min);
_rc_in.rssi = math::constrain(rc_rssi, 0, 100);
} else if (_analog_rc_rssi_stable) {
// set RSSI if analog RSSI input is present
float rssi_analog = ((_analog_rc_rssi_volt - 0.2f) / 3.0f) * 100.0f;
if (rssi_analog > 100.0f) {
rssi_analog = 100.0f;
}
if (rssi_analog < 0.0f) {
rssi_analog = 0.0f;
}
_rc_in.rssi = rssi_analog;
} else {
_rc_in.rssi = 255;
}
} else {
_rc_in.rssi = rssi;
}
if (valid_chans == 0) {
_rc_in.rssi = 0;
}
_rc_in.rc_failsafe = failsafe;
_rc_in.rc_lost = (valid_chans == 0);
_rc_in.rc_lost_frame_count = frame_drops;
_rc_in.rc_total_frame_count = 0;
}
void RcPpm::Run()
{
if (should_exit()) {
exit_and_cleanup();
return;
}
if (!_initialized) {
if (init() == PX4_OK) {
_initialized = true;
} else {
PX4_ERR("init failed");
exit_and_cleanup();
}
} else {
perf_begin(_cycle_perf);
const hrt_abstime cycle_timestamp = hrt_absolute_time();
#if defined(ADC_RC_RSSI_CHANNEL)
// update ADC sampling
if (_adc_report_sub.updated()) {
adc_report_s adc;
if (_adc_report_sub.copy(&adc)) {
for (unsigned i = 0; i < PX4_MAX_ADC_CHANNELS; ++i) {
if (adc.channel_id[i] == ADC_RC_RSSI_CHANNEL) {
float adc_volt = adc.raw_data[i] *
adc.v_ref /
adc.resolution;
if (_analog_rc_rssi_volt < 0.0f) {
_analog_rc_rssi_volt = adc_volt;
}
_analog_rc_rssi_volt = _analog_rc_rssi_volt * 0.995f + adc_volt * 0.005f;
/* only allow this to be used if we see a high RSSI once */
if (_analog_rc_rssi_volt > 2.5f) {
_analog_rc_rssi_stable = true;
}
}
}
}
}
#endif // ADC_RC_RSSI_CHANNEL
bool rc_updated = false;
// This block scans for a supported serial RC input and locks onto the first one found
// Scan for 500 msec, then switch protocol
constexpr hrt_abstime rc_scan_max = 500_ms;
const bool rc_scan_locked = _rc_scan_locked;
if (_rc_scan_begin == 0) {
_rc_scan_begin = cycle_timestamp;
// Configure timer input pin for CPPM
px4_arch_configgpio(GPIO_PPM_IN);
} else if (_rc_scan_locked || cycle_timestamp - _rc_scan_begin < rc_scan_max) {
// see if we have new PPM input data
if ((ppm_last_valid_decode != _rc_in.timestamp_last_signal) && ppm_decoded_channels > 3) {
// we have a new PPM frame. Publish it.
rc_updated = true;
_rc_in.input_source = input_rc_s::RC_INPUT_SOURCE_PX4FMU_PPM;
fill_rc_in(ppm_decoded_channels, ppm_buffer, cycle_timestamp, false, false, 0);
_rc_scan_locked = true;
_rc_in.rc_ppm_frame_length = ppm_frame_length;
_rc_in.timestamp_last_signal = ppm_last_valid_decode;
}
} else {
// disable CPPM input by mapping it away from the timer capture input
px4_arch_unconfiggpio(GPIO_PPM_IN);
}
perf_end(_cycle_perf);
if (rc_updated) {
perf_count(_publish_interval_perf);
_to_input_rc.publish(_rc_in);
} else if (!rc_updated && (hrt_elapsed_time(&_rc_in.timestamp_last_signal) > 1_s)) {
_rc_scan_locked = false;
}
if (!rc_scan_locked && _rc_scan_locked) {
PX4_INFO("RC input locked");
}
}
}
int RcPpm::custom_command(int argc, char *argv[])
{
/* start the FMU if not running */
if (!is_running()) {
int ret = RcPpm::task_spawn(argc, argv);
if (ret) {
return ret;
}
}
return print_usage("unknown command");
}
int RcPpm::print_status()
{
PX4_INFO("Max update rate: %u Hz", 1000000 / _current_update_interval);
PX4_INFO("RC state: %s", _rc_scan_locked ? "found" : "searching for signal");
#if ADC_RC_RSSI_CHANNEL
if (_analog_rc_rssi_stable) {
PX4_INFO("vrssi: %dmV", (int)(_analog_rc_rssi_volt * 1000.0f));
}
#endif
perf_print_counter(_cycle_perf);
perf_print_counter(_publish_interval_perf);
if (hrt_elapsed_time(&_rc_in.timestamp) < 1_s) {
print_message(ORB_ID(input_rc), _rc_in);
}
return 0;
}
int
RcPpm::print_usage(const char *reason)
{
if (reason) {
PX4_WARN("%s\n", reason);
}
PRINT_MODULE_DESCRIPTION(
R"DESCR_STR(
### Description
This module does the RC input parsing and auto-selecting the method. Supported methods are:
- PPM
)DESCR_STR");
PRINT_MODULE_USAGE_NAME("ppm_in", "driver");
PRINT_MODULE_USAGE_COMMAND("start");
PRINT_MODULE_USAGE_DEFAULT_COMMANDS();
return 0;
}
extern "C" __EXPORT int ppm_in_main(int argc, char *argv[])
{
return RcPpm::main(argc, argv);
}
+118
View File
@@ -0,0 +1,118 @@
/****************************************************************************
*
* Copyright (c) 2012-2022 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 <float.h>
#include <board_config.h>
#include <drivers/drv_adc.h>
#include <drivers/drv_hrt.h>
#include <lib/perf/perf_counter.h>
#include <px4_platform_common/px4_config.h>
#include <px4_platform_common/getopt.h>
#include <px4_platform_common/log.h>
#include <px4_platform_common/module.h>
#include <px4_platform_common/module_params.h>
#include <px4_platform_common/px4_work_queue/ScheduledWorkItem.hpp>
#include <uORB/PublicationMulti.hpp>
#include <uORB/Subscription.hpp>
#include <uORB/SubscriptionInterval.hpp>
#include <uORB/topics/adc_report.h>
#include <uORB/topics/input_rc.h>
#include <uORB/topics/parameter_update.h>
#include <lib/systemlib/ppm_decode.h>
using namespace time_literals;
class RcPpm : public ModuleBase<RcPpm>, public ModuleParams, public px4::ScheduledWorkItem
{
public:
RcPpm();
virtual ~RcPpm();
/** @see ModuleBase */
static int task_spawn(int argc, char *argv[]);
/** @see ModuleBase */
static int custom_command(int argc, char *argv[]);
/** @see ModuleBase */
static int print_usage(const char *reason = nullptr);
/** @see ModuleBase::print_status() */
int print_status() override;
int init();
private:
void Run() override;
void fill_rc_in(uint16_t raw_rc_count_local,
uint16_t raw_rc_values_local[input_rc_s::RC_INPUT_MAX_CHANNELS],
hrt_abstime now, bool frame_drop, bool failsafe,
unsigned frame_drops, int rssi);
void rc_io_invert(bool invert);
hrt_abstime _rc_scan_begin{0};
bool _initialized{false};
bool _rc_scan_locked{false};
static constexpr unsigned _current_update_interval{4000}; // 250 Hz
uORB::SubscriptionInterval _parameter_update_sub{ORB_ID(parameter_update), 1_s};
uORB::Subscription _adc_report_sub{ORB_ID(adc_report)};
input_rc_s _rc_in{};
float _analog_rc_rssi_volt{-1.0f};
bool _analog_rc_rssi_stable{false};
uORB::PublicationMulti<input_rc_s> _to_input_rc{ORB_ID(input_rc)};
uint16_t _raw_rc_values[input_rc_s::RC_INPUT_MAX_CHANNELS] {};
uint16_t _raw_rc_count{};
perf_counter_t _cycle_perf{perf_alloc(PC_ELAPSED, MODULE_NAME": cycle time")};
perf_counter_t _publish_interval_perf{perf_alloc(PC_INTERVAL, MODULE_NAME": publish interval")};
DEFINE_PARAMETERS(
(ParamInt<px4::params::RC_RSSI_PWM_CHAN>) _param_rc_rssi_pwm_chan,
(ParamInt<px4::params::RC_RSSI_PWM_MIN>) _param_rc_rssi_pwm_min,
(ParamInt<px4::params::RC_RSSI_PWM_MAX>) _param_rc_rssi_pwm_max
)
};
+12
View File
@@ -0,0 +1,12 @@
module_name: RC PPM input driver
parameters:
- group: RC
definitions:
PPM_IN_EN:
description:
short: RC PPM in enable
long: |
RC PPM in enable.
type: boolean
default: false
+45
View File
@@ -0,0 +1,45 @@
############################################################################
#
# Copyright (c) 2015-2022 PX4 Development Team. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# 3. Neither the name PX4 nor the names of its contributors may be
# used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
############################################################################
px4_add_module(
MODULE drivers__rc__sbus
MAIN sbus
COMPILE_FLAGS
SRCS
RCInput.cpp
RCInput.hpp
MODULE_CONFIG
module.yaml
DEPENDS
rc_sbus
px4_work_queue
)
+5
View File
@@ -0,0 +1,5 @@
menuconfig DRIVERS_RC_SBUS
bool "sbus"
default n
---help---
Enable support for sbus
+415
View File
@@ -0,0 +1,415 @@
/****************************************************************************
*
* Copyright (c) 2012-2022 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 "RCInput.hpp"
#include <termios.h>
using namespace time_literals;
RcSbus::RcSbus(const char *device) :
ModuleParams(nullptr),
ScheduledWorkItem(MODULE_NAME, px4::serial_port_to_wq(device))
{
// initialize raw_rc values and count
for (unsigned i = 0; i < input_rc_s::RC_INPUT_MAX_CHANNELS; i++) {
_raw_rc_values[i] = UINT16_MAX;
}
if (device) {
strncpy(_device, device, sizeof(_device) - 1);
_device[sizeof(_device) - 1] = '\0';
}
}
RcSbus::~RcSbus()
{
perf_free(_cycle_perf);
perf_free(_publish_interval_perf);
}
int RcSbus::init()
{
_rcs_fd = ::open(_device, O_RDWR | O_NONBLOCK);
// assume SBUS input and immediately switch it to
// so that if Single wire mode on TX there will be only
// a short contention
sbus_config(_rcs_fd, board_rc_singlewire(_device));
if (_rcs_fd < 0) {
return -errno;
}
if (board_rc_swap_rxtx(_device)) {
#if defined(TIOCSSWAP)
ioctl(_rcs_fd, TIOCSSWAP, SER_SWAP_ENABLED);
#endif // TIOCSSWAP
}
#ifdef GPIO_PPM_IN
// disable CPPM input by mapping it away from the timer capture input
px4_arch_unconfiggpio(GPIO_PPM_IN);
#endif // GPIO_PPM_IN
rc_io_invert(false);
return 0;
}
int RcSbus::task_spawn(int argc, char *argv[])
{
bool error_flag = false;
int myoptind = 1;
int ch;
const char *myoptarg = nullptr;
const char *device_name = nullptr;
#if defined(RC_SERIAL_PORT)
device_name = RC_SERIAL_PORT;
#endif // RC_SERIAL_PORT
while ((ch = px4_getopt(argc, argv, "d:", &myoptind, &myoptarg)) != EOF) {
switch (ch) {
case 'd':
device_name = myoptarg;
break;
case '?':
error_flag = true;
break;
default:
PX4_WARN("unrecognized flag");
error_flag = true;
break;
}
}
if (error_flag) {
return -1;
}
if (device_name && (access(device_name, R_OK | W_OK) == 0)) {
RcSbus *instance = new RcSbus(device_name);
if (instance == nullptr) {
PX4_ERR("alloc failed");
return PX4_ERROR;
}
_object.store(instance);
_task_id = task_id_is_work_queue;
instance->ScheduleOnInterval(_current_update_interval);
return PX4_OK;
} else {
if (device_name) {
PX4_ERR("invalid device (-d) %s", device_name);
} else {
PX4_INFO("valid device required");
}
}
return PX4_ERROR;
}
void RcSbus::fill_rc_in(uint16_t raw_rc_count_local,
uint16_t raw_rc_values_local[input_rc_s::RC_INPUT_MAX_CHANNELS],
hrt_abstime now, bool frame_drop, bool failsafe,
unsigned frame_drops, int rssi = -1)
{
// fill rc_in struct for publishing
_rc_in.channel_count = raw_rc_count_local;
if (_rc_in.channel_count > input_rc_s::RC_INPUT_MAX_CHANNELS) {
_rc_in.channel_count = input_rc_s::RC_INPUT_MAX_CHANNELS;
}
unsigned valid_chans = 0;
for (unsigned i = 0; i < _rc_in.channel_count; i++) {
_rc_in.values[i] = raw_rc_values_local[i];
if (raw_rc_values_local[i] != UINT16_MAX) {
valid_chans++;
}
// once filled, reset values back to default
_raw_rc_values[i] = UINT16_MAX;
}
_rc_in.timestamp = now;
_rc_in.timestamp_last_signal = _rc_in.timestamp;
_rc_in.rc_ppm_frame_length = 0;
/* fake rssi if no value was provided */
if (rssi == -1) {
if ((_param_rc_rssi_pwm_chan.get() > 0) && (_param_rc_rssi_pwm_chan.get() < _rc_in.channel_count)) {
const int32_t rssi_pwm_chan = _param_rc_rssi_pwm_chan.get();
const int32_t rssi_pwm_min = _param_rc_rssi_pwm_min.get();
const int32_t rssi_pwm_max = _param_rc_rssi_pwm_max.get();
// get RSSI from input channel
int rc_rssi = ((_rc_in.values[rssi_pwm_chan - 1] - rssi_pwm_min) * 100) / (rssi_pwm_max - rssi_pwm_min);
_rc_in.rssi = math::constrain(rc_rssi, 0, 100);
} else if (_analog_rc_rssi_stable) {
// set RSSI if analog RSSI input is present
float rssi_analog = ((_analog_rc_rssi_volt - 0.2f) / 3.0f) * 100.0f;
if (rssi_analog > 100.0f) {
rssi_analog = 100.0f;
}
if (rssi_analog < 0.0f) {
rssi_analog = 0.0f;
}
_rc_in.rssi = rssi_analog;
} else {
_rc_in.rssi = 255;
}
} else {
_rc_in.rssi = rssi;
}
if (valid_chans == 0) {
_rc_in.rssi = 0;
}
_rc_in.rc_failsafe = failsafe;
_rc_in.rc_lost = (valid_chans == 0);
_rc_in.rc_lost_frame_count = frame_drops;
_rc_in.rc_total_frame_count = 0;
}
void RcSbus::rc_io_invert(bool invert)
{
// First check if the board provides a board-specific inversion method (e.g. via GPIO),
// and if not use an IOCTL
if (!board_rc_invert_input(_device, invert)) {
#if defined(TIOCSINVERT)
if (invert) {
ioctl(_rcs_fd, TIOCSINVERT, SER_INVERT_ENABLED_RX | SER_INVERT_ENABLED_TX);
} else {
ioctl(_rcs_fd, TIOCSINVERT, 0);
}
#endif // TIOCSINVERT
}
}
void RcSbus::Run()
{
if (should_exit()) {
exit_and_cleanup();
return;
}
if (!_initialized) {
if (init() == PX4_OK) {
_initialized = true;
} else {
PX4_ERR("init failed");
exit_and_cleanup();
}
} else {
perf_begin(_cycle_perf);
// Check if parameters have changed
if (_parameter_update_sub.updated()) {
// clear update
parameter_update_s param_update;
_parameter_update_sub.copy(&param_update);
updateParams();
}
const hrt_abstime cycle_timestamp = hrt_absolute_time();
bool rc_updated = false;
// This block scans for a supported serial RC input and locks onto the first one found
// Scan for 500 msec, then switch protocol
constexpr hrt_abstime rc_scan_max = 500_ms;
unsigned frame_drops = 0;
// TODO: needs work (poll _rcs_fd)
// int ret = poll(fds, sizeof(fds) / sizeof(fds[0]), 100);
// then update priority to SCHED_PRIORITY_FAST_DRIVER
// read all available data from the serial RC input UART
// read all available data from the serial RC input UART
int newBytes = ::read(_rcs_fd, &_rcs_buf[0], RC_MAX_BUFFER_SIZE);
if (newBytes > 0) {
_bytes_rx += newBytes;
}
const bool rc_scan_locked = _rc_scan_locked;
if (_rc_scan_begin == 0) {
_rc_scan_begin = cycle_timestamp;
// Configure serial port for SBUS
sbus_config(_rcs_fd, board_rc_singlewire(_device));
rc_io_invert(true);
// flush serial buffer and any existing buffered data
tcflush(_rcs_fd, TCIOFLUSH);
memset(_rcs_buf, 0, sizeof(_rcs_buf));
} else if (_rc_scan_locked
|| cycle_timestamp - _rc_scan_begin < rc_scan_max) {
// parse new data
if (newBytes > 0) {
bool sbus_failsafe = false;
bool sbus_frame_drop = false;
rc_updated = sbus_parse(cycle_timestamp, &_rcs_buf[0], newBytes, &_raw_rc_values[0], &_raw_rc_count, &sbus_failsafe,
&sbus_frame_drop, &frame_drops, input_rc_s::RC_INPUT_MAX_CHANNELS);
if (rc_updated) {
// we have a new SBUS frame. Publish it.
_rc_in.input_source = input_rc_s::RC_INPUT_SOURCE_PX4FMU_SBUS;
fill_rc_in(_raw_rc_count, _raw_rc_values, cycle_timestamp,
sbus_frame_drop, sbus_failsafe, frame_drops);
_rc_scan_locked = true;
}
}
} else {
// Scan the next protocol
rc_io_invert(false);
}
perf_end(_cycle_perf);
if (rc_updated) {
perf_count(_publish_interval_perf);
_to_input_rc.publish(_rc_in);
} else if (!rc_updated && (hrt_elapsed_time(&_rc_in.timestamp_last_signal) > 1_s)) {
_rc_scan_locked = false;
}
if (!rc_scan_locked && _rc_scan_locked) {
PX4_INFO("RC input locked");
}
}
}
int RcSbus::custom_command(int argc, char *argv[])
{
if (!is_running()) {
int ret = RcSbus::task_spawn(argc, argv);
if (ret) {
return ret;
}
}
return print_usage("unknown command");
}
int RcSbus::print_status()
{
PX4_INFO("Max update rate: %u Hz", 1000000 / _current_update_interval);
if (_device[0] != '\0') {
PX4_INFO("UART device: %s", _device);
PX4_INFO("UART RX bytes: %" PRIu32, _bytes_rx);
}
PX4_INFO("RC state: %s", _rc_scan_locked ? "found" : "searching for signal");
PX4_INFO("SBUS frame drops: %u", sbus_dropped_frames());
#if ADC_RC_RSSI_CHANNEL
if (_analog_rc_rssi_stable) {
PX4_INFO("vrssi: %dmV", (int)(_analog_rc_rssi_volt * 1000.0f));
}
#endif
perf_print_counter(_cycle_perf);
perf_print_counter(_publish_interval_perf);
if (hrt_elapsed_time(&_rc_in.timestamp) < 1_s) {
print_message(ORB_ID(input_rc), _rc_in);
}
return 0;
}
int RcSbus::print_usage(const char *reason)
{
if (reason) {
PX4_WARN("%s\n", reason);
}
PRINT_MODULE_DESCRIPTION(
R"DESCR_STR(
### Description
This module does the RC input parsing and auto-selecting the method. Supported methods are:
- SBUS
)DESCR_STR");
PRINT_MODULE_USAGE_NAME("rc_sbus", "driver");
PRINT_MODULE_USAGE_COMMAND("start");
PRINT_MODULE_USAGE_PARAM_STRING('d', "/dev/ttyS3", "<file:dev>", "RC device", false);
PRINT_MODULE_USAGE_DEFAULT_COMMANDS();
return 0;
}
extern "C" __EXPORT int sbus_main(int argc, char *argv[])
{
return RcSbus::main(argc, argv);
}
@@ -39,12 +39,7 @@
#include <drivers/drv_adc.h>
#include <drivers/drv_hrt.h>
#include <lib/perf/perf_counter.h>
#include <lib/rc/crsf.h>
#include <lib/rc/ghst.hpp>
#include <lib/rc/dsm.h>
#include <lib/rc/sbus.h>
#include <lib/rc/st24.h>
#include <lib/rc/sumd.h>
#include <lib/rc/sbus/sbus.h>
#include <px4_platform_common/px4_config.h>
#include <px4_platform_common/getopt.h>
#include <px4_platform_common/log.h>
@@ -57,22 +52,15 @@
#include <uORB/topics/adc_report.h>
#include <uORB/topics/input_rc.h>
#include <uORB/topics/parameter_update.h>
#include <uORB/topics/vehicle_command.h>
#include <uORB/topics/vehicle_status.h>
#include "crsf_telemetry.h"
#include "ghst_telemetry.hpp"
using namespace time_literals;
#ifdef HRT_PPM_CHANNEL
# include <systemlib/ppm_decode.h>
#endif
class RCInput : public ModuleBase<RCInput>, public ModuleParams, public px4::ScheduledWorkItem
class RcSbus : public ModuleBase<RcSbus>, public ModuleParams, public px4::ScheduledWorkItem
{
public:
RCInput(const char *device);
virtual ~RCInput();
RcSbus(const char *device);
virtual ~RcSbus();
/** @see ModuleBase */
static int task_spawn(int argc, char *argv[]);
@@ -86,45 +74,16 @@ public:
/** @see ModuleBase::print_status() */
int print_status() override;
int init();
int init();
private:
enum RC_SCAN {
RC_SCAN_NONE = 0,
RC_SCAN_PPM = 1,
RC_SCAN_SBUS = 2,
RC_SCAN_DSM = 3,
RC_SCAN_ST24 = 5,
RC_SCAN_SUMD = 4,
RC_SCAN_CRSF = 6,
RC_SCAN_GHST = 7,
} _rc_scan_state{RC_SCAN_SBUS};
static constexpr char const *RC_SCAN_STRING[] {
"None",
"PPM",
"SBUS",
"DSM",
"ST24",
"SUMD",
"CRSF",
"GHST"
};
void Run() override;
#if defined(SPEKTRUM_POWER)
bool bind_spektrum(int arg = DSMX8_BIND_PULSES) const;
#endif // SPEKTRUM_POWER
void fill_rc_in(uint16_t raw_rc_count_local,
uint16_t raw_rc_values_local[input_rc_s::RC_INPUT_MAX_CHANNELS],
hrt_abstime now, bool frame_drop, bool failsafe,
unsigned frame_drops, int rssi);
void set_rc_scan_state(RC_SCAN _rc_scan_state);
void rc_io_invert(bool invert);
hrt_abstime _rc_scan_begin{0};
@@ -136,40 +95,29 @@ private:
uORB::SubscriptionInterval _parameter_update_sub{ORB_ID(parameter_update), 1_s};
uORB::Subscription _adc_report_sub{ORB_ID(adc_report)};
uORB::Subscription _vehicle_cmd_sub{ORB_ID(vehicle_command)};
uORB::Subscription _vehicle_status_sub{ORB_ID(vehicle_status)};
input_rc_s _rc_in{};
float _analog_rc_rssi_volt{-1.0f};
bool _analog_rc_rssi_stable{false};
bool _armed{false};
float _analog_rc_rssi_volt{-1.0f};
bool _analog_rc_rssi_stable{false};
uORB::PublicationMulti<input_rc_s> _to_input_rc{ORB_ID(input_rc)};
int _rcs_fd{-1};
char _device[20] {}; ///< device / serial port path
static constexpr size_t RC_MAX_BUFFER_SIZE{SBUS_BUFFER_SIZE};
static constexpr size_t RC_MAX_BUFFER_SIZE{sizeof(sbus_frame_t)};
uint8_t _rcs_buf[RC_MAX_BUFFER_SIZE] {};
uint16_t _raw_rc_values[input_rc_s::RC_INPUT_MAX_CHANNELS] {};
uint16_t _raw_rc_count{};
CRSFTelemetry *_crsf_telemetry{nullptr};
GHSTTelemetry *_ghst_telemetry{nullptr};
perf_counter_t _cycle_perf;
perf_counter_t _publish_interval_perf;
perf_counter_t _cycle_perf{perf_alloc(PC_ELAPSED, MODULE_NAME": cycle time")};
perf_counter_t _publish_interval_perf{perf_alloc(PC_INTERVAL, MODULE_NAME": publish interval")};
uint32_t _bytes_rx{0};
DEFINE_PARAMETERS(
(ParamInt<px4::params::RC_RSSI_PWM_CHAN>) _param_rc_rssi_pwm_chan,
(ParamInt<px4::params::RC_RSSI_PWM_MIN>) _param_rc_rssi_pwm_min,
(ParamInt<px4::params::RC_RSSI_PWM_MAX>) _param_rc_rssi_pwm_max,
(ParamInt<px4::params::RC_INPUT_PROTO>) _param_rc_input_proto
(ParamInt<px4::params::RC_RSSI_PWM_MAX>) _param_rc_rssi_pwm_max
)
};
+7
View File
@@ -0,0 +1,7 @@
module_name: RC SBUS input driver
serial_config:
- command: "sbus start -d ${SERIAL_DEV}"
port_config_param:
name: RC_SBUS_PRT_CFG
group: Serial
#default: RC
@@ -0,0 +1,45 @@
############################################################################
#
# Copyright (c) 2015-2022 PX4 Development Team. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# 3. Neither the name PX4 nor the names of its contributors may be
# used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
############################################################################
px4_add_module(
MODULE drivers__rc__spektrum_dsm
MAIN spektrum_dsm
COMPILE_FLAGS
SRCS
RCInput.cpp
RCInput.hpp
MODULE_CONFIG
module.yaml
DEPENDS
rc_dsm
px4_work_queue
)
+5
View File
@@ -0,0 +1,5 @@
menuconfig DRIVERS_RC_SPEKTRUM_DSM
bool "spektrum_dsm"
default n
---help---
Enable support for spektrum_dsm
+456
View File
@@ -0,0 +1,456 @@
/****************************************************************************
*
* Copyright (c) 2012-2022 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 "RCInput.hpp"
#include <uORB/topics/vehicle_command_ack.h>
#include <termios.h>
using namespace time_literals;
RcDsm::RcDsm(const char *device) :
ModuleParams(nullptr),
ScheduledWorkItem(MODULE_NAME, px4::serial_port_to_wq(device))
{
if (device) {
strncpy(_device, device, sizeof(_device) - 1);
_device[sizeof(_device) - 1] = '\0';
}
}
RcDsm::~RcDsm()
{
#if defined(RC_SERIAL_PORT) && defined(SPEKTRUM_POWER_PASSIVE)
if ((strcmp(RC_SERIAL_PORT, _device) == 0)) {
// Disable power controls for Spektrum receiver
SPEKTRUM_POWER_PASSIVE();
}
#endif // RC_SERIAL_PORT && SPEKTRUM_POWER_PASSIVE
perf_free(_cycle_perf);
perf_free(_publish_interval_perf);
}
int RcDsm::init()
{
return 0;
}
int RcDsm::task_spawn(int argc, char *argv[])
{
bool error_flag = false;
int myoptind = 1;
int ch;
const char *myoptarg = nullptr;
const char *device_name = nullptr;
while ((ch = px4_getopt(argc, argv, "d:", &myoptind, &myoptarg)) != EOF) {
switch (ch) {
case 'd':
device_name = myoptarg;
break;
case '?':
error_flag = true;
break;
default:
PX4_WARN("unrecognized flag");
error_flag = true;
break;
}
}
if (error_flag) {
return -1;
}
if (device_name && (access(device_name, R_OK | W_OK) == 0)) {
RcDsm *instance = new RcDsm(device_name);
if (instance == nullptr) {
PX4_ERR("alloc failed");
return PX4_ERROR;
}
_object.store(instance);
_task_id = task_id_is_work_queue;
instance->ScheduleOnInterval(_current_update_interval);
return PX4_OK;
} else {
if (device_name) {
PX4_ERR("invalid device (-d) %s", device_name);
} else {
PX4_INFO("valid device required");
}
}
return PX4_ERROR;
}
void RcDsm::Run()
{
if (should_exit()) {
if (_rcs_fd >= 0) {
close(_rcs_fd);
_rcs_fd = -1;
}
exit_and_cleanup();
return;
}
perf_begin(_cycle_perf);
if (_vehicle_status_sub.updated()) {
vehicle_status_s vehicle_status;
if (_vehicle_status_sub.copy(&vehicle_status)) {
_armed = (vehicle_status.arming_state == vehicle_status_s::ARMING_STATE_ARMED);
}
}
const hrt_abstime cycle_timestamp = hrt_absolute_time();
/* vehicle command */
vehicle_command_s vcmd;
if (_vehicle_cmd_sub.update(&vcmd)) {
// Check for a pairing command
if (vcmd.command == vehicle_command_s::VEHICLE_CMD_START_RX_PAIR) {
uint8_t cmd_ret = vehicle_command_ack_s::VEHICLE_CMD_RESULT_UNSUPPORTED;
#if defined(RC_SERIAL_PORT)
if ((strcmp(RC_SERIAL_PORT, _device) == 0)) {
#if defined(SPEKTRUM_POWER)
if (!_rc_scan_locked && !_armed) {
if ((int)vcmd.param1 == 0) {
// DSM binding command
int dsm_bind_mode = (int)vcmd.param2;
int dsm_bind_pulses = 0;
if (dsm_bind_mode == 0) {
dsm_bind_pulses = DSM2_BIND_PULSES;
} else if (dsm_bind_mode == 1) {
dsm_bind_pulses = DSMX_BIND_PULSES;
} else {
dsm_bind_pulses = DSMX8_BIND_PULSES;
}
// specify 11ms DSMX. RX will automatically fall back to 22ms or DSM2 if necessary
if (dsm_bind_pulses == DSM2_BIND_PULSES ||
dsm_bind_pulses == DSMX_BIND_PULSES ||
dsm_bind_pulses == DSMX8_BIND_PULSES) {
/* only allow DSM2, DSM-X and DSM-X with more than 7 channels */
PX4_INFO("DSM_BIND_START: DSM%s RX", (dsm_bind_pulses == 0) ? "2" : ((dsm_bind_pulses == 1) ? "-X" : "-X8"));
dsm_bind(DSM_CMD_BIND_POWER_DOWN, 0);
dsm_bind(DSM_CMD_BIND_SET_RX_OUT, 0);
usleep(500000);
dsm_bind(DSM_CMD_BIND_POWER_UP, 0);
usleep(72000);
irqstate_t flags = px4_enter_critical_section();
dsm_bind(DSM_CMD_BIND_SEND_PULSES, dsm_bind_pulses);
px4_leave_critical_section(flags);
usleep(50000);
dsm_bind(DSM_CMD_BIND_REINIT_UART, 0);
cmd_ret = vehicle_command_ack_s::VEHICLE_CMD_RESULT_ACCEPTED;
} else {
cmd_ret = vehicle_command_ack_s::VEHICLE_CMD_RESULT_FAILED;
PX4_ERR("DSM bind failed");
}
}
} else {
cmd_ret = vehicle_command_ack_s::VEHICLE_CMD_RESULT_TEMPORARILY_REJECTED;
}
#endif // SPEKTRUM_POWER
}
#endif // RC_SERIAL_PORT
// publish acknowledgement
vehicle_command_ack_s command_ack{};
command_ack.command = vcmd.command;
command_ack.result = cmd_ret;
command_ack.target_system = vcmd.source_system;
command_ack.target_component = vcmd.source_component;
command_ack.timestamp = hrt_absolute_time();
uORB::Publication<vehicle_command_ack_s> vehicle_command_ack_pub{ORB_ID(vehicle_command_ack)};
vehicle_command_ack_pub.publish(command_ack);
}
}
if (!_rc_scan_locked && (_rc_scan_begin == 0)) {
_rc_scan_begin = cycle_timestamp;
// Configure serial port for DSM
#if defined(RC_SERIAL_PORT)
if ((strcmp(RC_SERIAL_PORT, _device) == 0)) {
# if defined(SPEKTRUM_POWER_CONFIG)
// Enable power controls for Spektrum receiver
SPEKTRUM_POWER_CONFIG();
# endif // SPEKTRUM_POWER_CONFIG
# if defined(SPEKTRUM_POWER)
// enable power on DSM connector
SPEKTRUM_POWER(true);
# endif // SPEKTRUM_POWER
}
#endif // RC_SERIAL_PORT
if (_rcs_fd >= 0) {
close(_rcs_fd);
_rcs_fd = -1;
}
if (_rcs_fd < 0) {
// dsm_init sets some file static variables and returns a file descriptor
// it also powers on the radio if needed
_rcs_fd = open(_device, O_RDWR | O_NONBLOCK);
if (board_rc_swap_rxtx(_device)) {
#if defined(TIOCSSWAP)
ioctl(_rcs_fd, TIOCSSWAP, SER_SWAP_ENABLED);
#endif // TIOCSSWAP
}
// First check if the board provides a board-specific inversion method (e.g. via GPIO),
// and if not use an IOCTL
if (!board_rc_invert_input(_device, false)) {
#if defined(TIOCSINVERT)
ioctl(_rcs_fd, TIOCSINVERT, 0);
#endif // TIOCSINVERT
}
}
if (_rcs_fd >= 0) {
// 115200bps, no parity, one stop bit
struct termios t;
tcgetattr(_rcs_fd, &t);
cfsetspeed(&t, 115200);
t.c_cflag &= ~(CSTOPB | PARENB);
tcsetattr(_rcs_fd, TCSANOW, &t);
// initialise the decoder
dsm_proto_init();
}
}
if (_rcs_fd >= 0) {
// TODO: needs work (poll _rcs_fd)
// int ret = poll(fds, sizeof(fds) / sizeof(fds[0]), 100);
// then update priority to SCHED_PRIORITY_FAST_DRIVER
// read all available data from the serial RC input UART
const hrt_abstime timestamp_last_signal = hrt_absolute_time();
static constexpr size_t RC_MAX_BUFFER_SIZE{sizeof(dsm_decode_t)};
uint8_t rcs_buf[RC_MAX_BUFFER_SIZE] {};
int new_bytes = ::read(_rcs_fd, &rcs_buf[0], RC_MAX_BUFFER_SIZE);
if (new_bytes > 0) {
_bytes_rx += new_bytes;
}
if (new_bytes > 0) {
uint16_t raw_rc_values[input_rc_s::RC_INPUT_MAX_CHANNELS];
for (auto &rc_channel : raw_rc_values) {
rc_channel = UINT16_MAX;
}
uint16_t raw_rc_count = 0;
bool dsm_11_bit = false;
unsigned frame_drops = 0;
int8_t dsm_rssi = -1;
// parse new data
const bool rc_updated = dsm_parse(cycle_timestamp, &rcs_buf[0], new_bytes, &raw_rc_values[0], &raw_rc_count,
&dsm_11_bit, &frame_drops, &dsm_rssi, input_rc_s::RC_INPUT_MAX_CHANNELS);
if (rc_updated) {
if (!_rc_scan_locked) {
_rc_scan_locked = true;
PX4_INFO("RC input locked");
}
input_rc_s input_rc{};
input_rc.timestamp_last_signal = timestamp_last_signal;
input_rc.input_source = input_rc_s::RC_INPUT_SOURCE_PX4FMU_DSM;
unsigned valid_chans = 0;
for (unsigned i = 0; i < math::min(raw_rc_count, (uint16_t)input_rc_s::RC_INPUT_MAX_CHANNELS); i++) {
input_rc.values[i] = raw_rc_values[i];
if (raw_rc_values[i] != UINT16_MAX) {
valid_chans++;
}
}
input_rc.channel_count = valid_chans;
input_rc.rssi = -1;
input_rc.link_quality = -1;
input_rc.rssi_dbm = NAN;
if ((valid_chans == 0) && (dsm_rssi >= 0) && (dsm_rssi <= 100)) {
input_rc.rssi = dsm_rssi;
}
input_rc.rc_lost = (valid_chans == 0);
input_rc.rc_lost_frame_count = frame_drops;
input_rc.rc_total_frame_count = 0; // TODO:
input_rc.timestamp = hrt_absolute_time();
_input_rc_pub.publish(input_rc);
perf_count_interval(_publish_interval_perf, timestamp_last_signal);
_last_valid_rc = timestamp_last_signal;
}
}
}
perf_end(_cycle_perf);
if (!_armed && (hrt_elapsed_time(&_last_valid_rc) > 5_s)) {
_rc_scan_locked = false;
_rc_scan_begin = 0;
}
}
int RcDsm::custom_command(int argc, char *argv[])
{
const char *verb = argv[0];
if (!strcmp(verb, "bind")) {
if (is_running()) {
uORB::Publication<vehicle_command_s> vehicle_command_pub{ORB_ID(vehicle_command)};
vehicle_command_s vcmd{};
vcmd.command = vehicle_command_s::VEHICLE_CMD_START_RX_PAIR;
vcmd.timestamp = hrt_absolute_time();
vehicle_command_pub.publish(vcmd);
return 0;
} else {
return -1;
}
}
/* start the FMU if not running */
if (!is_running()) {
int ret = RcDsm::task_spawn(argc, argv);
if (ret) {
return ret;
}
}
return print_usage("unknown command");
}
int RcDsm::print_status()
{
PX4_INFO("Max update rate: %u Hz", 1000000 / _current_update_interval);
if (_device[0] != '\0') {
PX4_INFO("UART device: %s", _device);
PX4_INFO("UART RX bytes: %" PRIu32, _bytes_rx);
}
PX4_INFO("RC state: %s", _rc_scan_locked ? "found" : "searching for signal");
perf_print_counter(_cycle_perf);
perf_print_counter(_publish_interval_perf);
return 0;
}
int RcDsm::print_usage(const char *reason)
{
if (reason) {
PX4_WARN("%s\n", reason);
}
PRINT_MODULE_DESCRIPTION(
R"DESCR_STR(
### Description
This module does the RC input parsing and auto-selecting the method. Supported methods are:
- DSM
)DESCR_STR");
PRINT_MODULE_USAGE_NAME("rc_dsm", "driver");
PRINT_MODULE_USAGE_COMMAND("start");
PRINT_MODULE_USAGE_PARAM_STRING('d', "/dev/ttyS3", "<file:dev>", "RC device", false);
#if defined(SPEKTRUM_POWER)
PRINT_MODULE_USAGE_COMMAND_DESCR("bind", "Send a DSM bind command (module must be running)");
#endif /* SPEKTRUM_POWER */
PRINT_MODULE_USAGE_DEFAULT_COMMANDS();
return 0;
}
extern "C" __EXPORT int spektrum_dsm_main(int argc, char *argv[])
{
return RcDsm::main(argc, argv);
}
+95
View File
@@ -0,0 +1,95 @@
/****************************************************************************
*
* Copyright (c) 2012-2022 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 <board_config.h>
#include <drivers/drv_hrt.h>
#include <lib/perf/perf_counter.h>
#include <lib/rc/dsm/dsm.h>
#include <px4_platform_common/px4_config.h>
#include <px4_platform_common/getopt.h>
#include <px4_platform_common/log.h>
#include <px4_platform_common/module.h>
#include <px4_platform_common/module_params.h>
#include <px4_platform_common/px4_work_queue/ScheduledWorkItem.hpp>
#include <uORB/PublicationMulti.hpp>
#include <uORB/Subscription.hpp>
#include <uORB/SubscriptionInterval.hpp>
#include <uORB/topics/input_rc.h>
#include <uORB/topics/vehicle_command.h>
#include <uORB/topics/vehicle_status.h>
class RcDsm : public ModuleBase<RcDsm>, public ModuleParams, public px4::ScheduledWorkItem
{
public:
RcDsm(const char *device);
virtual ~RcDsm();
/** @see ModuleBase */
static int task_spawn(int argc, char *argv[]);
/** @see ModuleBase */
static int custom_command(int argc, char *argv[]);
/** @see ModuleBase */
static int print_usage(const char *reason = nullptr);
/** @see ModuleBase::print_status() */
int print_status() override;
int init();
private:
void Run() override;
static constexpr unsigned _current_update_interval{4000}; // 250 Hz
uORB::Subscription _vehicle_cmd_sub{ORB_ID(vehicle_command)};
uORB::Subscription _vehicle_status_sub{ORB_ID(vehicle_status)};
uORB::PublicationMulti<input_rc_s> _input_rc_pub{ORB_ID(input_rc)};
int _rcs_fd{-1};
char _device[20] {}; ///< device / serial port path
uint32_t _bytes_rx{0};
hrt_abstime _rc_scan_begin{0};
hrt_abstime _last_valid_rc{0};
bool _armed{false};
bool _rc_scan_locked{false};
perf_counter_t _cycle_perf{perf_alloc(PC_ELAPSED, MODULE_NAME": cycle time")};
perf_counter_t _publish_interval_perf{perf_alloc(PC_INTERVAL, MODULE_NAME": publish interval")};
};
+7
View File
@@ -0,0 +1,7 @@
module_name: RC Spektrum DSM input driver
serial_config:
- command: "spektrum_dsm start -d ${SERIAL_DEV}"
port_config_param:
name: RC_DSM_PRT_CFG
group: Serial
#default: RC
+45
View File
@@ -0,0 +1,45 @@
############################################################################
#
# Copyright (c) 2015-2022 PX4 Development Team. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# 3. Neither the name PX4 nor the names of its contributors may be
# used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
############################################################################
px4_add_module(
MODULE drivers__rc__rc_st24
MAIN rc_st24
COMPILE_FLAGS
SRCS
RCInput.cpp
RCInput.hpp
MODULE_CONFIG
module.yaml
DEPENDS
rc_st24
px4_work_queue
)
+5
View File
@@ -0,0 +1,5 @@
menuconfig DRIVERS_RC_ST24_RC
bool "rc_st24"
default n
---help---
Enable support for rc_st24
+378
View File
@@ -0,0 +1,378 @@
/****************************************************************************
*
* Copyright (c) 2012-2022 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 "RCInput.hpp"
#include <termios.h>
using namespace time_literals;
RcSt24::RcSt24(const char *device) :
ModuleParams(nullptr),
ScheduledWorkItem(MODULE_NAME, px4::serial_port_to_wq(device))
{
// initialize raw_rc values and count
for (unsigned i = 0; i < input_rc_s::RC_INPUT_MAX_CHANNELS; i++) {
_raw_rc_values[i] = UINT16_MAX;
}
if (device) {
strncpy(_device, device, sizeof(_device) - 1);
_device[sizeof(_device) - 1] = '\0';
}
}
RcSt24::~RcSt24()
{
perf_free(_cycle_perf);
perf_free(_publish_interval_perf);
}
int RcSt24::init()
{
_rcs_fd = ::open(_device, O_RDWR | O_NONBLOCK);
if (_rcs_fd >= 0) {
// 115200bps, no parity, one stop bit
struct termios t {};
tcgetattr(_rcs_fd, &t);
cfsetspeed(&t, 115200);
t.c_cflag &= ~(CSTOPB | PARENB);
tcsetattr(_rcs_fd, TCSANOW, &t);
}
if (_rcs_fd < 0) {
return -errno;
}
if (board_rc_swap_rxtx(_device)) {
#if defined(TIOCSSWAP)
ioctl(_rcs_fd, TIOCSSWAP, SER_SWAP_ENABLED);
#endif // TIOCSSWAP
}
rc_io_invert(false);
return 0;
}
int RcSt24::task_spawn(int argc, char *argv[])
{
bool error_flag = false;
int myoptind = 1;
int ch;
const char *myoptarg = nullptr;
const char *device_name = nullptr;
#if defined(RC_SERIAL_PORT)
device_name = RC_SERIAL_PORT;
#endif // RC_SERIAL_PORT
while ((ch = px4_getopt(argc, argv, "d:", &myoptind, &myoptarg)) != EOF) {
switch (ch) {
case 'd':
device_name = myoptarg;
break;
case '?':
error_flag = true;
break;
default:
PX4_WARN("unrecognized flag");
error_flag = true;
break;
}
}
if (error_flag) {
return -1;
}
if (device_name && (access(device_name, R_OK | W_OK) == 0)) {
RcSt24 *instance = new RcSt24(device_name);
if (instance == nullptr) {
PX4_ERR("alloc failed");
return PX4_ERROR;
}
_object.store(instance);
_task_id = task_id_is_work_queue;
instance->ScheduleOnInterval(_current_update_interval);
return PX4_OK;
} else {
if (device_name) {
PX4_ERR("invalid device (-d) %s", device_name);
} else {
PX4_INFO("valid device required");
}
}
return PX4_ERROR;
}
void RcSt24::fill_rc_in(uint16_t raw_rc_count_local,
uint16_t raw_rc_values_local[input_rc_s::RC_INPUT_MAX_CHANNELS],
hrt_abstime now, bool frame_drop, bool failsafe,
unsigned frame_drops, int rssi = -1)
{
// fill rc_in struct for publishing
_rc_in.channel_count = raw_rc_count_local;
if (_rc_in.channel_count > input_rc_s::RC_INPUT_MAX_CHANNELS) {
_rc_in.channel_count = input_rc_s::RC_INPUT_MAX_CHANNELS;
}
unsigned valid_chans = 0;
for (unsigned i = 0; i < _rc_in.channel_count; i++) {
_rc_in.values[i] = raw_rc_values_local[i];
if (raw_rc_values_local[i] != UINT16_MAX) {
valid_chans++;
}
// once filled, reset values back to default
_raw_rc_values[i] = UINT16_MAX;
}
_rc_in.timestamp = now;
_rc_in.timestamp_last_signal = _rc_in.timestamp;
_rc_in.rc_ppm_frame_length = 0;
if (valid_chans == 0) {
_rc_in.rssi = 0;
}
_rc_in.rc_failsafe = failsafe;
_rc_in.rc_lost = (valid_chans == 0);
_rc_in.rc_lost_frame_count = frame_drops;
_rc_in.rc_total_frame_count = 0;
}
void RcSt24::rc_io_invert(bool invert)
{
// First check if the board provides a board-specific inversion method (e.g. via GPIO),
// and if not use an IOCTL
if (!board_rc_invert_input(_device, invert)) {
#if defined(TIOCSINVERT)
if (invert) {
ioctl(_rcs_fd, TIOCSINVERT, SER_INVERT_ENABLED_RX | SER_INVERT_ENABLED_TX);
} else {
ioctl(_rcs_fd, TIOCSINVERT, 0);
}
#endif // TIOCSINVERT
}
}
void RcSt24::Run()
{
if (should_exit()) {
exit_and_cleanup();
return;
}
if (!_initialized) {
if (init() == PX4_OK) {
_initialized = true;
} else {
PX4_ERR("init failed");
exit_and_cleanup();
}
} else {
perf_begin(_cycle_perf);
const hrt_abstime cycle_timestamp = hrt_absolute_time();
bool rc_updated = false;
// This block scans for a supported serial RC input and locks onto the first one found
// Scan for 500 msec, then switch protocol
constexpr hrt_abstime rc_scan_max = 500_ms;
unsigned frame_drops = 0;
// TODO: needs work (poll _rcs_fd)
// int ret = poll(fds, sizeof(fds) / sizeof(fds[0]), 100);
// then update priority to SCHED_PRIORITY_FAST_DRIVER
// read all available data from the serial RC input UART
// read all available data from the serial RC input UART
int newBytes = ::read(_rcs_fd, &_rcs_buf[0], RC_MAX_BUFFER_SIZE);
if (newBytes > 0) {
_bytes_rx += newBytes;
}
const bool rc_scan_locked = _rc_scan_locked;
if (_rc_scan_begin == 0) {
_rc_scan_begin = cycle_timestamp;
// Configure serial port for DSM
if (_rcs_fd >= 0) {
// 115200bps, no parity, one stop bit
struct termios t {};
tcgetattr(_rcs_fd, &t);
cfsetspeed(&t, 115200);
t.c_cflag &= ~(CSTOPB | PARENB);
tcsetattr(_rcs_fd, TCSANOW, &t);
}
// flush serial buffer and any existing buffered data
tcflush(_rcs_fd, TCIOFLUSH);
memset(_rcs_buf, 0, sizeof(_rcs_buf));
} else if (_rc_scan_locked
|| cycle_timestamp - _rc_scan_begin < rc_scan_max) {
if (newBytes > 0) {
// parse new data
uint8_t st24_rssi, lost_count;
rc_updated = false;
for (unsigned i = 0; i < (unsigned)newBytes; i++) {
/* set updated flag if one complete packet was parsed */
st24_rssi = input_rc_s::RSSI_MAX;
rc_updated = (OK == st24_decode(_rcs_buf[i], &st24_rssi, &lost_count,
&_raw_rc_count, _raw_rc_values, input_rc_s::RC_INPUT_MAX_CHANNELS));
}
// The st24 will keep outputting RC channels and RSSI even if RC has been lost.
// The only way to detect RC loss is therefore to look at the lost_count.
if (rc_updated) {
if (lost_count == 0) {
// we have a new ST24 frame. Publish it.
_rc_in.input_source = input_rc_s::RC_INPUT_SOURCE_PX4FMU_ST24;
fill_rc_in(_raw_rc_count, _raw_rc_values, cycle_timestamp,
false, false, frame_drops, st24_rssi);
_rc_scan_locked = true;
} else {
// if the lost count > 0 means that there is an RC loss
_rc_in.rc_lost = true;
}
}
}
}
perf_end(_cycle_perf);
if (rc_updated) {
perf_count(_publish_interval_perf);
_to_input_rc.publish(_rc_in);
} else if (!rc_updated && (hrt_elapsed_time(&_rc_in.timestamp_last_signal) > 1_s)) {
_rc_scan_locked = false;
}
if (!rc_scan_locked && _rc_scan_locked) {
PX4_INFO("RC input locked");
}
}
}
int RcSt24::custom_command(int argc, char *argv[])
{
if (!is_running()) {
int ret = RcSt24::task_spawn(argc, argv);
if (ret) {
return ret;
}
}
return print_usage("unknown command");
}
int RcSt24::print_status()
{
PX4_INFO("Max update rate: %u Hz", 1000000 / _current_update_interval);
if (_device[0] != '\0') {
PX4_INFO("UART device: %s", _device);
PX4_INFO("UART RX bytes: %" PRIu32, _bytes_rx);
}
PX4_INFO("RC state: %s", _rc_scan_locked ? "found" : "searching for signal");
perf_print_counter(_cycle_perf);
perf_print_counter(_publish_interval_perf);
if (hrt_elapsed_time(&_rc_in.timestamp) < 1_s) {
print_message(ORB_ID(input_rc), _rc_in);
}
return 0;
}
int RcSt24::print_usage(const char *reason)
{
if (reason) {
PX4_WARN("%s\n", reason);
}
PRINT_MODULE_DESCRIPTION(
R"DESCR_STR(
### Description
This module does the RC input parsing and auto-selecting the method. Supported methods are:
- ST24
)DESCR_STR");
PRINT_MODULE_USAGE_NAME("rc_st24", "driver");
PRINT_MODULE_USAGE_COMMAND("start");
PRINT_MODULE_USAGE_PARAM_STRING('d', "/dev/ttyS3", "<file:dev>", "RC device", false);
PRINT_MODULE_USAGE_DEFAULT_COMMANDS();
return 0;
}
extern "C" __EXPORT int rc_st24_main(int argc, char *argv[])
{
return RcSt24::main(argc, argv);
}
+106
View File
@@ -0,0 +1,106 @@
/****************************************************************************
*
* Copyright (c) 2012-2022 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 <float.h>
#include <board_config.h>
#include <drivers/drv_hrt.h>
#include <lib/perf/perf_counter.h>
#include <lib/rc/st24.h>
#include <px4_platform_common/px4_config.h>
#include <px4_platform_common/getopt.h>
#include <px4_platform_common/log.h>
#include <px4_platform_common/module.h>
#include <px4_platform_common/module_params.h>
#include <px4_platform_common/px4_work_queue/ScheduledWorkItem.hpp>
#include <uORB/PublicationMulti.hpp>
#include <uORB/Subscription.hpp>
#include <uORB/SubscriptionInterval.hpp>
#include <uORB/topics/input_rc.h>
class RcSt24 : public ModuleBase<RcSt24>, public ModuleParams, public px4::ScheduledWorkItem
{
public:
RcSt24(const char *device);
virtual ~RcSt24();
/** @see ModuleBase */
static int task_spawn(int argc, char *argv[]);
/** @see ModuleBase */
static int custom_command(int argc, char *argv[]);
/** @see ModuleBase */
static int print_usage(const char *reason = nullptr);
/** @see ModuleBase::print_status() */
int print_status() override;
int init();
private:
void Run() override;
void fill_rc_in(uint16_t raw_rc_count_local,
uint16_t raw_rc_values_local[input_rc_s::RC_INPUT_MAX_CHANNELS],
hrt_abstime now, bool frame_drop, bool failsafe,
unsigned frame_drops, int rssi);
void rc_io_invert(bool invert);
hrt_abstime _rc_scan_begin{0};
bool _initialized{false};
bool _rc_scan_locked{false};
static constexpr unsigned _current_update_interval{4000}; // 250 Hz
input_rc_s _rc_in{};
uORB::PublicationMulti<input_rc_s> _to_input_rc{ORB_ID(input_rc)};
int _rcs_fd{-1};
char _device[20] {}; ///< device / serial port path
static constexpr size_t RC_MAX_BUFFER_SIZE{sizeof(ReceiverFcPacket)};
uint8_t _rcs_buf[RC_MAX_BUFFER_SIZE] {};
uint16_t _raw_rc_values[input_rc_s::RC_INPUT_MAX_CHANNELS] {};
uint16_t _raw_rc_count{};
perf_counter_t _cycle_perf{perf_alloc(PC_ELAPSED, MODULE_NAME": cycle time")};
perf_counter_t _publish_interval_perf{perf_alloc(PC_INTERVAL, MODULE_NAME": publish interval")};
uint32_t _bytes_rx{0};
};
+7
View File
@@ -0,0 +1,7 @@
module_name: RC ST24 input driver
serial_config:
- command: "rc_st24 start -d ${SERIAL_DEV}"
port_config_param:
name: RC_ST24_PRT_CFG
group: Serial
#default: RC
+45
View File
@@ -0,0 +1,45 @@
############################################################################
#
# Copyright (c) 2015-2022 PX4 Development Team. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# 3. Neither the name PX4 nor the names of its contributors may be
# used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
############################################################################
px4_add_module(
MODULE drivers__rc__sumd
MAIN rc_sumd
COMPILE_FLAGS
SRCS
RCInput.cpp
RCInput.hpp
MODULE_CONFIG
module.yaml
DEPENDS
rc_sumd
px4_work_queue
)
+5
View File
@@ -0,0 +1,5 @@
menuconfig DRIVERS_RC_SUMD_RC
bool "rc_sumd"
default n
---help---
Enable support for rc_sumd
+371
View File
@@ -0,0 +1,371 @@
/****************************************************************************
*
* Copyright (c) 2012-2022 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 "RCInput.hpp"
#include <termios.h>
using namespace time_literals;
RcSumd::RcSumd(const char *device) :
ModuleParams(nullptr),
ScheduledWorkItem(MODULE_NAME, px4::serial_port_to_wq(device))
{
// initialize raw_rc values and count
for (unsigned i = 0; i < input_rc_s::RC_INPUT_MAX_CHANNELS; i++) {
_raw_rc_values[i] = UINT16_MAX;
}
if (device) {
strncpy(_device, device, sizeof(_device) - 1);
_device[sizeof(_device) - 1] = '\0';
}
}
RcSumd::~RcSumd()
{
perf_free(_cycle_perf);
perf_free(_publish_interval_perf);
}
int RcSumd::init()
{
_rcs_fd = ::open(_device, O_RDWR | O_NONBLOCK);
if (_rcs_fd >= 0) {
// 115200bps, no parity, one stop bit
struct termios t {};
tcgetattr(_rcs_fd, &t);
cfsetspeed(&t, 115200);
t.c_cflag &= ~(CSTOPB | PARENB);
tcsetattr(_rcs_fd, TCSANOW, &t);
}
if (_rcs_fd < 0) {
return -errno;
}
if (board_rc_swap_rxtx(_device)) {
#if defined(TIOCSSWAP)
ioctl(_rcs_fd, TIOCSSWAP, SER_SWAP_ENABLED);
#endif // TIOCSSWAP
}
rc_io_invert(false);
return 0;
}
int RcSumd::task_spawn(int argc, char *argv[])
{
bool error_flag = false;
int myoptind = 1;
int ch;
const char *myoptarg = nullptr;
const char *device_name = nullptr;
#if defined(RC_SERIAL_PORT)
device_name = RC_SERIAL_PORT;
#endif // RC_SERIAL_PORT
while ((ch = px4_getopt(argc, argv, "d:", &myoptind, &myoptarg)) != EOF) {
switch (ch) {
case 'd':
device_name = myoptarg;
break;
case '?':
error_flag = true;
break;
default:
PX4_WARN("unrecognized flag");
error_flag = true;
break;
}
}
if (error_flag) {
return -1;
}
if (device_name && (access(device_name, R_OK | W_OK) == 0)) {
RcSumd *instance = new RcSumd(device_name);
if (instance == nullptr) {
PX4_ERR("alloc failed");
return PX4_ERROR;
}
_object.store(instance);
_task_id = task_id_is_work_queue;
instance->ScheduleOnInterval(_current_update_interval);
return PX4_OK;
} else {
if (device_name) {
PX4_ERR("invalid device (-d) %s", device_name);
} else {
PX4_INFO("valid device required");
}
}
return PX4_ERROR;
}
void RcSumd::fill_rc_in(uint16_t raw_rc_count_local,
uint16_t raw_rc_values_local[input_rc_s::RC_INPUT_MAX_CHANNELS],
hrt_abstime now, bool frame_drop, bool failsafe,
unsigned frame_drops, int rssi = -1)
{
// fill rc_in struct for publishing
_rc_in.channel_count = raw_rc_count_local;
if (_rc_in.channel_count > input_rc_s::RC_INPUT_MAX_CHANNELS) {
_rc_in.channel_count = input_rc_s::RC_INPUT_MAX_CHANNELS;
}
unsigned valid_chans = 0;
for (unsigned i = 0; i < _rc_in.channel_count; i++) {
_rc_in.values[i] = raw_rc_values_local[i];
if (raw_rc_values_local[i] != UINT16_MAX) {
valid_chans++;
}
// once filled, reset values back to default
_raw_rc_values[i] = UINT16_MAX;
}
_rc_in.timestamp = now;
_rc_in.timestamp_last_signal = _rc_in.timestamp;
_rc_in.rc_ppm_frame_length = 0;
if (valid_chans == 0) {
_rc_in.rssi = 0;
}
_rc_in.rc_failsafe = failsafe;
_rc_in.rc_lost = (valid_chans == 0);
_rc_in.rc_lost_frame_count = frame_drops;
_rc_in.rc_total_frame_count = 0;
}
void RcSumd::rc_io_invert(bool invert)
{
// First check if the board provides a board-specific inversion method (e.g. via GPIO),
// and if not use an IOCTL
if (!board_rc_invert_input(_device, invert)) {
#if defined(TIOCSINVERT)
if (invert) {
ioctl(_rcs_fd, TIOCSINVERT, SER_INVERT_ENABLED_RX | SER_INVERT_ENABLED_TX);
} else {
ioctl(_rcs_fd, TIOCSINVERT, 0);
}
#endif // TIOCSINVERT
}
}
void RcSumd::Run()
{
if (should_exit()) {
exit_and_cleanup();
return;
}
if (!_initialized) {
if (init() == PX4_OK) {
_initialized = true;
} else {
PX4_ERR("init failed");
exit_and_cleanup();
}
} else {
perf_begin(_cycle_perf);
const hrt_abstime cycle_timestamp = hrt_absolute_time();
bool rc_updated = false;
// This block scans for a supported serial RC input and locks onto the first one found
// Scan for 500 msec, then switch protocol
constexpr hrt_abstime rc_scan_max = 500_ms;
unsigned frame_drops = 0;
// TODO: needs work (poll _rcs_fd)
// int ret = poll(fds, sizeof(fds) / sizeof(fds[0]), 100);
// then update priority to SCHED_PRIORITY_FAST_DRIVER
// read all available data from the serial RC input UART
// read all available data from the serial RC input UART
int newBytes = ::read(_rcs_fd, &_rcs_buf[0], RC_MAX_BUFFER_SIZE);
if (newBytes > 0) {
_bytes_rx += newBytes;
}
const bool rc_scan_locked = _rc_scan_locked;
if (_rc_scan_begin == 0) {
_rc_scan_begin = cycle_timestamp;
// Configure serial port for DSM
if (_rcs_fd >= 0) {
// 115200bps, no parity, one stop bit
struct termios t {};
tcgetattr(_rcs_fd, &t);
cfsetspeed(&t, 115200);
t.c_cflag &= ~(CSTOPB | PARENB);
tcsetattr(_rcs_fd, TCSANOW, &t);
}
// flush serial buffer and any existing buffered data
tcflush(_rcs_fd, TCIOFLUSH);
memset(_rcs_buf, 0, sizeof(_rcs_buf));
} else if (_rc_scan_locked
|| cycle_timestamp - _rc_scan_begin < rc_scan_max) {
if (newBytes > 0) {
// parse new data
uint8_t sumd_rssi, rx_count;
bool sumd_failsafe;
rc_updated = false;
for (unsigned i = 0; i < (unsigned)newBytes; i++) {
/* set updated flag if one complete packet was parsed */
sumd_rssi = input_rc_s::RSSI_MAX;
rc_updated = (OK == sumd_decode(_rcs_buf[i], &sumd_rssi, &rx_count,
&_raw_rc_count, _raw_rc_values, input_rc_s::RC_INPUT_MAX_CHANNELS, &sumd_failsafe));
}
if (rc_updated) {
// we have a new SUMD frame. Publish it.
_rc_in.input_source = input_rc_s::RC_INPUT_SOURCE_PX4FMU_SUMD;
fill_rc_in(_raw_rc_count, _raw_rc_values, cycle_timestamp,
false, sumd_failsafe, frame_drops, sumd_rssi);
_rc_scan_locked = true;
}
}
}
perf_end(_cycle_perf);
if (rc_updated) {
perf_count(_publish_interval_perf);
_to_input_rc.publish(_rc_in);
} else if (!rc_updated && (hrt_elapsed_time(&_rc_in.timestamp_last_signal) > 1_s)) {
_rc_scan_locked = false;
}
if (!rc_scan_locked && _rc_scan_locked) {
PX4_INFO("RC input locked");
}
}
}
int RcSumd::custom_command(int argc, char *argv[])
{
if (!is_running()) {
int ret = RcSumd::task_spawn(argc, argv);
if (ret) {
return ret;
}
}
return print_usage("unknown command");
}
int RcSumd::print_status()
{
PX4_INFO("Max update rate: %u Hz", 1000000 / _current_update_interval);
if (_device[0] != '\0') {
PX4_INFO("UART device: %s", _device);
PX4_INFO("UART RX bytes: %" PRIu32, _bytes_rx);
}
PX4_INFO("RC state: %s", _rc_scan_locked ? "found" : "searching for signal");
perf_print_counter(_cycle_perf);
perf_print_counter(_publish_interval_perf);
if (hrt_elapsed_time(&_rc_in.timestamp) < 1_s) {
print_message(ORB_ID(input_rc), _rc_in);
}
return 0;
}
int RcSumd::print_usage(const char *reason)
{
if (reason) {
PX4_WARN("%s\n", reason);
}
PRINT_MODULE_DESCRIPTION(
R"DESCR_STR(
### Description
This module does the RC input parsing and auto-selecting the method. Supported methods are:
- SUMD
)DESCR_STR");
PRINT_MODULE_USAGE_NAME("rc_sumd", "driver");
PRINT_MODULE_USAGE_COMMAND("start");
PRINT_MODULE_USAGE_PARAM_STRING('d', "/dev/ttyS3", "<file:dev>", "RC device", false);
PRINT_MODULE_USAGE_DEFAULT_COMMANDS();
return 0;
}
extern "C" __EXPORT int rc_sumd_main(int argc, char *argv[])
{
return RcSumd::main(argc, argv);
}
+107
View File
@@ -0,0 +1,107 @@
/****************************************************************************
*
* Copyright (c) 2012-2022 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 <float.h>
#include <board_config.h>
#include <drivers/drv_hrt.h>
#include <lib/perf/perf_counter.h>
#include <lib/rc/sumd.h>
#include <px4_platform_common/px4_config.h>
#include <px4_platform_common/getopt.h>
#include <px4_platform_common/log.h>
#include <px4_platform_common/module.h>
#include <px4_platform_common/module_params.h>
#include <px4_platform_common/px4_work_queue/ScheduledWorkItem.hpp>
#include <uORB/PublicationMulti.hpp>
#include <uORB/Subscription.hpp>
#include <uORB/SubscriptionInterval.hpp>
#include <uORB/topics/adc_report.h>
#include <uORB/topics/input_rc.h>
class RcSumd : public ModuleBase<RcSumd>, public ModuleParams, public px4::ScheduledWorkItem
{
public:
RcSumd(const char *device);
virtual ~RcSumd();
/** @see ModuleBase */
static int task_spawn(int argc, char *argv[]);
/** @see ModuleBase */
static int custom_command(int argc, char *argv[]);
/** @see ModuleBase */
static int print_usage(const char *reason = nullptr);
/** @see ModuleBase::print_status() */
int print_status() override;
int init();
private:
void Run() override;
void fill_rc_in(uint16_t raw_rc_count_local,
uint16_t raw_rc_values_local[input_rc_s::RC_INPUT_MAX_CHANNELS],
hrt_abstime now, bool frame_drop, bool failsafe,
unsigned frame_drops, int rssi);
void rc_io_invert(bool invert);
hrt_abstime _rc_scan_begin{0};
bool _initialized{false};
bool _rc_scan_locked{false};
static constexpr unsigned _current_update_interval{4000}; // 250 Hz
input_rc_s _rc_in{};
uORB::PublicationMulti<input_rc_s> _to_input_rc{ORB_ID(input_rc)};
int _rcs_fd{-1};
char _device[20] {}; ///< device / serial port path
static constexpr size_t RC_MAX_BUFFER_SIZE{sizeof(ReceiverFcPacketHoTT)};
uint8_t _rcs_buf[RC_MAX_BUFFER_SIZE] {};
uint16_t _raw_rc_values[input_rc_s::RC_INPUT_MAX_CHANNELS] {};
uint16_t _raw_rc_count{};
perf_counter_t _cycle_perf{perf_alloc(PC_ELAPSED, MODULE_NAME": cycle time")};
perf_counter_t _publish_interval_perf{perf_alloc(PC_INTERVAL, MODULE_NAME": publish interval")};
uint32_t _bytes_rx{0};
};
+7
View File
@@ -0,0 +1,7 @@
module_name: RC SUMD input driver
serial_config:
- command: "rc_sumd start -d ${SERIAL_DEV}"
port_config_param:
name: RC_SUMD_PRT_CFG
group: Serial
#default: RC
-5
View File
@@ -1,5 +0,0 @@
menuconfig DRIVERS_RC_INPUT
bool "rc_input"
default n
---help---
Enable support for rc_input
-953
View File
@@ -1,953 +0,0 @@
/****************************************************************************
*
* Copyright (c) 2012-2022 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 "RCInput.hpp"
#include "crsf_telemetry.h"
#include <uORB/topics/vehicle_command_ack.h>
#include <termios.h>
using namespace time_literals;
constexpr char const *RCInput::RC_SCAN_STRING[];
RCInput::RCInput(const char *device) :
ModuleParams(nullptr),
ScheduledWorkItem(MODULE_NAME, px4::serial_port_to_wq(device)),
_cycle_perf(perf_alloc(PC_ELAPSED, MODULE_NAME": cycle time")),
_publish_interval_perf(perf_alloc(PC_INTERVAL, MODULE_NAME": publish interval"))
{
// initialize raw_rc values and count
for (unsigned i = 0; i < input_rc_s::RC_INPUT_MAX_CHANNELS; i++) {
_raw_rc_values[i] = UINT16_MAX;
}
if (device) {
strncpy(_device, device, sizeof(_device) - 1);
_device[sizeof(_device) - 1] = '\0';
}
if ((_param_rc_input_proto.get() >= 0) && (_param_rc_input_proto.get() <= RC_SCAN::RC_SCAN_GHST)) {
_rc_scan_state = static_cast<RC_SCAN>(_param_rc_input_proto.get());
}
}
RCInput::~RCInput()
{
#if defined(SPEKTRUM_POWER_PASSIVE)
// Disable power controls for Spektrum receiver
SPEKTRUM_POWER_PASSIVE();
#endif
dsm_deinit();
delete _crsf_telemetry;
delete _ghst_telemetry;
perf_free(_cycle_perf);
perf_free(_publish_interval_perf);
}
int
RCInput::init()
{
#ifdef RF_RADIO_POWER_CONTROL
// power radio on
RF_RADIO_POWER_CONTROL(true);
#endif // RF_RADIO_POWER_CONTROL
// dsm_init sets some file static variables and returns a file descriptor
// it also powers on the radio if needed
_rcs_fd = dsm_init(_device);
if (_rcs_fd < 0) {
return -errno;
}
if (board_rc_swap_rxtx(_device)) {
#if defined(TIOCSSWAP)
ioctl(_rcs_fd, TIOCSSWAP, SER_SWAP_ENABLED);
#endif // TIOCSSWAP
}
// assume SBUS input and immediately switch it to
// so that if Single wire mode on TX there will be only
// a short contention
sbus_config(_rcs_fd, board_rc_singlewire(_device));
#ifdef GPIO_PPM_IN
// disable CPPM input by mapping it away from the timer capture input
px4_arch_unconfiggpio(GPIO_PPM_IN);
#endif // GPIO_PPM_IN
rc_io_invert(false);
return 0;
}
int
RCInput::task_spawn(int argc, char *argv[])
{
bool error_flag = false;
int myoptind = 1;
int ch;
const char *myoptarg = nullptr;
const char *device_name = nullptr;
#if defined(RC_SERIAL_PORT)
device_name = RC_SERIAL_PORT;
#endif // RC_SERIAL_PORT
#if defined(RC_SERIAL_PORT) && defined(PX4IO_SERIAL_DEVICE)
// if RC_SERIAL_PORT == PX4IO_SERIAL_DEVICE then don't use it by default if the px4io is running
if ((strcmp(RC_SERIAL_PORT, PX4IO_SERIAL_DEVICE) == 0) && (access("/dev/px4io", R_OK) == 0)) {
device_name = nullptr;
}
#endif // RC_SERIAL_PORT && PX4IO_SERIAL_DEVICE
while ((ch = px4_getopt(argc, argv, "d:", &myoptind, &myoptarg)) != EOF) {
switch (ch) {
case 'd':
device_name = myoptarg;
break;
case '?':
error_flag = true;
break;
default:
PX4_WARN("unrecognized flag");
error_flag = true;
break;
}
}
if (error_flag) {
return -1;
}
if (device_name && (access(device_name, R_OK | W_OK) == 0)) {
RCInput *instance = new RCInput(device_name);
if (instance == nullptr) {
PX4_ERR("alloc failed");
return PX4_ERROR;
}
_object.store(instance);
_task_id = task_id_is_work_queue;
instance->ScheduleOnInterval(_current_update_interval);
return PX4_OK;
} else {
if (device_name) {
PX4_ERR("invalid device (-d) %s", device_name);
} else {
PX4_INFO("valid device required");
}
}
return PX4_ERROR;
}
void
RCInput::fill_rc_in(uint16_t raw_rc_count_local,
uint16_t raw_rc_values_local[input_rc_s::RC_INPUT_MAX_CHANNELS],
hrt_abstime now, bool frame_drop, bool failsafe,
unsigned frame_drops, int rssi = -1)
{
// fill rc_in struct for publishing
_rc_in.channel_count = raw_rc_count_local;
if (_rc_in.channel_count > input_rc_s::RC_INPUT_MAX_CHANNELS) {
_rc_in.channel_count = input_rc_s::RC_INPUT_MAX_CHANNELS;
}
unsigned valid_chans = 0;
for (unsigned i = 0; i < _rc_in.channel_count; i++) {
_rc_in.values[i] = raw_rc_values_local[i];
if (raw_rc_values_local[i] != UINT16_MAX) {
valid_chans++;
}
// once filled, reset values back to default
_raw_rc_values[i] = UINT16_MAX;
}
_rc_in.timestamp = now;
_rc_in.timestamp_last_signal = _rc_in.timestamp;
_rc_in.rc_ppm_frame_length = 0;
/* fake rssi if no value was provided */
if (rssi == -1) {
if ((_param_rc_rssi_pwm_chan.get() > 0) && (_param_rc_rssi_pwm_chan.get() < _rc_in.channel_count)) {
const int32_t rssi_pwm_chan = _param_rc_rssi_pwm_chan.get();
const int32_t rssi_pwm_min = _param_rc_rssi_pwm_min.get();
const int32_t rssi_pwm_max = _param_rc_rssi_pwm_max.get();
// get RSSI from input channel
int rc_rssi = ((_rc_in.values[rssi_pwm_chan - 1] - rssi_pwm_min) * 100) / (rssi_pwm_max - rssi_pwm_min);
_rc_in.rssi = math::constrain(rc_rssi, 0, 100);
} else if (_analog_rc_rssi_stable) {
// set RSSI if analog RSSI input is present
float rssi_analog = ((_analog_rc_rssi_volt - 0.2f) / 3.0f) * 100.0f;
if (rssi_analog > 100.0f) {
rssi_analog = 100.0f;
}
if (rssi_analog < 0.0f) {
rssi_analog = 0.0f;
}
_rc_in.rssi = rssi_analog;
} else {
_rc_in.rssi = 255;
}
} else {
_rc_in.rssi = rssi;
}
if (valid_chans == 0) {
_rc_in.rssi = 0;
}
_rc_in.rc_failsafe = failsafe;
_rc_in.rc_lost = (valid_chans == 0);
_rc_in.rc_lost_frame_count = frame_drops;
_rc_in.rc_total_frame_count = 0;
}
void RCInput::set_rc_scan_state(RC_SCAN newState)
{
if ((_param_rc_input_proto.get() > RC_SCAN::RC_SCAN_NONE)
&& (_param_rc_input_proto.get() <= RC_SCAN::RC_SCAN_GHST)) {
_rc_scan_state = static_cast<RC_SCAN>(_param_rc_input_proto.get());
} else if (_param_rc_input_proto.get() < 0) {
// only auto change if RC_INPUT_PROTO set to auto (-1)
PX4_DEBUG("RCscan: %s failed, trying %s", RCInput::RC_SCAN_STRING[_rc_scan_state], RCInput::RC_SCAN_STRING[newState]);
_rc_scan_state = newState;
} else {
_rc_scan_state = RC_SCAN::RC_SCAN_NONE;
}
_rc_scan_begin = 0;
_rc_scan_locked = false;
}
void RCInput::rc_io_invert(bool invert)
{
// First check if the board provides a board-specific inversion method (e.g. via GPIO),
// and if not use an IOCTL
if (!board_rc_invert_input(_device, invert)) {
#if defined(TIOCSINVERT)
if (invert) {
ioctl(_rcs_fd, TIOCSINVERT, SER_INVERT_ENABLED_RX | SER_INVERT_ENABLED_TX);
} else {
ioctl(_rcs_fd, TIOCSINVERT, 0);
}
#endif // TIOCSINVERT
}
}
void RCInput::Run()
{
if (should_exit()) {
exit_and_cleanup();
return;
}
if (!_initialized) {
if (init() == PX4_OK) {
_initialized = true;
} else {
PX4_ERR("init failed");
exit_and_cleanup();
}
} else {
perf_begin(_cycle_perf);
// Check if parameters have changed
if (_parameter_update_sub.updated()) {
// clear update
parameter_update_s param_update;
_parameter_update_sub.copy(&param_update);
updateParams();
}
if (_vehicle_status_sub.updated()) {
vehicle_status_s vehicle_status;
if (_vehicle_status_sub.copy(&vehicle_status)) {
_armed = (vehicle_status.arming_state == vehicle_status_s::ARMING_STATE_ARMED);
}
}
const hrt_abstime cycle_timestamp = hrt_absolute_time();
/* vehicle command */
vehicle_command_s vcmd;
if (_vehicle_cmd_sub.update(&vcmd)) {
// Check for a pairing command
if (vcmd.command == vehicle_command_s::VEHICLE_CMD_START_RX_PAIR) {
uint8_t cmd_ret = vehicle_command_ack_s::VEHICLE_CMD_RESULT_UNSUPPORTED;
#if defined(SPEKTRUM_POWER)
if (!_rc_scan_locked && !_armed) {
if ((int)vcmd.param1 == 0) {
// DSM binding command
int dsm_bind_mode = (int)vcmd.param2;
int dsm_bind_pulses = 0;
if (dsm_bind_mode == 0) {
dsm_bind_pulses = DSM2_BIND_PULSES;
} else if (dsm_bind_mode == 1) {
dsm_bind_pulses = DSMX_BIND_PULSES;
} else {
dsm_bind_pulses = DSMX8_BIND_PULSES;
}
bind_spektrum(dsm_bind_pulses);
cmd_ret = vehicle_command_ack_s::VEHICLE_CMD_RESULT_ACCEPTED;
}
} else {
cmd_ret = vehicle_command_ack_s::VEHICLE_CMD_RESULT_TEMPORARILY_REJECTED;
}
#endif // SPEKTRUM_POWER
// publish acknowledgement
vehicle_command_ack_s command_ack{};
command_ack.command = vcmd.command;
command_ack.result = cmd_ret;
command_ack.target_system = vcmd.source_system;
command_ack.target_component = vcmd.source_component;
command_ack.timestamp = hrt_absolute_time();
uORB::Publication<vehicle_command_ack_s> vehicle_command_ack_pub{ORB_ID(vehicle_command_ack)};
vehicle_command_ack_pub.publish(command_ack);
}
}
#if defined(ADC_RC_RSSI_CHANNEL)
// update ADC sampling
if (_adc_report_sub.updated()) {
adc_report_s adc;
if (_adc_report_sub.copy(&adc)) {
for (unsigned i = 0; i < PX4_MAX_ADC_CHANNELS; ++i) {
if (adc.channel_id[i] == ADC_RC_RSSI_CHANNEL) {
float adc_volt = adc.raw_data[i] *
adc.v_ref /
adc.resolution;
if (_analog_rc_rssi_volt < 0.0f) {
_analog_rc_rssi_volt = adc_volt;
}
_analog_rc_rssi_volt = _analog_rc_rssi_volt * 0.995f + adc_volt * 0.005f;
/* only allow this to be used if we see a high RSSI once */
if (_analog_rc_rssi_volt > 2.5f) {
_analog_rc_rssi_stable = true;
}
}
}
}
}
#endif // ADC_RC_RSSI_CHANNEL
bool rc_updated = false;
// This block scans for a supported serial RC input and locks onto the first one found
// Scan for 500 msec, then switch protocol
constexpr hrt_abstime rc_scan_max = 500_ms;
unsigned frame_drops = 0;
// TODO: needs work (poll _rcs_fd)
// int ret = poll(fds, sizeof(fds) / sizeof(fds[0]), 100);
// then update priority to SCHED_PRIORITY_FAST_DRIVER
// read all available data from the serial RC input UART
// read all available data from the serial RC input UART
int newBytes = ::read(_rcs_fd, &_rcs_buf[0], RC_MAX_BUFFER_SIZE);
if (newBytes > 0) {
_bytes_rx += newBytes;
}
const bool rc_scan_locked = _rc_scan_locked;
switch (_rc_scan_state) {
case RC_SCAN_NONE:
// do nothing
break;
case RC_SCAN_SBUS:
if (_rc_scan_begin == 0) {
_rc_scan_begin = cycle_timestamp;
// Configure serial port for SBUS
sbus_config(_rcs_fd, board_rc_singlewire(_device));
rc_io_invert(true);
// flush serial buffer and any existing buffered data
tcflush(_rcs_fd, TCIOFLUSH);
memset(_rcs_buf, 0, sizeof(_rcs_buf));
} else if (_rc_scan_locked
|| cycle_timestamp - _rc_scan_begin < rc_scan_max) {
// parse new data
if (newBytes > 0) {
bool sbus_failsafe = false;
bool sbus_frame_drop = false;
rc_updated = sbus_parse(cycle_timestamp, &_rcs_buf[0], newBytes, &_raw_rc_values[0], &_raw_rc_count, &sbus_failsafe,
&sbus_frame_drop, &frame_drops, input_rc_s::RC_INPUT_MAX_CHANNELS);
if (rc_updated) {
// we have a new SBUS frame. Publish it.
_rc_in.input_source = input_rc_s::RC_INPUT_SOURCE_PX4FMU_SBUS;
fill_rc_in(_raw_rc_count, _raw_rc_values, cycle_timestamp,
sbus_frame_drop, sbus_failsafe, frame_drops);
_rc_scan_locked = true;
}
}
} else {
// Scan the next protocol
rc_io_invert(false);
set_rc_scan_state(RC_SCAN_DSM);
}
break;
case RC_SCAN_DSM:
if (_rc_scan_begin == 0) {
_rc_scan_begin = cycle_timestamp;
// Configure serial port for DSM
dsm_config(_rcs_fd);
// flush serial buffer and any existing buffered data
tcflush(_rcs_fd, TCIOFLUSH);
memset(_rcs_buf, 0, sizeof(_rcs_buf));
} else if (_rc_scan_locked
|| cycle_timestamp - _rc_scan_begin < rc_scan_max) {
if (newBytes > 0) {
int8_t dsm_rssi = 0;
bool dsm_11_bit = false;
// parse new data
rc_updated = dsm_parse(cycle_timestamp, &_rcs_buf[0], newBytes, &_raw_rc_values[0], &_raw_rc_count,
&dsm_11_bit, &frame_drops, &dsm_rssi, input_rc_s::RC_INPUT_MAX_CHANNELS);
if (rc_updated) {
// we have a new DSM frame. Publish it.
_rc_in.input_source = input_rc_s::RC_INPUT_SOURCE_PX4FMU_DSM;
fill_rc_in(_raw_rc_count, _raw_rc_values, cycle_timestamp,
false, false, frame_drops, dsm_rssi);
_rc_scan_locked = true;
}
}
} else {
// Scan the next protocol
set_rc_scan_state(RC_SCAN_ST24);
}
break;
case RC_SCAN_ST24:
if (_rc_scan_begin == 0) {
_rc_scan_begin = cycle_timestamp;
// Configure serial port for DSM
dsm_config(_rcs_fd);
// flush serial buffer and any existing buffered data
tcflush(_rcs_fd, TCIOFLUSH);
memset(_rcs_buf, 0, sizeof(_rcs_buf));
} else if (_rc_scan_locked
|| cycle_timestamp - _rc_scan_begin < rc_scan_max) {
if (newBytes > 0) {
// parse new data
uint8_t st24_rssi, lost_count;
rc_updated = false;
for (unsigned i = 0; i < (unsigned)newBytes; i++) {
/* set updated flag if one complete packet was parsed */
st24_rssi = input_rc_s::RSSI_MAX;
rc_updated = (OK == st24_decode(_rcs_buf[i], &st24_rssi, &lost_count,
&_raw_rc_count, _raw_rc_values, input_rc_s::RC_INPUT_MAX_CHANNELS));
}
// The st24 will keep outputting RC channels and RSSI even if RC has been lost.
// The only way to detect RC loss is therefore to look at the lost_count.
if (rc_updated) {
if (lost_count == 0) {
// we have a new ST24 frame. Publish it.
_rc_in.input_source = input_rc_s::RC_INPUT_SOURCE_PX4FMU_ST24;
fill_rc_in(_raw_rc_count, _raw_rc_values, cycle_timestamp,
false, false, frame_drops, st24_rssi);
_rc_scan_locked = true;
} else {
// if the lost count > 0 means that there is an RC loss
_rc_in.rc_lost = true;
}
}
}
} else {
// Scan the next protocol
set_rc_scan_state(RC_SCAN_SUMD);
}
break;
case RC_SCAN_SUMD:
if (_rc_scan_begin == 0) {
_rc_scan_begin = cycle_timestamp;
// Configure serial port for DSM
dsm_config(_rcs_fd);
// flush serial buffer and any existing buffered data
tcflush(_rcs_fd, TCIOFLUSH);
memset(_rcs_buf, 0, sizeof(_rcs_buf));
} else if (_rc_scan_locked
|| cycle_timestamp - _rc_scan_begin < rc_scan_max) {
if (newBytes > 0) {
// parse new data
uint8_t sumd_rssi, rx_count;
bool sumd_failsafe;
rc_updated = false;
for (unsigned i = 0; i < (unsigned)newBytes; i++) {
/* set updated flag if one complete packet was parsed */
sumd_rssi = input_rc_s::RSSI_MAX;
rc_updated = (OK == sumd_decode(_rcs_buf[i], &sumd_rssi, &rx_count,
&_raw_rc_count, _raw_rc_values, input_rc_s::RC_INPUT_MAX_CHANNELS, &sumd_failsafe));
}
if (rc_updated) {
// we have a new SUMD frame. Publish it.
_rc_in.input_source = input_rc_s::RC_INPUT_SOURCE_PX4FMU_SUMD;
fill_rc_in(_raw_rc_count, _raw_rc_values, cycle_timestamp,
false, sumd_failsafe, frame_drops, sumd_rssi);
_rc_scan_locked = true;
}
}
} else {
// Scan the next protocol
set_rc_scan_state(RC_SCAN_PPM);
}
break;
case RC_SCAN_PPM:
// skip PPM if it's not supported
#ifdef HRT_PPM_CHANNEL
if (_rc_scan_begin == 0) {
_rc_scan_begin = cycle_timestamp;
// Configure timer input pin for CPPM
px4_arch_configgpio(GPIO_PPM_IN);
} else if (_rc_scan_locked || cycle_timestamp - _rc_scan_begin < rc_scan_max) {
// see if we have new PPM input data
if ((ppm_last_valid_decode != _rc_in.timestamp_last_signal) && ppm_decoded_channels > 3) {
// we have a new PPM frame. Publish it.
rc_updated = true;
_rc_in.input_source = input_rc_s::RC_INPUT_SOURCE_PX4FMU_PPM;
fill_rc_in(ppm_decoded_channels, ppm_buffer, cycle_timestamp, false, false, 0);
_rc_scan_locked = true;
_rc_in.rc_ppm_frame_length = ppm_frame_length;
_rc_in.timestamp_last_signal = ppm_last_valid_decode;
}
} else {
// disable CPPM input by mapping it away from the timer capture input
px4_arch_unconfiggpio(GPIO_PPM_IN);
// Scan the next protocol
set_rc_scan_state(RC_SCAN_CRSF);
}
#else // skip PPM if it's not supported
set_rc_scan_state(RC_SCAN_CRSF);
#endif // HRT_PPM_CHANNEL
break;
case RC_SCAN_CRSF:
if (_rc_scan_begin == 0) {
_rc_scan_begin = cycle_timestamp;
// Configure serial port for CRSF
crsf_config(_rcs_fd);
// flush serial buffer and any existing buffered data
tcflush(_rcs_fd, TCIOFLUSH);
memset(_rcs_buf, 0, sizeof(_rcs_buf));
} else if (_rc_scan_locked
|| cycle_timestamp - _rc_scan_begin < rc_scan_max) {
// parse new data
if (newBytes > 0) {
rc_updated = crsf_parse(cycle_timestamp, &_rcs_buf[0], newBytes, &_raw_rc_values[0], &_raw_rc_count,
input_rc_s::RC_INPUT_MAX_CHANNELS);
if (rc_updated) {
// we have a new CRSF frame. Publish it.
_rc_in.input_source = input_rc_s::RC_INPUT_SOURCE_PX4FMU_CRSF;
fill_rc_in(_raw_rc_count, _raw_rc_values, cycle_timestamp, false, false, 0);
// on Pixhawk (-related) boards we cannot write to the RC UART
// another option is to use a different UART port
#ifdef BOARD_SUPPORTS_RC_SERIAL_PORT_OUTPUT
if (!_rc_scan_locked && !_crsf_telemetry) {
_crsf_telemetry = new CRSFTelemetry(_rcs_fd);
}
#endif /* BOARD_SUPPORTS_RC_SERIAL_PORT_OUTPUT */
_rc_scan_locked = true;
if (_crsf_telemetry) {
_crsf_telemetry->update(cycle_timestamp);
}
}
}
} else {
// Scan the next protocol
set_rc_scan_state(RC_SCAN_GHST);
}
break;
case RC_SCAN_GHST:
if (_rc_scan_begin == 0) {
_rc_scan_begin = cycle_timestamp;
// Configure serial port for GHST
ghst_config(_rcs_fd);
// flush serial buffer and any existing buffered data
tcflush(_rcs_fd, TCIOFLUSH);
memset(_rcs_buf, 0, sizeof(_rcs_buf));
} else if (_rc_scan_locked
|| cycle_timestamp - _rc_scan_begin < rc_scan_max) {
// parse new data
if (newBytes > 0) {
int8_t ghst_rssi = -1;
rc_updated = ghst_parse(cycle_timestamp, &_rcs_buf[0], newBytes, &_raw_rc_values[0], &ghst_rssi,
&_raw_rc_count, input_rc_s::RC_INPUT_MAX_CHANNELS);
if (rc_updated) {
// we have a new GHST frame. Publish it.
_rc_in.input_source = input_rc_s::RC_INPUT_SOURCE_PX4FMU_GHST;
fill_rc_in(_raw_rc_count, _raw_rc_values, cycle_timestamp, false, false, 0, ghst_rssi);
// ghst telemetry works on fmu-v5
// on other Pixhawk (-related) boards we cannot write to the RC UART
// another option is to use a different UART port
#ifdef BOARD_SUPPORTS_RC_SERIAL_PORT_OUTPUT
if (!_rc_scan_locked && !_ghst_telemetry) {
_ghst_telemetry = new GHSTTelemetry(_rcs_fd);
}
#endif /* BOARD_SUPPORTS_RC_SERIAL_PORT_OUTPUT */
_rc_scan_locked = true;
if (_ghst_telemetry) {
_ghst_telemetry->update(cycle_timestamp);
}
}
}
} else {
// Scan the next protocol
set_rc_scan_state(RC_SCAN_SBUS);
}
break;
}
perf_end(_cycle_perf);
if (rc_updated) {
perf_count(_publish_interval_perf);
_rc_in.link_quality = -1;
_rc_in.rssi_dbm = NAN;
_to_input_rc.publish(_rc_in);
} else if (!rc_updated && !_armed && (hrt_elapsed_time(&_rc_in.timestamp_last_signal) > 1_s)) {
_rc_scan_locked = false;
}
if (!rc_scan_locked && _rc_scan_locked) {
PX4_INFO("RC scan: %s RC input locked", RC_SCAN_STRING[_rc_scan_state]);
}
// set RC_INPUT_PROTO if RC successfully locked for > 3 seconds
if (!_armed && rc_updated && _rc_scan_locked
&& ((_rc_scan_begin != 0) && hrt_elapsed_time(&_rc_scan_begin) > 3_s)
&& (_param_rc_input_proto.get() < 0)
) {
// RC_INPUT_PROTO auto => locked selection
_param_rc_input_proto.set(_rc_scan_state);
_param_rc_input_proto.commit();
}
}
}
#if defined(SPEKTRUM_POWER)
bool RCInput::bind_spektrum(int arg) const
{
int ret = PX4_ERROR;
/* specify 11ms DSMX. RX will automatically fall back to 22ms or DSM2 if necessary */
/* only allow DSM2, DSM-X and DSM-X with more than 7 channels */
PX4_INFO("DSM_BIND_START: DSM%s RX", (arg == 0) ? "2" : ((arg == 1) ? "-X" : "-X8"));
if (arg == DSM2_BIND_PULSES ||
arg == DSMX_BIND_PULSES ||
arg == DSMX8_BIND_PULSES) {
dsm_bind(DSM_CMD_BIND_POWER_DOWN, 0);
dsm_bind(DSM_CMD_BIND_SET_RX_OUT, 0);
usleep(500000);
dsm_bind(DSM_CMD_BIND_POWER_UP, 0);
usleep(72000);
irqstate_t flags = px4_enter_critical_section();
dsm_bind(DSM_CMD_BIND_SEND_PULSES, arg);
px4_leave_critical_section(flags);
usleep(50000);
dsm_bind(DSM_CMD_BIND_REINIT_UART, 0);
ret = OK;
} else {
PX4_ERR("DSM bind failed");
ret = -EINVAL;
}
return (ret == PX4_OK);
}
#endif /* SPEKTRUM_POWER */
int RCInput::custom_command(int argc, char *argv[])
{
#if defined(SPEKTRUM_POWER)
const char *verb = argv[0];
if (!strcmp(verb, "bind")) {
uORB::Publication<vehicle_command_s> vehicle_command_pub{ORB_ID(vehicle_command)};
vehicle_command_s vcmd{};
vcmd.command = vehicle_command_s::VEHICLE_CMD_START_RX_PAIR;
vcmd.timestamp = hrt_absolute_time();
vehicle_command_pub.publish(vcmd);
return 0;
}
#endif /* SPEKTRUM_POWER */
/* start the FMU if not running */
if (!is_running()) {
int ret = RCInput::task_spawn(argc, argv);
if (ret) {
return ret;
}
}
return print_usage("unknown command");
}
int RCInput::print_status()
{
PX4_INFO("Max update rate: %u Hz", 1000000 / _current_update_interval);
if (_device[0] != '\0') {
PX4_INFO("UART device: %s", _device);
PX4_INFO("UART RX bytes: %" PRIu32, _bytes_rx);
}
PX4_INFO("RC state: %s: %s", _rc_scan_locked ? "found" : "searching for signal", RC_SCAN_STRING[_rc_scan_state]);
if (_rc_scan_locked) {
switch (_rc_scan_state) {
case RC_SCAN_NONE:
break;
case RC_SCAN_CRSF:
PX4_INFO("CRSF Telemetry: %s", _crsf_telemetry ? "yes" : "no");
break;
case RC_SCAN_GHST:
PX4_INFO("GHST Telemetry: %s", _ghst_telemetry ? "yes" : "no");
break;
case RC_SCAN_SBUS:
PX4_INFO("SBUS frame drops: %u", sbus_dropped_frames());
break;
case RC_SCAN_DSM:
// DSM status output
#if defined(SPEKTRUM_POWER)
#endif
break;
case RC_SCAN_PPM:
// PPM status output
break;
case RC_SCAN_SUMD:
// SUMD status output
break;
case RC_SCAN_ST24:
// SUMD status output
break;
}
}
#if ADC_RC_RSSI_CHANNEL
if (_analog_rc_rssi_stable) {
PX4_INFO("vrssi: %dmV", (int)(_analog_rc_rssi_volt * 1000.0f));
}
#endif
perf_print_counter(_cycle_perf);
perf_print_counter(_publish_interval_perf);
if (hrt_elapsed_time(&_rc_in.timestamp) < 1_s) {
print_message(ORB_ID(input_rc), _rc_in);
}
return 0;
}
int
RCInput::print_usage(const char *reason)
{
if (reason) {
PX4_WARN("%s\n", reason);
}
PRINT_MODULE_DESCRIPTION(
R"DESCR_STR(
### Description
This module does the RC input parsing and auto-selecting the method. Supported methods are:
- PPM
- SBUS
- DSM
- SUMD
- ST24
- TBS Crossfire (CRSF)
)DESCR_STR");
PRINT_MODULE_USAGE_NAME("rc_input", "driver");
PRINT_MODULE_USAGE_COMMAND("start");
PRINT_MODULE_USAGE_PARAM_STRING('d', "/dev/ttyS3", "<file:dev>", "RC device", true);
#if defined(SPEKTRUM_POWER)
PRINT_MODULE_USAGE_COMMAND_DESCR("bind", "Send a DSM bind command (module must be running)");
#endif /* SPEKTRUM_POWER */
PRINT_MODULE_USAGE_DEFAULT_COMMANDS();
return 0;
}
extern "C" __EXPORT int rc_input_main(int argc, char *argv[])
{
return RCInput::main(argc, argv);
}
-183
View File
@@ -1,183 +0,0 @@
/****************************************************************************
*
* Copyright (c) 2018 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 "crsf_telemetry.h"
#include <lib/rc/crsf.h>
CRSFTelemetry::CRSFTelemetry(int uart_fd) :
_uart_fd(uart_fd)
{
}
bool CRSFTelemetry::update(const hrt_abstime &now)
{
const int update_rate_hz = 10;
if (now - _last_update <= 1_s / (update_rate_hz * num_data_types)) {
return false;
}
bool sent = false;
switch (_next_type) {
case 0:
sent = send_battery();
break;
case 1:
sent = send_gps();
break;
case 2:
sent = send_attitude();
break;
case 3:
sent = send_flight_mode();
break;
}
_last_update = now;
_next_type = (_next_type + 1) % num_data_types;
return sent;
}
bool CRSFTelemetry::send_battery()
{
battery_status_s battery_status;
if (!_battery_status_sub.update(&battery_status)) {
return false;
}
uint16_t voltage = battery_status.voltage_filtered_v * 10;
uint16_t current = battery_status.current_filtered_a * 10;
int fuel = battery_status.discharged_mah;
uint8_t remaining = battery_status.remaining * 100;
return crsf_send_telemetry_battery(_uart_fd, voltage, current, fuel, remaining);
}
bool CRSFTelemetry::send_gps()
{
sensor_gps_s vehicle_gps_position;
if (!_vehicle_gps_position_sub.update(&vehicle_gps_position)) {
return false;
}
int32_t latitude = vehicle_gps_position.lat;
int32_t longitude = vehicle_gps_position.lon;
uint16_t groundspeed = vehicle_gps_position.vel_d_m_s / 3.6f * 10.f;
uint16_t gps_heading = math::degrees(vehicle_gps_position.cog_rad) * 100.f;
uint16_t altitude = vehicle_gps_position.alt + 1000;
uint8_t num_satellites = vehicle_gps_position.satellites_used;
return crsf_send_telemetry_gps(_uart_fd, latitude, longitude, groundspeed,
gps_heading, altitude, num_satellites);
}
bool CRSFTelemetry::send_attitude()
{
vehicle_attitude_s vehicle_attitude;
if (!_vehicle_attitude_sub.update(&vehicle_attitude)) {
return false;
}
matrix::Eulerf attitude = matrix::Quatf(vehicle_attitude.q);
int16_t pitch = attitude(1) * 1e4f;
int16_t roll = attitude(0) * 1e4f;
int16_t yaw = attitude(2) * 1e4f;
return crsf_send_telemetry_attitude(_uart_fd, pitch, roll, yaw);
}
bool CRSFTelemetry::send_flight_mode()
{
vehicle_status_s vehicle_status;
if (!_vehicle_status_sub.update(&vehicle_status)) {
return false;
}
const char *flight_mode = "(unknown)";
switch (vehicle_status.nav_state) {
case vehicle_status_s::NAVIGATION_STATE_MANUAL:
flight_mode = "Manual";
break;
case vehicle_status_s::NAVIGATION_STATE_ALTCTL:
flight_mode = "Altitude";
break;
case vehicle_status_s::NAVIGATION_STATE_POSCTL:
flight_mode = "Position";
break;
case vehicle_status_s::NAVIGATION_STATE_AUTO_RTL:
flight_mode = "Return";
break;
case vehicle_status_s::NAVIGATION_STATE_AUTO_MISSION:
flight_mode = "Mission";
break;
case vehicle_status_s::NAVIGATION_STATE_AUTO_LOITER:
case vehicle_status_s::NAVIGATION_STATE_DESCEND:
case vehicle_status_s::NAVIGATION_STATE_AUTO_TAKEOFF:
case vehicle_status_s::NAVIGATION_STATE_AUTO_LAND:
case vehicle_status_s::NAVIGATION_STATE_AUTO_FOLLOW_TARGET:
case vehicle_status_s::NAVIGATION_STATE_AUTO_PRECLAND:
flight_mode = "Auto";
break;
case vehicle_status_s::NAVIGATION_STATE_ACRO:
flight_mode = "Acro";
break;
case vehicle_status_s::NAVIGATION_STATE_TERMINATION:
flight_mode = "Terminate";
break;
case vehicle_status_s::NAVIGATION_STATE_OFFBOARD:
flight_mode = "Offboard";
break;
case vehicle_status_s::NAVIGATION_STATE_STAB:
flight_mode = "Stabilized";
break;
}
return crsf_send_telemetry_flight_mode(_uart_fd, flight_mode);
}
-92
View File
@@ -1,92 +0,0 @@
/****************************************************************************
*
* Copyright (c) 2018 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/**
* @file crsf_telemetry.h
*
* @author Beat Küng <beat-kueng@gmx.net>
*/
#pragma once
#include <uORB/Subscription.hpp>
#include <uORB/topics/battery_status.h>
#include <uORB/topics/vehicle_attitude.h>
#include <uORB/topics/sensor_gps.h>
#include <uORB/topics/vehicle_status.h>
#include <drivers/drv_hrt.h>
#include <matrix/math.hpp>
#include <mathlib/mathlib.h>
using namespace time_literals;
/**
* High-level class that handles sending of CRSF telemetry data
*/
class CRSFTelemetry
{
public:
/**
* @param uart_fd file descriptor for the UART to use. It is expected to be configured
* already.
*/
CRSFTelemetry(int uart_fd);
~CRSFTelemetry() = default;
/**
* Send telemetry data. Call this regularly (i.e. at 100Hz), it will automatically
* limit the sending rate.
* @return true if new data sent
*/
bool update(const hrt_abstime &now);
private:
bool send_battery();
bool send_gps();
bool send_attitude();
bool send_flight_mode();
uORB::Subscription _vehicle_gps_position_sub{ORB_ID(vehicle_gps_position)};
uORB::Subscription _battery_status_sub{ORB_ID(battery_status)};
uORB::Subscription _vehicle_attitude_sub{ORB_ID(vehicle_attitude)};
uORB::Subscription _vehicle_status_sub{ORB_ID(vehicle_status)};
hrt_abstime _last_update{0};
static constexpr int num_data_types{4}; ///< number of different telemetry data types
int _next_type{0};
int _uart_fd;
};
-139
View File
@@ -1,139 +0,0 @@
/****************************************************************************
*
* Copyright (c) 2021 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/**
* @file ghst_telemetry.cpp
*
* IRC Ghost (Immersion RC Ghost) telemetry.
*
* @author Igor Misic <igy1000mb@gmail.com>
* @author Juraj Ciberlin <jciberlin1@gmail.com>
*/
#include "ghst_telemetry.hpp"
#include <lib/rc/ghst.hpp>
using time_literals::operator ""_s;
GHSTTelemetry::GHSTTelemetry(int uart_fd) :
_uart_fd(uart_fd)
{
}
bool GHSTTelemetry::update(const hrt_abstime &now)
{
bool success = false;
if ((now - _last_update) > (1_s / (UPDATE_RATE_HZ * NUM_DATA_TYPES))) {
switch (_next_type) {
case 0U:
success = send_battery_status();
break;
case 1U:
success = send_gps1_status();
break;
case 2U:
success = send_gps2_status();
break;
default:
success = false;
break;
}
_last_update = now;
_next_type = (_next_type + 1U) % NUM_DATA_TYPES;
}
return success;
}
bool GHSTTelemetry::send_battery_status()
{
bool success = false;
float voltage_in_10mV;
float current_in_10mA;
float fuel_in_10mAh;
battery_status_s battery_status;
if (_battery_status_sub.update(&battery_status)) {
voltage_in_10mV = battery_status.voltage_filtered_v * FACTOR_VOLTS_TO_10MV;
current_in_10mA = battery_status.current_filtered_a * FACTOR_AMPS_TO_10MA;
fuel_in_10mAh = battery_status.discharged_mah * FACTOR_MAH_TO_10MAH;
success = ghst_send_telemetry_battery_status(_uart_fd,
static_cast<uint16_t>(voltage_in_10mV),
static_cast<uint16_t>(current_in_10mA),
static_cast<uint16_t>(fuel_in_10mAh));
}
return success;
}
bool GHSTTelemetry::send_gps1_status()
{
sensor_gps_s vehicle_gps_position;
if (!_vehicle_gps_position_sub.update(&vehicle_gps_position)) {
return false;
}
int32_t latitude = vehicle_gps_position.lat; // 1e-7 degrees
int32_t longitude = vehicle_gps_position.lon; // 1e-7 degrees
uint16_t altitude = vehicle_gps_position.alt / 1000; // mm -> m
return ghst_send_telemetry_gps1_status(_uart_fd, latitude, longitude, altitude);
}
bool GHSTTelemetry::send_gps2_status()
{
sensor_gps_s vehicle_gps_position;
if (!_vehicle_gps_position_sub.update(&vehicle_gps_position)) {
return false;
}
uint16_t ground_speed = (uint16_t)(vehicle_gps_position.vel_d_m_s / 3.6f * 10.f);
uint16_t ground_course = (uint16_t)(math::degrees(vehicle_gps_position.cog_rad) * 100.f);
uint8_t num_sats = vehicle_gps_position.satellites_used;
// TBD: Can these be computed in a RC telemetry driver?
uint16_t home_dist = 0;
uint16_t home_dir = 0;
uint8_t flags = 0;
return ghst_send_telemetry_gps2_status(_uart_fd, ground_speed, ground_course, num_sats, home_dist, home_dir, flags);
}
-35
View File
@@ -1,35 +0,0 @@
module_name: RC Input Driver
parameters:
- group: RC Input
definitions:
RC_INPUT_PROTO:
description:
short: RC input protocol
long: |
Select your RC input protocol or auto to scan.
category: System
type: enum
values:
-1: Auto
0: None
1: PPM
2: SBUS
3: DSM
4: ST24
5: SUMD
6: CRSF
7: GHST
min: -1
max: 7
default: -1
serial_config:
- command: set RC_INPUT_ARGS "-d ${SERIAL_DEV}"
port_config_param:
name: RC_PORT_CONFIG
group: Serial
default: RC
description_extended: |
Setting this to 'Disabled' will use a board-specific default port
for RC input.
@@ -1,298 +0,0 @@
/****************************************************************************
*
* Copyright (c) 2016 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/**
* @file spektrum_rc.cpp
*
* This is a driver for a Spektrum satellite receiver connected to a Snapdragon
* on the serial port. By default port J12 (next to J13, power module side) is used.
*/
#include <string.h>
#include <px4_platform_common/tasks.h>
#include <px4_platform_common/posix.h>
#include <px4_platform_common/getopt.h>
#include <lib/rc/dsm.h>
#include <drivers/drv_hrt.h>
#include <uORB/uORB.h>
#include <uORB/topics/input_rc.h>
// Snapdraogon: use J12 (next to J13, power module side)
#define SPEKTRUM_UART_DEVICE_PATH "/dev/tty-3"
#define UNUSED(x) (void)(x)
extern "C" { __EXPORT int spektrum_rc_main(int argc, char *argv[]); }
namespace spektrum_rc
{
volatile bool _task_should_exit = false;
static bool _is_running = false;
static px4_task_t _task_handle = -1;
int start();
int stop();
int info();
void usage();
void task_main(int argc, char *argv[]);
void fill_input_rc(uint16_t raw_rc_count, uint16_t raw_rc_values[input_rc_s::RC_INPUT_MAX_CHANNELS],
hrt_abstime now, bool frame_drop, bool failsafe, unsigned frame_drops, int rssi,
input_rc_s &input_rc);
void task_main(int argc, char *argv[])
{
const char *device_path = SPEKTRUM_UART_DEVICE_PATH;
int ch;
int myoptind = 1;
const char *myoptarg = NULL;
while ((ch = px4_getopt(argc, argv, "d:", &myoptind, &myoptarg)) != EOF) {
switch (ch) {
case 'd':
device_path = myoptarg;
break;
default:
break;
}
}
int uart_fd = dsm_init(device_path);
if (uart_fd < 1) {
PX4_ERR("dsm init failed");
return;
}
orb_advert_t rc_pub = nullptr;
// Use a buffer size of the double of the minimum, just to be safe.
uint8_t rx_buf[2 * DSM_BUFFER_SIZE];
_is_running = true;
uint16_t raw_rc_values[input_rc_s::RC_INPUT_MAX_CHANNELS];
uint16_t raw_rc_count = 0;
// Main loop
while (!_task_should_exit) {
int newbytes = ::read(uart_fd, &rx_buf[0], sizeof(rx_buf));
if (newbytes < 0) {
PX4_WARN("read failed");
continue;
}
if (newbytes == 0) {
continue;
}
const hrt_abstime now = hrt_absolute_time();
bool dsm_11_bit;
unsigned frame_drops;
int8_t dsm_rssi;
// parse new data
bool rc_updated = dsm_parse(now, rx_buf, newbytes, &raw_rc_values[0], &raw_rc_count,
&dsm_11_bit, &frame_drops, &dsm_rssi, input_rc_s::RC_INPUT_MAX_CHANNELS);
UNUSED(dsm_11_bit);
if (rc_updated) {
input_rc_s input_rc = {};
fill_input_rc(raw_rc_count, raw_rc_values, now, false, false, frame_drops, dsm_rssi,
input_rc);
if (rc_pub == nullptr) {
rc_pub = orb_advertise(ORB_ID(input_rc), &input_rc);
} else {
orb_publish(ORB_ID(input_rc), rc_pub, &input_rc);
}
}
// sleep since no poll for qurt
usleep(10000);
}
orb_unadvertise(rc_pub);
dsm_deinit();
_is_running = false;
}
void fill_input_rc(uint16_t raw_rc_count, uint16_t raw_rc_values[input_rc_s::RC_INPUT_MAX_CHANNELS],
hrt_abstime now, bool frame_drop, bool failsafe, unsigned frame_drops, int rssi,
input_rc_s &input_rc)
{
input_rc.input_source = input_rc_s::RC_INPUT_SOURCE_QURT;
input_rc.channel_count = raw_rc_count;
if (input_rc.channel_count > input_rc_s::RC_INPUT_MAX_CHANNELS) {
input_rc.channel_count = input_rc_s::RC_INPUT_MAX_CHANNELS;
}
unsigned valid_chans = 0;
for (unsigned i = 0; i < input_rc.channel_count; ++i) {
input_rc.values[i] = raw_rc_values[i];
if (raw_rc_values[i] != UINT16_MAX) {
valid_chans++;
}
}
input_rc.timestamp = now;
input_rc.timestamp_last_signal = input_rc.timestamp;
input_rc.rc_ppm_frame_length = 0;
/* fake rssi if no value was provided */
if (rssi == -1) {
input_rc.rssi = 255;
} else {
input_rc.rssi = rssi;
}
if (valid_chans == 0) {
input_rc.rssi = 0;
}
input_rc.rc_failsafe = failsafe;
input_rc.rc_lost = (valid_chans == 0);
input_rc.rc_lost_frame_count = frame_drops;
input_rc.rc_total_frame_count = 0;
input_rc.link_quality = -1;
input_rc.rssi_dbm = NAN;
}
int start(int argc, char *argv[])
{
if (_is_running) {
PX4_WARN("already running");
return -1;
}
_task_should_exit = false;
_task_handle = px4_task_spawn_cmd("spektrum_rc_main",
SCHED_DEFAULT,
SCHED_PRIORITY_DEFAULT,
2000,
(px4_main_t)&task_main,
(char *const *)argv);
if (_task_handle < 0) {
PX4_ERR("task start failed");
return -1;
}
return 0;
}
int stop()
{
if (!_is_running) {
PX4_WARN("not running");
return -1;
}
_task_should_exit = true;
while (_is_running) {
usleep(200000);
PX4_INFO(".");
}
_task_handle = -1;
return 0;
}
int info()
{
PX4_INFO("running: %s", _is_running ? "yes" : "no");
return 0;
}
void
usage()
{
PX4_INFO("Usage: spektrum_rc {start|info|stop}");
}
} // namespace spektrum_rc
int spektrum_rc_main(int argc, char *argv[])
{
int myoptind = 1;
if (argc <= 1) {
spektrum_rc::usage();
return 1;
}
const char *verb = argv[myoptind];
if (!strcmp(verb, "start")) {
return spektrum_rc::start(argc - 1, argv + 1);
}
else if (!strcmp(verb, "stop")) {
return spektrum_rc::stop();
}
else if (!strcmp(verb, "info")) {
return spektrum_rc::info();
}
else {
spektrum_rc::usage();
return 1;
}
}
-5
View File
@@ -1,5 +0,0 @@
menuconfig DRIVERS_SPEKTRUM_RC
bool "spektrum_rc"
default n
---help---
Enable support for spektrum_rc
+4 -19
View File
@@ -31,26 +31,11 @@
#
############################################################################
add_library(rc
crsf.cpp
ghst.cpp
st24.cpp
sumd.cpp
sbus.cpp
dsm.cpp
common_rc.cpp
)
target_compile_options(rc
PRIVATE
#-DDEBUG_BUILD
-Wno-unused-result
)
target_link_libraries(rc PRIVATE prebuild_targets)
add_subdirectory(dsm)
add_subdirectory(sbus)
add_subdirectory(st24)
add_subdirectory(sumd)
if(PX4_TESTING AND (${PX4_PLATFORM} MATCHES "posix"))
add_subdirectory(rc_tests)
endif()
if(${PX4_PLATFORM} MATCHES "nuttx")
target_link_libraries(rc PRIVATE nuttx_fs)
endif()
-31
View File
@@ -1,31 +0,0 @@
#include "common_rc.h"
__EXPORT rc_decode_buf_t rc_decode_buf;
uint8_t crc8_dvb_s2(uint8_t crc, uint8_t a)
{
crc ^= a;
for (int i = 0; i < 8; ++i) {
if (crc & 0x80) {
crc = (crc << 1) ^ 0xD5;
} else {
crc = crc << 1;
}
}
return crc;
}
uint8_t crc8_dvb_s2_buf(uint8_t *buf, int len)
{
uint8_t crc = 0;
for (int i = 0; i < len; ++i) {
crc = crc8_dvb_s2(crc, buf[i]);
}
return crc;
}
-29
View File
@@ -1,29 +0,0 @@
#pragma once
#include <stdint.h>
#include "crsf.h"
#include "dsm.h"
#include "ghst.hpp"
#include "sbus.h"
#include "st24.h"
#include "sumd.h"
#pragma pack(push, 1)
typedef struct rc_decode_buf_ {
union {
crsf_frame_t crsf_frame;
ghst_frame_t ghst_frame;
dsm_decode_t dsm;
sbus_frame_t sbus_frame;
ReceiverFcPacket _strxpacket;
ReceiverFcPacketHoTT _hottrxpacket;
};
} rc_decode_buf_t;
#pragma pack(pop)
extern rc_decode_buf_t rc_decode_buf;
uint8_t crc8_dvb_s2(uint8_t crc, uint8_t a);
uint8_t crc8_dvb_s2_buf(uint8_t *buf, int len);
-478
View File
@@ -1,478 +0,0 @@
/****************************************************************************
*
* Copyright (c) 2018 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.
*
****************************************************************************/
#if 0 // enable non-verbose debugging
#define CRSF_DEBUG PX4_WARN
#else
#define CRSF_DEBUG(...)
#endif
#if 0 // verbose debugging. Careful when enabling: it leads to too much output, causing dropped bytes
#define CRSF_VERBOSE PX4_WARN
#else
#define CRSF_VERBOSE(...)
#endif
#include <drivers/drv_hrt.h>
#include <termios.h>
#include <string.h>
#include <unistd.h>
#include "crsf.h"
#include "common_rc.h"
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
#define CRSF_BAUDRATE 420000
#define CRSF_SYNC_BYTE 0xC8
enum class crsf_frame_type_t : uint8_t {
gps = 0x02,
battery_sensor = 0x08,
link_statistics = 0x14,
rc_channels_packed = 0x16,
attitude = 0x1E,
flight_mode = 0x21,
// Extended Header Frames, range: 0x28 to 0x96
device_ping = 0x28,
device_info = 0x29,
parameter_settings_entry = 0x2B,
parameter_read = 0x2C,
parameter_write = 0x2D,
command = 0x32
};
enum class crsf_payload_size_t : uint8_t {
gps = 15,
battery_sensor = 8,
link_statistics = 10,
rc_channels = 22, ///< 11 bits per channel * 16 channels = 22 bytes.
attitude = 6,
};
enum class crsf_address_t : uint8_t {
broadcast = 0x00,
usb = 0x10,
tbs_core_pnp_pro = 0x80,
reserved1 = 0x8A,
current_sensor = 0xC0,
gps = 0xC2,
tbs_blackbox = 0xC4,
flight_controller = 0xC8,
reserved2 = 0xCA,
race_tag = 0xCC,
radio_transmitter = 0xEA,
crsf_receiver = 0xEC,
crsf_transmitter = 0xEE
};
#pragma pack(push, 1)
struct crsf_payload_RC_channels_packed_t {
// 176 bits of data (11 bits per channel * 16 channels) = 22 bytes
unsigned chan0 : 11;
unsigned chan1 : 11;
unsigned chan2 : 11;
unsigned chan3 : 11;
unsigned chan4 : 11;
unsigned chan5 : 11;
unsigned chan6 : 11;
unsigned chan7 : 11;
unsigned chan8 : 11;
unsigned chan9 : 11;
unsigned chan10 : 11;
unsigned chan11 : 11;
unsigned chan12 : 11;
unsigned chan13 : 11;
unsigned chan14 : 11;
unsigned chan15 : 11;
};
#pragma pack(pop)
enum class crsf_parser_state_t : uint8_t {
unsynced = 0,
synced
};
static crsf_frame_t &crsf_frame = rc_decode_buf.crsf_frame;
static unsigned current_frame_position = 0;
static crsf_parser_state_t parser_state = crsf_parser_state_t::unsynced;
/**
* parse the current crsf_frame buffer
*/
static bool crsf_parse_buffer(uint16_t *values, uint16_t *num_values, uint16_t max_channels);
uint8_t crsf_frame_CRC(const crsf_frame_t &frame);
int
crsf_config(int uart_fd)
{
struct termios t;
/* no parity, one stop bit */
tcgetattr(uart_fd, &t);
cfsetspeed(&t, CRSF_BAUDRATE);
t.c_cflag &= ~(CSTOPB | PARENB);
return tcsetattr(uart_fd, TCSANOW, &t);
}
/**
* Convert from RC to PWM value
* @param chan_value channel value in [172, 1811]
* @return PWM channel value in [1000, 2000]
*/
static uint16_t convert_channel_value(unsigned chan_value);
bool crsf_parse(const uint64_t now, const uint8_t *frame, unsigned len, uint16_t *values,
uint16_t *num_values, uint16_t max_channels)
{
bool ret = false;
uint8_t *crsf_frame_ptr = (uint8_t *)&crsf_frame;
while (len > 0) {
// fill in the crsf_buffer, as much as we can
const unsigned current_len = MIN(len, sizeof(crsf_frame_t) - current_frame_position);
memcpy(crsf_frame_ptr + current_frame_position, frame, current_len);
current_frame_position += current_len;
// protection to guarantee parsing progress
if (current_len == 0) {
CRSF_DEBUG("========== parser bug: no progress (%i) ===========", len);
for (unsigned i = 0; i < current_frame_position; ++i) {
CRSF_DEBUG("crsf_frame_ptr[%i]: 0x%x", i, (int)crsf_frame_ptr[i]);
}
// reset the parser
current_frame_position = 0;
parser_state = crsf_parser_state_t::unsynced;
return false;
}
len -= current_len;
frame += current_len;
if (crsf_parse_buffer(values, num_values, max_channels)) {
ret = true;
}
}
return ret;
}
uint8_t crsf_frame_CRC(const crsf_frame_t &frame)
{
// CRC includes type and payload
uint8_t crc = crc8_dvb_s2(0, frame.type);
for (int i = 0; i < frame.header.length - 2; ++i) {
crc = crc8_dvb_s2(crc, frame.payload[i]);
}
return crc;
}
static uint16_t convert_channel_value(unsigned chan_value)
{
/*
* RC PWM
* min 172 -> 988us
* mid 992 -> 1500us
* max 1811 -> 2012us
*/
static constexpr float scale = (2012.f - 988.f) / (1811.f - 172.f);
static constexpr float offset = 988.f - 172.f * scale;
return (scale * chan_value) + offset;
}
static bool crsf_parse_buffer(uint16_t *values, uint16_t *num_values, uint16_t max_channels)
{
uint8_t *crsf_frame_ptr = (uint8_t *)&crsf_frame;
if (parser_state == crsf_parser_state_t::unsynced) {
// there is no sync byte, try to find an RC packet by searching for a matching frame length and type
for (unsigned i = 1; i < current_frame_position - 1; ++i) {
if (crsf_frame_ptr[i] == (uint8_t)crsf_payload_size_t::rc_channels + 2 &&
crsf_frame_ptr[i + 1] == (uint8_t)crsf_frame_type_t::rc_channels_packed) {
parser_state = crsf_parser_state_t::synced;
unsigned frame_offset = i - 1;
CRSF_VERBOSE("RC channels found at offset %i", frame_offset);
// move the rest of the buffer to the beginning
if (frame_offset != 0) {
memmove(crsf_frame_ptr, crsf_frame_ptr + frame_offset, current_frame_position - frame_offset);
current_frame_position -= frame_offset;
}
break;
}
}
}
if (parser_state != crsf_parser_state_t::synced) {
if (current_frame_position >= sizeof(crsf_frame_t)) {
// discard most of the data, but keep the last 3 bytes (otherwise we could miss the frame start)
current_frame_position = 3;
for (unsigned i = 0; i < current_frame_position; ++i) {
crsf_frame_ptr[i] = crsf_frame_ptr[sizeof(crsf_frame_t) - current_frame_position + i];
}
CRSF_VERBOSE("Discarding buffer");
}
return false;
}
if (current_frame_position < 3) {
// wait until we have the header & type
return false;
}
// Now we have at least the header and the type
const unsigned current_frame_length = crsf_frame.header.length + sizeof(crsf_frame_header_t);
if (current_frame_length > sizeof(crsf_frame_t) || current_frame_length < 4) {
// frame too long or bogus -> discard everything and go into unsynced state
current_frame_position = 0;
parser_state = crsf_parser_state_t::unsynced;
CRSF_DEBUG("Frame too long/bogus (%i, type=%i) -> unsync", current_frame_length, crsf_frame.type);
return false;
}
if (current_frame_position < current_frame_length) {
// we don't have the full frame yet -> wait for more data
CRSF_VERBOSE("waiting for more data (%i < %i)", current_frame_position, current_frame_length);
return false;
}
bool ret = false;
// Now we have the full frame
if (crsf_frame.type == (uint8_t)crsf_frame_type_t::rc_channels_packed &&
crsf_frame.header.length == (uint8_t)crsf_payload_size_t::rc_channels + 2) {
const uint8_t crc = crsf_frame.payload[crsf_frame.header.length - 2];
if (crc == crsf_frame_CRC(crsf_frame)) {
const crsf_payload_RC_channels_packed_t *const rc_channels =
(crsf_payload_RC_channels_packed_t *)&crsf_frame.payload;
*num_values = MIN(max_channels, 16);
if (max_channels > 0) { values[0] = convert_channel_value(rc_channels->chan0); }
if (max_channels > 1) { values[1] = convert_channel_value(rc_channels->chan1); }
if (max_channels > 2) { values[2] = convert_channel_value(rc_channels->chan2); }
if (max_channels > 3) { values[3] = convert_channel_value(rc_channels->chan3); }
if (max_channels > 4) { values[4] = convert_channel_value(rc_channels->chan4); }
if (max_channels > 5) { values[5] = convert_channel_value(rc_channels->chan5); }
if (max_channels > 6) { values[6] = convert_channel_value(rc_channels->chan6); }
if (max_channels > 7) { values[7] = convert_channel_value(rc_channels->chan7); }
if (max_channels > 8) { values[8] = convert_channel_value(rc_channels->chan8); }
if (max_channels > 9) { values[9] = convert_channel_value(rc_channels->chan9); }
if (max_channels > 10) { values[10] = convert_channel_value(rc_channels->chan10); }
if (max_channels > 11) { values[11] = convert_channel_value(rc_channels->chan11); }
if (max_channels > 12) { values[12] = convert_channel_value(rc_channels->chan12); }
if (max_channels > 13) { values[13] = convert_channel_value(rc_channels->chan13); }
if (max_channels > 14) { values[14] = convert_channel_value(rc_channels->chan14); }
if (max_channels > 15) { values[15] = convert_channel_value(rc_channels->chan15); }
CRSF_VERBOSE("Got Channels");
ret = true;
} else {
CRSF_DEBUG("CRC check failed");
}
} else {
CRSF_DEBUG("Got Non-RC frame (len=%i, type=%i)", current_frame_length, crsf_frame.type);
// We could check the CRC here and reset the parser into unsynced state if it fails.
// But in practise it's robust even without that.
}
// Either reset or move the rest of the buffer
if (current_frame_position > current_frame_length) {
CRSF_VERBOSE("Moving buffer (%i > %i)", current_frame_position, current_frame_length);
memmove(crsf_frame_ptr, crsf_frame_ptr + current_frame_length, current_frame_position - current_frame_length);
current_frame_position -= current_frame_length;
} else {
current_frame_position = 0;
}
return ret;
}
/**
* write an uint8_t value to a buffer at a given offset and increment the offset
*/
static inline void write_uint8_t(uint8_t *buf, int &offset, uint8_t value)
{
buf[offset++] = value;
}
/**
* write an uint16_t value to a buffer at a given offset and increment the offset
*/
static inline void write_uint16_t(uint8_t *buf, int &offset, uint16_t value)
{
// Big endian
buf[offset] = value >> 8;
buf[offset + 1] = value & 0xff;
offset += 2;
}
/**
* write an uint24_t value to a buffer at a given offset and increment the offset
*/
static inline void write_uint24_t(uint8_t *buf, int &offset, int value)
{
// Big endian
buf[offset] = value >> 16;
buf[offset + 1] = (value >> 8) & 0xff;
buf[offset + 2] = value & 0xff;
offset += 3;
}
/**
* write an int32_t value to a buffer at a given offset and increment the offset
*/
static inline void write_int32_t(uint8_t *buf, int &offset, int32_t value)
{
// Big endian
buf[offset] = value >> 24;
buf[offset + 1] = (value >> 16) & 0xff;
buf[offset + 2] = (value >> 8) & 0xff;
buf[offset + 3] = value & 0xff;
offset += 4;
}
static inline void write_frame_header(uint8_t *buf, int &offset, crsf_frame_type_t type, uint8_t payload_size)
{
write_uint8_t(buf, offset, CRSF_SYNC_BYTE); // this got changed from the address to the sync byte
write_uint8_t(buf, offset, payload_size + 2);
write_uint8_t(buf, offset, (uint8_t)type);
}
static inline void write_frame_crc(uint8_t *buf, int &offset, int buf_size)
{
// CRC does not include the address and length
write_uint8_t(buf, offset, crc8_dvb_s2_buf(buf + 2, buf_size - 3));
// check correctness of buffer size (only needed during development)
//if (buf_size != offset) { PX4_ERR("frame size mismatch (%i != %i)", buf_size, offset); }
}
bool crsf_send_telemetry_battery(int uart_fd, uint16_t voltage, uint16_t current, int fuel, uint8_t remaining)
{
uint8_t buf[(uint8_t)crsf_payload_size_t::battery_sensor + 4];
int offset = 0;
write_frame_header(buf, offset, crsf_frame_type_t::battery_sensor, (uint8_t)crsf_payload_size_t::battery_sensor);
write_uint16_t(buf, offset, voltage);
write_uint16_t(buf, offset, current);
write_uint24_t(buf, offset, fuel);
write_uint8_t(buf, offset, remaining);
write_frame_crc(buf, offset, sizeof(buf));
return write(uart_fd, buf, offset) == offset;
}
bool crsf_send_telemetry_gps(int uart_fd, int32_t latitude, int32_t longitude, uint16_t groundspeed,
uint16_t gps_heading, uint16_t altitude, uint8_t num_satellites)
{
uint8_t buf[(uint8_t)crsf_payload_size_t::gps + 4];
int offset = 0;
write_frame_header(buf, offset, crsf_frame_type_t::gps, (uint8_t)crsf_payload_size_t::gps);
write_int32_t(buf, offset, latitude);
write_int32_t(buf, offset, longitude);
write_uint16_t(buf, offset, groundspeed);
write_uint16_t(buf, offset, gps_heading);
write_uint16_t(buf, offset, altitude);
write_uint8_t(buf, offset, num_satellites);
write_frame_crc(buf, offset, sizeof(buf));
return write(uart_fd, buf, offset) == offset;
}
bool crsf_send_telemetry_attitude(int uart_fd, int16_t pitch, int16_t roll, int16_t yaw)
{
uint8_t buf[(uint8_t)crsf_payload_size_t::attitude + 4];
int offset = 0;
write_frame_header(buf, offset, crsf_frame_type_t::attitude, (uint8_t)crsf_payload_size_t::attitude);
write_uint16_t(buf, offset, pitch);
write_uint16_t(buf, offset, roll);
write_uint16_t(buf, offset, yaw);
write_frame_crc(buf, offset, sizeof(buf));
return write(uart_fd, buf, offset) == offset;
}
bool crsf_send_telemetry_flight_mode(int uart_fd, const char *flight_mode)
{
const int max_length = 16;
int length = strlen(flight_mode) + 1;
if (length > max_length) {
length = max_length;
}
uint8_t buf[max_length + 4];
int offset = 0;
write_frame_header(buf, offset, crsf_frame_type_t::flight_mode, length);
memcpy(buf + offset, flight_mode, length);
offset += length;
buf[offset - 1] = 0; // ensure null-terminated string
write_frame_crc(buf, offset, length + 4);
return write(uart_fd, buf, offset) == offset;
}
-134
View File
@@ -1,134 +0,0 @@
/****************************************************************************
*
* Copyright (c) 2018 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/**
* @file crsf.h
*
* RC protocol definition for CSRF (TBS Crossfire).
* It is an uninverted protocol at 420000 baudrate.
*
* RC channels come in at 150Hz.
*
* @author Beat Küng <beat-kueng@gmx.net>
*/
#pragma once
#include <stdint.h>
#include <px4_platform_common/defines.h>
__BEGIN_DECLS
#define CRSF_FRAME_SIZE_MAX 30 // the actual maximum length is 64, but we're only interested in RC channels and want to minimize buffer size
#define CRSF_PAYLOAD_SIZE_MAX (CRSF_FRAME_SIZE_MAX-4)
struct crsf_frame_header_t {
uint8_t device_address; ///< @see crsf_address_t
uint8_t length; ///< length of crsf_frame_t (including CRC) minus sizeof(crsf_frame_header_t)
};
struct crsf_frame_t {
crsf_frame_header_t header;
uint8_t type; ///< @see crsf_frame_type_t
uint8_t payload[CRSF_PAYLOAD_SIZE_MAX + 1]; ///< payload data including 1 byte CRC at end
};
/**
* Configure an UART port to be used for CRSF
* @param uart_fd UART file descriptor
* @return 0 on success, -errno otherwise
*/
__EXPORT int crsf_config(int uart_fd);
/**
* Parse the CRSF protocol and extract RC channel data.
*
* @param now current time
* @param frame data to parse
* @param len length of frame
* @param values output channel values, each in range [1000, 2000]
* @param num_values set to the number of parsed channels in values
* @param max_channels maximum length of values
* @return true if channels successfully decoded
*/
__EXPORT bool crsf_parse(const uint64_t now, const uint8_t *frame, unsigned len, uint16_t *values,
uint16_t *num_values, uint16_t max_channels);
/**
* Send telemetry battery information
* @param uart_fd UART file descriptor
* @param voltage Voltage [0.1V]
* @param current Current [0.1A]
* @param fuel drawn mAh
* @param remaining battery remaining [%]
* @return true on success
*/
__EXPORT bool crsf_send_telemetry_battery(int uart_fd, uint16_t voltage, uint16_t current, int fuel, uint8_t remaining);
/**
* Send telemetry GPS information
* @param uart_fd UART file descriptor
* @param latitude latitude [degree * 1e7]
* @param longitude longitude [degree * 1e7]
* @param groundspeed Ground speed [km/h * 10]
* @param gps_heading GPS heading [degree * 100]
* @param altitude Altitude [meters + 1000m offset]
* @param num_satellites number of satellites used
* @return true on success
*/
__EXPORT bool crsf_send_telemetry_gps(int uart_fd, int32_t latitude, int32_t longitude, uint16_t groundspeed,
uint16_t gps_heading, uint16_t altitude, uint8_t num_satellites);
/**
* Send telemetry Attitude information
* @param uart_fd UART file descriptor
* @param pitch Pitch angle [rad * 1e4]
* @param roll Roll angle [rad * 1e4]
* @param yaw Yaw angle [rad * 1e4]
* @return true on success
*/
__EXPORT bool crsf_send_telemetry_attitude(int uart_fd, int16_t pitch, int16_t roll, int16_t yaw);
/**
* Send telemetry Flight Mode information
* @param uart_fd UART file descriptor
* @param flight_mode Flight Mode string (max length = 15)
* @return true on success
*/
__EXPORT bool crsf_send_telemetry_flight_mode(int uart_fd, const char *flight_mode);
__END_DECLS
+44
View File
@@ -0,0 +1,44 @@
############################################################################
#
# Copyright (c) 2015-2022 PX4 Development Team. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# 3. Neither the name PX4 nor the names of its contributors may be
# used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
############################################################################
add_library(rc_dsm
dsm.cpp
dsm.h
)
target_compile_options(rc_dsm
PRIVATE
#-DDEBUG_BUILD
#-Wno-unused-result
)
target_link_libraries(rc_dsm PRIVATE prebuild_targets)
+44 -4
View File
@@ -51,8 +51,7 @@
#include <string.h>
#include "dsm.h"
#include "spektrum_rssi.h"
#include "common_rc.h"
#include <drivers/drv_hrt.h>
#include <include/containers/Bitset.hpp>
@@ -64,6 +63,47 @@
#define dsm_udelay(arg) px4_usleep(arg)
#endif
#include <climits>
/*
min_rssi_dbm = -92.0
max_rssi_dbm = -42.0
def dbm_to_percent(abs_dbm):
if -abs_dbm < min_rssi_dbm:
return 0.0
elif -abs_dbm > max_rssi_dbm:
return 100.0
else:
return 100.0 * math.log10(1.0 + (float)(-abs_dbm - min_rssi_dbm) * (9.0 / (max_rssi_dbm - min_rssi_dbm)))
*/
constexpr int8_t lu_dbm_percent[] = {
100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 99, 98, 98, 97, 96,
95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 79,
78, 77, 75, 74, 73, 71, 70, 68, 66, 65, 63, 61, 59, 57, 55, 52,
50, 47, 45, 42, 39, 35, 32, 28, 24, 19, 13, 7, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
};
#define MASK(n) (n >> (sizeof(n) * CHAR_BIT - 1))
/* constexpr-compatible version of abs() */
static constexpr unsigned c_abs(int n) { return (n + MASK(n)) ^ MASK(n); }
/* convert signed spektrum dbm (-92 to -42) to percentage */
static constexpr int8_t spek_dbm_to_percent(int8_t dbm) { return lu_dbm_percent[c_abs(dbm)]; }
static_assert(spek_dbm_to_percent(0) == 100, "0 dbm should be 100%");
static_assert(spek_dbm_to_percent(-42) == 100, "-42 dbm should be 100%");
static_assert(spek_dbm_to_percent(-80) == 50, "-80 dbm should be 50%");
static_assert(spek_dbm_to_percent(-92) == 0, "-92 dbm should be 0%");
static_assert(spek_dbm_to_percent(-128) == 0, "-128 dbm should be 0%");
// #define DSM_DEBUG
static enum DSM_DECODE_STATE {
@@ -81,8 +121,8 @@ static dsm_channel_t channel_buffer[DSM_MAX_CHANNEL_COUNT];
static int dsm_fd = -1; /**< File handle to the DSM UART */
static hrt_abstime dsm_last_rx_time; /**< Timestamp when we last received data */
static hrt_abstime dsm_last_frame_time; /**< Timestamp for start of last valid dsm frame */
static dsm_frame_t &dsm_frame = rc_decode_buf.dsm.frame; /**< DSM_BUFFER_SIZE DSM dsm frame receive buffer */
static dsm_buf_t &dsm_buf = rc_decode_buf.dsm.buf; /**< DSM_BUFFER_SIZE DSM dsm frame receive buffer */
static dsm_frame_t dsm_frame; /**< DSM_BUFFER_SIZE DSM dsm frame receive buffer */
static dsm_buf_t dsm_buf; /**< DSM_BUFFER_SIZE DSM dsm frame receive buffer */
static unsigned dsm_partial_frame_count; /**< Count of bytes received for current dsm frame */
static unsigned dsm_channel_shift = 0; /**< Channel resolution, 0=unknown, 10=10 bit (1024), 11=11 bit (2048) */
@@ -53,8 +53,6 @@ __BEGIN_DECLS
#define DSM_MAX_CHANNEL_COUNT 20 /**< Max channel count of any DSM RC */
#define DSM_BUFFER_SIZE (DSM_FRAME_SIZE + DSM_FRAME_SIZE / 2)
#pragma pack(push, 1)
typedef uint8_t dsm_frame_t[DSM_BUFFER_SIZE]; /**< DSM dsm frame receive buffer */
typedef uint8_t dsm_buf_t[DSM_FRAME_SIZE * 2]; // Define working buffer
@@ -63,8 +61,6 @@ typedef struct dsm_decode_t {
dsm_buf_t buf;
} dsm_decode_t;
#pragma pack(pop)
__EXPORT int dsm_init(const char *device);
__EXPORT void dsm_deinit(void);
__EXPORT void dsm_proto_init(void);
+4 -1
View File
@@ -39,5 +39,8 @@ px4_add_module(
SRCS
RCTest.cpp
DEPENDS
rc
rc_dsm
rc_sbus
rc_st24
rc_sumd
)
+4 -184
View File
@@ -9,12 +9,10 @@
#include <drivers/drv_hrt.h>
#define DSM_DEBUG
#include <lib/rc/sbus.h>
#include <lib/rc/dsm.h>
#include <lib/rc/st24.h>
#include <lib/rc/sumd.h>
#include <lib/rc/crsf.h>
#include <lib/rc/ghst.hpp>
#include <lib/rc/sbus/sbus.h>
#include <lib/rc/dsm/dsm.h>
#include <lib/rc/st24/st24.h>
#include <lib/rc/sumd/sumd.h>
#if defined(CONFIG_ARCH_BOARD_PX4_SITL)
#define TEST_DATA_PATH "./test_data/"
@@ -30,8 +28,6 @@ public:
bool run_tests() override;
private:
bool crsfTest();
bool ghstTest();
bool dsmTest(const char *filepath, unsigned expected_chancount, unsigned expected_dropcount, unsigned chan0);
bool dsmTest10Ch();
bool dsmTest16Ch();
@@ -44,8 +40,6 @@ private:
bool RCTest::run_tests()
{
ut_run_test(crsfTest);
ut_run_test(ghstTest);
ut_run_test(dsmTest10Ch);
ut_run_test(dsmTest16Ch);
ut_run_test(dsmTest22msDSMX16Ch);
@@ -57,180 +51,6 @@ bool RCTest::run_tests()
return (_tests_failed == 0);
}
bool RCTest::crsfTest()
{
const char *filepath = TEST_DATA_PATH "crsf_rc_channels.txt";
FILE *fp = fopen(filepath, "rt");
ut_test(fp);
//PX4_INFO("loading data from: %s", filepath);
const int line_size = 500;
char line[line_size];
bool has_decoded_values = false;
const int max_channels = 16;
uint16_t rc_values[max_channels];
uint16_t num_values = 0;
int line_counter = 1;
while (fgets(line, line_size, fp) != nullptr) {
if (strncmp(line, "INPUT ", 6) == 0) {
if (has_decoded_values) {
PX4_ERR("Parser decoded values that are not in the test file (line=%i)", line_counter);
return false;
}
// read the values
const char *file_buffer = line + 6;
int frame_len = 0;
uint8_t frame[300];
int offset;
int number;
while (sscanf(file_buffer, "%x, %n", &number, &offset) > 0) {
frame[frame_len++] = number;
file_buffer += offset;
}
// Pipe the data into the parser
hrt_abstime now = hrt_absolute_time();
bool result = crsf_parse(now, frame, frame_len, rc_values, &num_values, max_channels);
if (result) {
has_decoded_values = true;
}
} else if (strncmp(line, "DECODED ", 8) == 0) {
if (!has_decoded_values) {
PX4_ERR("Test file contains decoded values but the parser did not decode anything (line=%i)", line_counter);
return false;
}
// read the values
const char *file_buffer = line + 8;
int offset;
int expected_rc_value;
int expected_num_channels = 0;
while (sscanf(file_buffer, "%x, %n", &expected_rc_value, &offset) > 0) {
// allow a small difference
if (abs(expected_rc_value - (int)rc_values[expected_num_channels]) > 10) {
PX4_ERR("File line: %i, channel: %i", line_counter, expected_num_channels);
ut_compare("Wrong decoded channel", expected_rc_value, rc_values[expected_num_channels]);
}
file_buffer += offset;
++expected_num_channels;
}
if (expected_num_channels != num_values) {
PX4_ERR("File line: %d", line_counter);
ut_compare("Unexpected number of decoded channels", expected_num_channels, num_values);
}
has_decoded_values = false;
}
++line_counter;
}
return true;
}
bool RCTest::ghstTest()
{
const char *filepath = TEST_DATA_PATH "ghst_rc_channels.txt";
FILE *fp = fopen(filepath, "rt");
ut_test(fp);
int uart_fd = -1;
const int line_size = 500;
char line[line_size];
bool has_decoded_values = false;
const int max_channels = 16;
uint16_t rc_values[max_channels];
uint16_t num_values = 0;
int line_counter = 1;
int8_t ghst_rssi = -1;
ghst_config(uart_fd);
while (fgets(line, line_size, fp) != nullptr) {
if (strncmp(line, "INPUT ", 6) == 0) {
if (has_decoded_values) {
PX4_ERR("Parser decoded values that are not in the test file (line=%i)", line_counter);
return false;
}
// read the values
const char *file_buffer = line + 6;
int frame_len = 0;
uint8_t frame[300];
int offset;
int number;
while (sscanf(file_buffer, "%x, %n", &number, &offset) > 0) {
frame[frame_len++] = number;
file_buffer += offset;
}
// Pipe the data into the parser
hrt_abstime now = hrt_absolute_time();
bool result = ghst_parse(now, frame, frame_len, rc_values, &ghst_rssi, &num_values, max_channels);
if (result) {
has_decoded_values = true;
}
} else if (strncmp(line, "DECODED ", 8) == 0) {
if (!has_decoded_values) {
PX4_ERR("Test file contains decoded values but the parser did not decode anything (line=%i)", line_counter);
return false;
}
// read the values
const char *file_buffer = line + 8;
int offset;
int expected_rc_value;
int expected_num_channels = 0;
while (sscanf(file_buffer, "%x, %n", &expected_rc_value, &offset) > 0) {
// allow a small difference
if (abs(expected_rc_value - (int)rc_values[expected_num_channels]) > 10) {
PX4_ERR("File line: %i, channel: %i", line_counter, expected_num_channels);
ut_compare("Wrong decoded channel", expected_rc_value, rc_values[expected_num_channels]);
}
file_buffer += offset;
++expected_num_channels;
}
if (expected_num_channels != num_values) {
PX4_ERR("File line: %d", line_counter);
ut_compare("Unexpected number of decoded channels", expected_num_channels, num_values);
}
has_decoded_values = false;
}
++line_counter;
}
return true;
}
bool RCTest::dsmTest10Ch()
{
return dsmTest(TEST_DATA_PATH "dsm_x_data.txt", 10, 2, 1500);
+48
View File
@@ -0,0 +1,48 @@
############################################################################
#
# Copyright (c) 2015-2022 PX4 Development Team. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# 3. Neither the name PX4 nor the names of its contributors may be
# used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
############################################################################
add_library(rc_sbus
sbus.cpp
sbus.h
)
target_compile_options(rc_sbus
PRIVATE
#-DDEBUG_BUILD
-Wno-unused-result
)
target_link_libraries(rc_sbus PRIVATE prebuild_targets)
if(${PX4_PLATFORM} MATCHES "nuttx")
target_link_libraries(rc_sbus PRIVATE nuttx_fs)
endif()
@@ -48,7 +48,7 @@
#endif
#include "sbus.h"
#include "common_rc.h"
#include <drivers/drv_hrt.h>
#include <lib/mathlib/mathlib.h>
@@ -119,7 +119,7 @@ static enum SBUS2_DECODE_STATE {
SBUS2_DECODE_STATE_SBUS2_DATA2 = 0x34
} sbus_decode_state = SBUS2_DECODE_STATE_DESYNC;
static sbus_frame_t &sbus_frame = rc_decode_buf.sbus_frame;
static sbus_frame_t sbus_frame;
static unsigned partial_frame_count;
static unsigned sbus1_frame_delay = (1000U * 1000U) / SBUS1_DEFAULT_RATE_HZ;
+1 -1
View File
@@ -1,6 +1,6 @@
/****************************************************************************
*
* Copyright (c) 2012-2015 PX4 Development Team. All rights reserved.
* Copyright (c) 2012-2022 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
-88
View File
@@ -1,88 +0,0 @@
/****************************************************************************
*
* Copyright (c) 2012-2015 PX4 Development Team. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name PX4 nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/**
* @file spektrum_rssi.h
*
* RSSI dBm to percentage conversion for Spektrum telemetry receivers
*
* @author Kurt Kiefer <kekiefer@gmail.com>
*/
#pragma once
#include <climits>
/*
min_rssi_dbm = -92.0
max_rssi_dbm = -42.0
def dbm_to_percent(abs_dbm):
if -abs_dbm < min_rssi_dbm:
return 0.0
elif -abs_dbm > max_rssi_dbm:
return 100.0
else:
return 100.0 * math.log10(1.0 + (float)(-abs_dbm - min_rssi_dbm) * (9.0 / (max_rssi_dbm - min_rssi_dbm)))
*/
constexpr int8_t lu_dbm_percent[] = {
100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 99, 98, 98, 97, 96,
95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 79,
78, 77, 75, 74, 73, 71, 70, 68, 66, 65, 63, 61, 59, 57, 55, 52,
50, 47, 45, 42, 39, 35, 32, 28, 24, 19, 13, 7, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
};
#define MASK(n) (n >> (sizeof(n) * CHAR_BIT - 1))
/* constexpr-compatible version of abs() */
constexpr unsigned c_abs(int n)
{
return (n + MASK(n)) ^ MASK(n);
}
/* convert signed spektrum dbm (-92 to -42) to percentage */
constexpr int8_t spek_dbm_to_percent(int8_t dbm)
{
return lu_dbm_percent[c_abs(dbm)];
}
static_assert(spek_dbm_to_percent(0) == 100, "0 dbm should be 100%");
static_assert(spek_dbm_to_percent(-42) == 100, "-42 dbm should be 100%");
static_assert(spek_dbm_to_percent(-80) == 50, "-80 dbm should be 50%");
static_assert(spek_dbm_to_percent(-92) == 0, "-92 dbm should be 0%");
static_assert(spek_dbm_to_percent(-128) == 0, "-128 dbm should be 0%");
+44
View File
@@ -0,0 +1,44 @@
############################################################################
#
# Copyright (c) 2015-2022 PX4 Development Team. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# 3. Neither the name PX4 nor the names of its contributors may be
# used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
############################################################################
add_library(rc_st24
st24.cpp
st24.h
)
target_compile_options(rc_st24
PRIVATE
#-DDEBUG_BUILD
#-Wno-unused-result
)
target_link_libraries(rc_st24 PRIVATE prebuild_targets)
@@ -42,7 +42,6 @@
#include <stdbool.h>
#include <stdio.h>
#include "st24.h"
#include "common_rc.h"
const char *decode_states[] = {"UNSYNCED",
"GOT_STX1",
@@ -66,8 +65,15 @@ const char *decode_states[] = {"UNSYNCED",
static enum ST24_DECODE_STATE _decode_state = ST24_DECODE_STATE_UNSYNCED;
static uint8_t _rxlen;
static ReceiverFcPacket &_rxpacket = rc_decode_buf._strxpacket;
static ReceiverFcPacket _rxpacket;
/**
* CRC8 implementation for ST24 protocol
*
* @param prt Pointer to the data to CRC
* @param len number of bytes to accumulate in the checksum
* @return the checksum of these bytes over len
*/
uint8_t st24_common_crc8(uint8_t *ptr, uint8_t len)
{
uint8_t i, crc ;
@@ -147,15 +147,6 @@ typedef struct {
#pragma pack(pop)
/**
* CRC8 implementation for ST24 protocol
*
* @param prt Pointer to the data to CRC
* @param len number of bytes to accumulate in the checksum
* @return the checksum of these bytes over len
*/
uint8_t st24_common_crc8(uint8_t *ptr, uint8_t len);
/**
* Decoder for ST24 protocol
*
+44
View File
@@ -0,0 +1,44 @@
############################################################################
#
# Copyright (c) 2015-2022 PX4 Development Team. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# 3. Neither the name PX4 nor the names of its contributors may be
# used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
############################################################################
add_library(rc_sumd
sumd.cpp
sumd.h
)
target_compile_options(rc_sumd
PRIVATE
#-DDEBUG_BUILD
#-Wno-unused-result
)
target_link_libraries(rc_sumd PRIVATE prebuild_targets)
@@ -42,7 +42,6 @@
#include <stdbool.h>
#include <stdio.h>
#include "sumd.h"
#include "common_rc.h"
enum SUMD_DECODE_STATE {
SUMD_DECODE_STATE_UNSYNCED = 0,
@@ -67,12 +66,11 @@ const char *decode_states[] = {"UNSYNCED",
};
*/
uint8_t _crc8 = 0x00;
uint8_t _crc8 = 0x00;
uint16_t _crc16 = 0x0000;
bool _sumd = true;
bool _crcOK = false;
bool _debug = false;
bool _sumd = true;
bool _crcOK = false;
bool _debug = false;
/* define range mapping here, -+100% -> 1000..2000 */
#define SUMD_RANGE_MIN 0.0f
@@ -88,8 +86,15 @@ bool _debug = false;
static enum SUMD_DECODE_STATE _decode_state = SUMD_DECODE_STATE_UNSYNCED;
static uint8_t _rxlen;
static ReceiverFcPacketHoTT &_rxpacket = rc_decode_buf._hottrxpacket;
static ReceiverFcPacketHoTT _rxpacket;
/**
* CRC16 implementation for SUMD protocol
*
* @param crc Initial CRC Value
* @param value to accumulate in the checksum
* @return the checksum
*/
uint16_t sumd_crc16(uint16_t crc, uint8_t value)
{
int i;
@@ -102,7 +107,14 @@ uint16_t sumd_crc16(uint16_t crc, uint8_t value)
return crc;
}
uint8_t sumd_crc8(uint8_t crc, uint8_t value)
/**
* CRC8 implementation for SUMH protocol
*
* @param crc Initial CRC Value
* @param value to accumulate in the checksum
* @return the checksum
*/
static uint8_t sumd_crc8(uint8_t crc, uint8_t value)
{
crc += value;
return crc;
@@ -52,9 +52,6 @@ __BEGIN_DECLS
#define SUMD_ID_SUMD 0x01
#define SUMD_ID_FAILSAFE 0x81
#pragma pack(push, 1)
typedef struct {
uint8_t header; ///< 0xA8 for a valid packet
@@ -68,25 +65,6 @@ typedef struct {
} ReceiverFcPacketHoTT;
#pragma pack(pop)
/**
* CRC16 implementation for SUMD protocol
*
* @param crc Initial CRC Value
* @param value to accumulate in the checksum
* @return the checksum
*/
uint16_t sumd_crc16(uint16_t crc, uint8_t value);
/**
* CRC8 implementation for SUMH protocol
*
* @param crc Initial CRC Value
* @param value to accumulate in the checksum
* @return the checksum
*/
uint8_t sumd_crc8(uint8_t crc, uint8_t value);
/**
* Decoder for SUMD/SUMH protocol
*
@@ -98,10 +76,6 @@ uint8_t sumd_crc8(uint8_t crc, uint8_t value);
* @param failsafe pointer to a boolean where the decoded failsafe flag is written back to
* @return 0 for success (a decoded packet), 1 for no packet yet (accumulating), 2 for unknown packet, 3 for out of sync, 4 for checksum error
*/
/*
__EXPORT int sumd_decode(uint8_t byte, uint8_t *rssi, uint8_t *rx_count, uint16_t *channel_count,
uint16_t *channels, uint16_t max_chan_count, bool *failsafe);
*/
__EXPORT int sumd_decode(uint8_t byte, uint8_t *rssi, uint8_t *rx_count, uint16_t *channel_count,
uint16_t *channels, uint16_t max_chan_count, bool *failsafe);
+4 -1
View File
@@ -52,7 +52,10 @@ target_link_libraries(px4iofirmware
nuttx_arch
nuttx_c
nuttx_mm
rc
rc_dsm
rc_sbus
rc_st24
rc_sumd
)
if(PX4IO_PERF)
+4 -4
View File
@@ -44,10 +44,10 @@
#include <drivers/drv_hrt.h>
#include <systemlib/ppm_decode.h>
#include <rc/st24.h>
#include <rc/sumd.h>
#include <rc/sbus.h>
#include <rc/dsm.h>
#include <lib/rc/st24/st24.h>
#include <lib/rc/sumd/sumd.h>
#include <lib/rc/sbus/sbus.h>
#include <lib/rc/dsm/dsm.h>
#include <uORB/topics/input_rc.h>
#if defined(PX4IO_PERF)
+1 -1
View File
@@ -51,7 +51,7 @@
#include <drivers/drv_pwm_output.h>
#include <drivers/drv_hrt.h>
#include <rc/sbus.h>
#include <lib/rc/sbus/sbus.h>
extern "C" {
/* #define DEBUG */
+2 -2
View File
@@ -48,8 +48,8 @@
#include <drivers/drv_hrt.h>
#include <drivers/drv_pwm_output.h>
#include <stm32_pwr.h>
#include <rc/dsm.h>
#include <rc/sbus.h>
#include <lib/rc/dsm/dsm.h>
#include <lib/rc/sbus/sbus.h>
#include "px4io.h"
#include "protocol.h"