/*
Common GCS MAVLink functions for all vehicle types
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include
#include
#include
#include
#include "ap_version.h"
#include "GCS.h"
#if CONFIG_HAL_BOARD == HAL_BOARD_PX4 || CONFIG_HAL_BOARD == HAL_BOARD_VRBRAIN
#include
#include
#include
#include
#endif
extern const AP_HAL::HAL& hal;
uint32_t GCS_MAVLINK::last_radio_status_remrssi_ms;
uint8_t GCS_MAVLINK::mavlink_active = 0;
uint8_t GCS_MAVLINK::chan_is_streaming = 0;
uint32_t GCS_MAVLINK::reserve_param_space_start_ms;
AP_HAL::Util::perf_counter_t GCS_MAVLINK::_perf_packet;
AP_HAL::Util::perf_counter_t GCS_MAVLINK::_perf_update;
GCS *GCS::_singleton = nullptr;
GCS_MAVLINK::GCS_MAVLINK()
{
AP_Param::setup_object_defaults(this, var_info);
}
void
GCS_MAVLINK::init(AP_HAL::UARTDriver *port, mavlink_channel_t mav_chan)
{
if (!valid_channel(mav_chan)) {
return;
}
_port = port;
chan = mav_chan;
mavlink_comm_port[chan] = _port;
initialised = true;
_queued_parameter = nullptr;
reset_cli_timeout();
if (!_perf_packet) {
_perf_packet = hal.util->perf_alloc(AP_HAL::Util::PC_ELAPSED, "GCS_Packet");
}
if (!_perf_update) {
_perf_update = hal.util->perf_alloc(AP_HAL::Util::PC_ELAPSED, "GCS_Update");
}
}
/*
setup a UART, handling begin() and init()
*/
void
GCS_MAVLINK::setup_uart(const AP_SerialManager& serial_manager, AP_SerialManager::SerialProtocol protocol, uint8_t instance)
{
serialmanager_p = &serial_manager;
// search for serial port
AP_HAL::UARTDriver *uart;
uart = serial_manager.find_serial(protocol, instance);
if (uart == nullptr) {
// return immediately if not found
return;
}
// get associated mavlink channel
mavlink_channel_t mav_chan;
if (!serial_manager.get_mavlink_channel(protocol, instance, mav_chan)) {
// return immediately in unlikely case mavlink channel cannot be found
return;
}
/*
Now try to cope with SiK radios that may be stuck in bootloader
mode because CTS was held while powering on. This tells the
bootloader to wait for a firmware. It affects any SiK radio with
CTS connected that is externally powered. To cope we send 0x30
0x20 at 115200 on startup, which tells the bootloader to reset
and boot normally
*/
uart->begin(115200);
AP_HAL::UARTDriver::flow_control old_flow_control = uart->get_flow_control();
uart->set_flow_control(AP_HAL::UARTDriver::FLOW_CONTROL_DISABLE);
for (uint8_t i=0; i<3; i++) {
hal.scheduler->delay(1);
uart->write(0x30);
uart->write(0x20);
}
// since tcdrain() and TCSADRAIN may not be implemented...
hal.scheduler->delay(1);
uart->set_flow_control(old_flow_control);
// now change back to desired baudrate
uart->begin(serial_manager.find_baudrate(protocol, instance));
// and init the gcs instance
init(uart, mav_chan);
AP_SerialManager::SerialProtocol mavlink_protocol = serialmanager_p->get_mavlink_protocol(mav_chan);
mavlink_status_t *status = mavlink_get_channel_status(chan);
if (status == nullptr) {
return;
}
if (mavlink_protocol == AP_SerialManager::SerialProtocol_MAVLink2) {
// load signing key
load_signing_key();
if (status->signing == nullptr) {
// if signing is off start by sending MAVLink1.
status->flags |= MAVLINK_STATUS_FLAG_OUT_MAVLINK1;
}
// announce that we are MAVLink2 capable
hal.util->set_capabilities(MAV_PROTOCOL_CAPABILITY_MAVLINK2);
} else if (status) {
// user has asked to only send MAVLink1
status->flags |= MAVLINK_STATUS_FLAG_OUT_MAVLINK1;
}
if (chan == MAVLINK_COMM_0) {
// Always start with MAVLink1 on first port for now, to allow for recovery
// after experiments with MAVLink2
status->flags |= MAVLINK_STATUS_FLAG_OUT_MAVLINK1;
}
}
/**
* @brief Send the next pending waypoint, called from deferred message
* handling code
*/
void
GCS_MAVLINK::queued_waypoint_send()
{
if (initialised &&
waypoint_receiving &&
waypoint_request_i <= waypoint_request_last) {
mavlink_msg_mission_request_send(
chan,
waypoint_dest_sysid,
waypoint_dest_compid,
waypoint_request_i,
MAV_MISSION_TYPE_MISSION);
}
}
void GCS_MAVLINK::reset_cli_timeout() {
_cli_timeout = AP_HAL::millis();
}
void GCS_MAVLINK::send_meminfo(void)
{
unsigned __brkval = 0;
uint32_t memory = hal.util->available_memory();
mavlink_msg_meminfo_send(chan, __brkval, memory & 0xFFFF, memory);
}
// report power supply status
void GCS_MAVLINK::send_power_status(void)
{
mavlink_msg_power_status_send(chan,
hal.analogin->board_voltage() * 1000,
hal.analogin->servorail_voltage() * 1000,
hal.analogin->power_status_flags());
}
void GCS_MAVLINK::send_battery_status(const AP_BattMonitor &battery, const uint8_t instance) const
{
// catch the battery backend not supporting the required number of cells
static_assert(sizeof(AP_BattMonitor::cells) >= (sizeof(uint16_t) * MAVLINK_MSG_BATTERY_STATUS_FIELD_VOLTAGES_LEN),
"Not enough battery cells for the MAVLink message");
float temp;
bool got_temperature = battery.get_temperature(temp, instance);
mavlink_msg_battery_status_send(chan,
instance, // id
MAV_BATTERY_FUNCTION_UNKNOWN, // function
MAV_BATTERY_TYPE_UNKNOWN, // type
got_temperature ? ((int16_t) (temp * 100)) : INT16_MAX, // temperature. INT16_MAX if unknown
battery.get_cell_voltages(instance).cells, // cell voltages
battery.has_current(instance) ? battery.current_amps(instance) * 100 : -1, // current
battery.has_current(instance) ? battery.current_total_mah(instance) : -1, // total current
-1, // joules used
battery.capacity_remaining_pct(instance));
}
// returns true if all battery instances were reported
bool GCS_MAVLINK::send_battery_status(const AP_BattMonitor &battery) const
{
for(uint8_t i = 0; i < battery.num_instances(); i++) {
CHECK_PAYLOAD_SIZE(BATTERY_STATUS);
send_battery_status(battery, i);
}
return true;
}
void GCS_MAVLINK::send_distance_sensor(const RangeFinder &rangefinder, const uint8_t instance) const
{
if (rangefinder.status(instance) != RangeFinder::RangeFinder_NotConnected &&
rangefinder.status(instance) != RangeFinder::RangeFinder_NoData) {
mavlink_msg_distance_sensor_send(
chan,
AP_HAL::millis(), // time since system boot TODO: take time of measurement
rangefinder.min_distance_cm(instance), // minimum distance the sensor can measure in centimeters
rangefinder.max_distance_cm(instance), // maximum distance the sensor can measure in centimeters
rangefinder.distance_cm(instance), // current distance reading
rangefinder.get_sensor_type(instance), // type from MAV_DISTANCE_SENSOR enum
instance, // onboard ID of the sensor == instance
rangefinder.get_orientation(instance), // direction the sensor faces from MAV_SENSOR_ORIENTATION enum
0); // Measurement covariance in centimeters, 0 for unknown / invalid readings
}
}
bool GCS_MAVLINK::send_distance_sensor(const RangeFinder &rangefinder) const
{
for (uint8_t i = 0; i < RANGEFINDER_MAX_INSTANCES; i++) {
CHECK_PAYLOAD_SIZE(DISTANCE_SENSOR);
send_distance_sensor(rangefinder, i);
}
return true;
}
void GCS_MAVLINK::send_distance_sensor_downward(const RangeFinder &rangefinder) const
{
// exit immediately if rangefinder is disabled or not downward looking
if (!rangefinder.has_data_orient(ROTATION_PITCH_270)) {
return;
}
uint8_t instance;
rangefinder.find_instance(ROTATION_PITCH_270, instance);
send_distance_sensor(rangefinder, instance);
}
void GCS_MAVLINK::send_rangefinder_downward(const RangeFinder &rangefinder) const
{
// exit immediately if rangefinder is disabled or not downward looking
if (!rangefinder.has_data_orient(ROTATION_PITCH_270)) {
// no sonar to report
return;
}
mavlink_msg_rangefinder_send(
chan,
rangefinder.distance_cm_orient(ROTATION_PITCH_270) * 0.01f,
rangefinder.voltage_mv_orient(ROTATION_PITCH_270) * 0.001f);
}
bool GCS_MAVLINK::send_proximity(const AP_Proximity &proximity) const
{
// return immediately if no proximity sensor is present
if (proximity.get_status() == AP_Proximity::Proximity_NotConnected) {
return false;
}
// send horizontal distances
AP_Proximity::Proximity_Distance_Array dist_array;
if (proximity.get_horizontal_distances(dist_array)) {
for (uint8_t i = 0; i < PROXIMITY_MAX_DIRECTION; i++) {
CHECK_PAYLOAD_SIZE(DISTANCE_SENSOR);
mavlink_msg_distance_sensor_send(
chan,
AP_HAL::millis(), // time since system boot
(uint16_t)(proximity.distance_min() * 100.0f), // minimum distance the sensor can measure in centimeters
(uint16_t)(proximity.distance_max() * 100.0f), // maximum distance the sensor can measure in centimeters
(uint16_t)(dist_array.distance[i] * 100.0f), // current distance reading
MAV_DISTANCE_SENSOR_LASER, // type from MAV_DISTANCE_SENSOR enum
PROXIMITY_SENSOR_ID_START + i, // onboard ID of the sensor
dist_array.orientation[i], // direction the sensor faces from MAV_SENSOR_ORIENTATION enum
0); // Measurement covariance in centimeters, 0 for unknown / invalid readings
}
}
// send upward distance
float dist_up;
if (proximity.get_upward_distance(dist_up)) {
CHECK_PAYLOAD_SIZE(DISTANCE_SENSOR);
mavlink_msg_distance_sensor_send(
chan,
AP_HAL::millis(), // time since system boot
(uint16_t)(proximity.distance_min() * 100.0f), // minimum distance the sensor can measure in centimeters
(uint16_t)(proximity.distance_max() * 100.0f), // maximum distance the sensor can measure in centimeters
(uint16_t)(dist_up * 100.0f), // current distance reading
MAV_DISTANCE_SENSOR_LASER, // type from MAV_DISTANCE_SENSOR enum
PROXIMITY_SENSOR_ID_START + PROXIMITY_MAX_DIRECTION + 1, // onboard ID of the sensor
MAV_SENSOR_ROTATION_PITCH_90, // direction upwards
0); // Measurement covariance in centimeters, 0 for unknown / invalid readings
}
return true;
}
// report AHRS2 state
void GCS_MAVLINK::send_ahrs2(AP_AHRS &ahrs)
{
#if AP_AHRS_NAVEKF_AVAILABLE
Vector3f euler;
struct Location loc {};
if (ahrs.get_secondary_attitude(euler)) {
mavlink_msg_ahrs2_send(chan,
euler.x,
euler.y,
euler.z,
loc.alt*1.0e-2f,
loc.lat,
loc.lng);
}
AP_AHRS_NavEKF &_ahrs = reinterpret_cast(ahrs);
if (_ahrs.get_NavEKF2().activeCores() > 0 &&
HAVE_PAYLOAD_SPACE(chan, AHRS3)) {
_ahrs.get_NavEKF2().getLLH(loc);
_ahrs.get_NavEKF2().getEulerAngles(-1,euler);
mavlink_msg_ahrs3_send(chan,
euler.x,
euler.y,
euler.z,
loc.alt*1.0e-2f,
loc.lat,
loc.lng,
0, 0, 0, 0);
}
#endif
}
/*
handle a MISSION_REQUEST_LIST mavlink packet
*/
void GCS_MAVLINK::handle_mission_request_list(AP_Mission &mission, mavlink_message_t *msg)
{
// decode
mavlink_mission_request_list_t packet;
mavlink_msg_mission_request_list_decode(msg, &packet);
// reply with number of commands in the mission. The GCS will then request each command separately
mavlink_msg_mission_count_send(chan,msg->sysid, msg->compid, mission.num_commands(),
MAV_MISSION_TYPE_MISSION);
// set variables to help handle the expected sending of commands to the GCS
waypoint_receiving = false; // record that we are sending commands (i.e. not receiving)
waypoint_dest_sysid = msg->sysid; // record system id of GCS who has requested the commands
waypoint_dest_compid = msg->compid; // record component id of GCS who has requested the commands
}
/*
handle a MISSION_REQUEST mavlink packet
*/
void GCS_MAVLINK::handle_mission_request(AP_Mission &mission, mavlink_message_t *msg)
{
AP_Mission::Mission_Command cmd;
if (msg->msgid == MAVLINK_MSG_ID_MISSION_REQUEST_INT) {
// decode
mavlink_mission_request_int_t packet;
mavlink_msg_mission_request_int_decode(msg, &packet);
// retrieve mission from eeprom
if (!mission.read_cmd_from_storage(packet.seq, cmd)) {
goto mission_item_send_failed;
}
mavlink_mission_item_int_t ret_packet;
memset(&ret_packet, 0, sizeof(ret_packet));
if (!AP_Mission::mission_cmd_to_mavlink_int(cmd, ret_packet)) {
goto mission_item_send_failed;
}
// set packet's current field to 1 if this is the command being executed
if (cmd.id == (uint16_t)mission.get_current_nav_cmd().index) {
ret_packet.current = 1;
} else {
ret_packet.current = 0;
}
// set auto continue to 1
ret_packet.autocontinue = 1; // 1 (true), 0 (false)
/*
avoid the _send() function to save memory, as it avoids
the stack usage of the _send() function by using the already
declared ret_packet above
*/
ret_packet.target_system = msg->sysid;
ret_packet.target_component = msg->compid;
ret_packet.seq = packet.seq;
ret_packet.command = cmd.id;
_mav_finalize_message_chan_send(chan,
MAVLINK_MSG_ID_MISSION_ITEM_INT,
(const char *)&ret_packet,
MAVLINK_MSG_ID_MISSION_ITEM_MIN_LEN,
MAVLINK_MSG_ID_MISSION_ITEM_INT_LEN,
MAVLINK_MSG_ID_MISSION_ITEM_INT_CRC);
} else {
// decode
mavlink_mission_request_t packet;
mavlink_msg_mission_request_decode(msg, &packet);
if (packet.seq != 0 && // always allow HOME to be read
packet.seq >= mission.num_commands()) {
// try to educate the GCS on the actual size of the mission:
mavlink_msg_mission_count_send(chan,msg->sysid, msg->compid, mission.num_commands(),
MAV_MISSION_TYPE_MISSION);
goto mission_item_send_failed;
}
// retrieve mission from eeprom
if (!mission.read_cmd_from_storage(packet.seq, cmd)) {
goto mission_item_send_failed;
}
mavlink_mission_item_t ret_packet;
memset(&ret_packet, 0, sizeof(ret_packet));
if (!AP_Mission::mission_cmd_to_mavlink(cmd, ret_packet)) {
goto mission_item_send_failed;
}
// set packet's current field to 1 if this is the command being executed
if (cmd.id == (uint16_t)mission.get_current_nav_cmd().index) {
ret_packet.current = 1;
} else {
ret_packet.current = 0;
}
// set auto continue to 1
ret_packet.autocontinue = 1; // 1 (true), 0 (false)
/*
avoid the _send() function to save memory, as it avoids
the stack usage of the _send() function by using the already
declared ret_packet above
*/
ret_packet.target_system = msg->sysid;
ret_packet.target_component = msg->compid;
ret_packet.seq = packet.seq;
ret_packet.command = cmd.id;
_mav_finalize_message_chan_send(chan,
MAVLINK_MSG_ID_MISSION_ITEM,
(const char *)&ret_packet,
MAVLINK_MSG_ID_MISSION_ITEM_MIN_LEN,
MAVLINK_MSG_ID_MISSION_ITEM_LEN,
MAVLINK_MSG_ID_MISSION_ITEM_CRC);
}
return;
mission_item_send_failed:
// send failure message
mavlink_msg_mission_ack_send(chan, msg->sysid, msg->compid, MAV_MISSION_ERROR,
MAV_MISSION_TYPE_MISSION);
}
/*
handle a MISSION_SET_CURRENT mavlink packet
*/
void GCS_MAVLINK::handle_mission_set_current(AP_Mission &mission, mavlink_message_t *msg)
{
// decode
mavlink_mission_set_current_t packet;
mavlink_msg_mission_set_current_decode(msg, &packet);
// set current command
if (mission.set_current_cmd(packet.seq)) {
mavlink_msg_mission_current_send(chan, packet.seq);
}
}
/*
handle a MISSION_COUNT mavlink packet
*/
void GCS_MAVLINK::handle_mission_count(AP_Mission &mission, mavlink_message_t *msg)
{
// decode
mavlink_mission_count_t packet;
mavlink_msg_mission_count_decode(msg, &packet);
// start waypoint receiving
if (packet.count > mission.num_commands_max()) {
// send NAK
mavlink_msg_mission_ack_send(chan, msg->sysid, msg->compid, MAV_MISSION_NO_SPACE,
MAV_MISSION_TYPE_MISSION);
return;
}
// new mission arriving, truncate mission to be the same length
mission.truncate(packet.count);
// set variables to help handle the expected receiving of commands from the GCS
waypoint_timelast_receive = AP_HAL::millis(); // set time we last received commands to now
waypoint_receiving = true; // record that we expect to receive commands
waypoint_request_i = 0; // reset the next expected command number to zero
waypoint_request_last = packet.count; // record how many commands we expect to receive
waypoint_timelast_request = 0; // set time we last requested commands to zero
}
/*
handle a MISSION_CLEAR_ALL mavlink packet
*/
void GCS_MAVLINK::handle_mission_clear_all(AP_Mission &mission, mavlink_message_t *msg)
{
// decode
mavlink_mission_clear_all_t packet;
mavlink_msg_mission_clear_all_decode(msg, &packet);
// clear all waypoints
if (mission.clear()) {
// send ack
mavlink_msg_mission_ack_send(chan, msg->sysid, msg->compid, MAV_RESULT_ACCEPTED,
MAV_MISSION_TYPE_MISSION);
}else{
// send nack
mavlink_msg_mission_ack_send(chan, msg->sysid, msg->compid, MAV_MISSION_ERROR,
MAV_MISSION_TYPE_MISSION);
}
}
/*
handle a MISSION_WRITE_PARTIAL_LIST mavlink packet
*/
void GCS_MAVLINK::handle_mission_write_partial_list(AP_Mission &mission, mavlink_message_t *msg)
{
// decode
mavlink_mission_write_partial_list_t packet;
mavlink_msg_mission_write_partial_list_decode(msg, &packet);
// start waypoint receiving
if ((unsigned)packet.start_index > mission.num_commands() ||
(unsigned)packet.end_index > mission.num_commands() ||
packet.end_index < packet.start_index) {
send_text(MAV_SEVERITY_WARNING,"Flight plan update rejected");
return;
}
waypoint_timelast_receive = AP_HAL::millis();
waypoint_timelast_request = 0;
waypoint_receiving = true;
waypoint_request_i = packet.start_index;
waypoint_request_last= packet.end_index;
}
/*
handle a GIMBAL_REPORT mavlink packet
*/
void GCS_MAVLINK::handle_gimbal_report(AP_Mount &mount, mavlink_message_t *msg) const
{
mount.handle_gimbal_report(chan, msg);
}
void GCS_MAVLINK::send_text(MAV_SEVERITY severity, const char *fmt, ...)
{
char text[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN] {};
va_list arg_list;
va_start(arg_list, fmt);
hal.util->vsnprintf((char *)text, sizeof(text), fmt, arg_list);
va_end(arg_list);
gcs().send_statustext(severity, (1< 95 && stream_slowdown > 10) {
// the buffer has plenty of space, speed up a lot
stream_slowdown -= 2;
} else if (packet.txbuf > 90 && stream_slowdown != 0) {
// the buffer has enough space, speed up a bit
stream_slowdown--;
}
//log rssi, noise, etc if logging Performance monitoring data
if (log_radio) {
dataflash.Log_Write_Radio(packet);
}
}
/*
handle an incoming mission item
return true if this is the last mission item, otherwise false
*/
bool GCS_MAVLINK::handle_mission_item(mavlink_message_t *msg, AP_Mission &mission)
{
MAV_MISSION_RESULT result = MAV_MISSION_ACCEPTED;
struct AP_Mission::Mission_Command cmd = {};
bool mission_is_complete = false;
uint16_t seq=0;
uint16_t current = 0;
if (msg->msgid == MAVLINK_MSG_ID_MISSION_ITEM) {
mavlink_mission_item_t packet;
mavlink_msg_mission_item_decode(msg, &packet);
// convert mavlink packet to mission command
result = AP_Mission::mavlink_to_mission_cmd(packet, cmd);
if (result != MAV_MISSION_ACCEPTED) {
goto mission_ack;
}
seq = packet.seq;
current = packet.current;
} else {
mavlink_mission_item_int_t packet;
mavlink_msg_mission_item_int_decode(msg, &packet);
// convert mavlink packet to mission command
result = AP_Mission::mavlink_int_to_mission_cmd(packet, cmd);
if (result != MAV_MISSION_ACCEPTED) {
goto mission_ack;
}
seq = packet.seq;
current = packet.current;
}
if (current == 2) {
// current = 2 is a flag to tell us this is a "guided mode"
// waypoint and not for the mission
result = (handle_guided_request(cmd) ? MAV_MISSION_ACCEPTED
: MAV_MISSION_ERROR) ;
// verify we received the command
goto mission_ack;
}
if (current == 3) {
//current = 3 is a flag to tell us this is a alt change only
// add home alt if needed
handle_change_alt_request(cmd);
// verify we recevied the command
result = MAV_MISSION_ACCEPTED;
goto mission_ack;
}
// Check if receiving waypoints (mission upload expected)
if (!waypoint_receiving) {
result = MAV_MISSION_ERROR;
goto mission_ack;
}
// check if this is the requested waypoint
if (seq != waypoint_request_i) {
result = MAV_MISSION_INVALID_SEQUENCE;
goto mission_ack;
}
// sanity check for DO_JUMP command
if (cmd.id == MAV_CMD_DO_JUMP) {
if ((cmd.content.jump.target >= mission.num_commands() && cmd.content.jump.target >= waypoint_request_last) || cmd.content.jump.target == 0) {
result = MAV_MISSION_ERROR;
goto mission_ack;
}
}
// if command index is within the existing list, replace the command
if (seq < mission.num_commands()) {
if (mission.replace_cmd(seq,cmd)) {
result = MAV_MISSION_ACCEPTED;
}else{
result = MAV_MISSION_ERROR;
goto mission_ack;
}
// if command is at the end of command list, add the command
} else if (seq == mission.num_commands()) {
if (mission.add_cmd(cmd)) {
result = MAV_MISSION_ACCEPTED;
}else{
result = MAV_MISSION_ERROR;
goto mission_ack;
}
// if beyond the end of the command list, return an error
} else {
result = MAV_MISSION_ERROR;
goto mission_ack;
}
// update waypoint receiving state machine
waypoint_timelast_receive = AP_HAL::millis();
waypoint_request_i++;
if (waypoint_request_i >= waypoint_request_last) {
mavlink_msg_mission_ack_send_buf(
msg,
chan,
msg->sysid,
msg->compid,
MAV_MISSION_ACCEPTED,
MAV_MISSION_TYPE_MISSION);
send_text(MAV_SEVERITY_INFO,"Flight plan received");
waypoint_receiving = false;
mission_is_complete = true;
// XXX ignores waypoint radius for individual waypoints, can
// only set WP_RADIUS parameter
} else {
waypoint_timelast_request = AP_HAL::millis();
// if we have enough space, then send the next WP immediately
if (HAVE_PAYLOAD_SPACE(chan, MISSION_ITEM)) {
queued_waypoint_send();
} else {
send_message(MSG_NEXT_WAYPOINT);
}
}
return mission_is_complete;
mission_ack:
// we are rejecting the mission/waypoint
mavlink_msg_mission_ack_send_buf(
msg,
chan,
msg->sysid,
msg->compid,
result,
MAV_MISSION_TYPE_MISSION);
return mission_is_complete;
}
void
GCS_MAVLINK::handle_gps_inject(const mavlink_message_t *msg, AP_GPS &gps)
{
mavlink_gps_inject_data_t packet;
mavlink_msg_gps_inject_data_decode(msg, &packet);
//TODO: check target
gps.inject_data(packet.data, packet.len);
}
// send a message using mavlink, handling message queueing
void GCS_MAVLINK::send_message(enum ap_message id)
{
uint8_t i, nextid;
if (id == MSG_HEARTBEAT) {
save_signing_timestamp(false);
}
// see if we can send the deferred messages, if any
while (num_deferred_messages != 0) {
if (!try_send_message(deferred_messages[next_deferred_message])) {
break;
}
next_deferred_message++;
if (next_deferred_message == MSG_RETRY_DEFERRED) {
next_deferred_message = 0;
}
num_deferred_messages--;
}
if (id == MSG_RETRY_DEFERRED) {
return;
}
// this message id might already be deferred
for (i=0, nextid = next_deferred_message; i < num_deferred_messages; i++) {
if (deferred_messages[nextid] == id) {
// it's already deferred, discard
return;
}
nextid++;
if (nextid == MSG_RETRY_DEFERRED) {
nextid = 0;
}
}
if (num_deferred_messages != 0 ||
!try_send_message(id)) {
// can't send it now, so defer it
if (num_deferred_messages == MSG_RETRY_DEFERRED) {
// the defer buffer is full, discard
return;
}
nextid = next_deferred_message + num_deferred_messages;
if (nextid >= MSG_RETRY_DEFERRED) {
nextid -= MSG_RETRY_DEFERRED;
}
deferred_messages[nextid] = id;
num_deferred_messages++;
}
}
void GCS_MAVLINK::packetReceived(const mavlink_status_t &status,
mavlink_message_t &msg)
{
// we exclude radio packets to make it possible to use the
// CLI over the radio
if (msg.msgid != MAVLINK_MSG_ID_RADIO && msg.msgid != MAVLINK_MSG_ID_RADIO_STATUS) {
mavlink_active |= (1U<<(chan-MAVLINK_COMM_0));
}
if (!(status.flags & MAVLINK_STATUS_FLAG_IN_MAVLINK1) &&
(status.flags & MAVLINK_STATUS_FLAG_OUT_MAVLINK1) &&
serialmanager_p &&
serialmanager_p->get_mavlink_protocol(chan) == AP_SerialManager::SerialProtocol_MAVLink2) {
// if we receive any MAVLink2 packets on a connection
// currently sending MAVLink1 then switch to sending
// MAVLink2
mavlink_status_t *cstatus = mavlink_get_channel_status(chan);
if (cstatus != nullptr) {
cstatus->flags &= ~MAVLINK_STATUS_FLAG_OUT_MAVLINK1;
}
}
// if a snoop handler has been setup then use it
if (msg_snoop != nullptr) {
msg_snoop(&msg);
}
if (routing.check_and_forward(chan, &msg) &&
accept_packet(status, msg)) {
handleMessage(&msg);
}
}
void
GCS_MAVLINK::update(run_cli_fn run_cli, uint32_t max_time_us)
{
// receive new packets
mavlink_message_t msg;
mavlink_status_t status;
uint32_t tstart_us = AP_HAL::micros();
hal.util->perf_begin(_perf_update);
status.packet_rx_drop_count = 0;
// process received bytes
uint16_t nbytes = comm_get_available(chan);
for (uint16_t i=0; iperf_begin(_perf_packet);
packetReceived(status, msg);
hal.util->perf_end(_perf_packet);
parsed_packet = true;
}
if (parsed_packet || i % 100 == 0) {
// make sure we don't spend too much time parsing mavlink messages
if (AP_HAL::micros() - tstart_us > max_time_us) {
break;
}
}
}
if (!waypoint_receiving) {
hal.util->perf_end(_perf_update);
return;
}
uint32_t tnow = AP_HAL::millis();
uint32_t wp_recv_time = 1000U + (stream_slowdown*20);
// stop waypoint receiving if timeout
if (waypoint_receiving && (tnow - waypoint_timelast_receive) > wp_recv_time+waypoint_receive_timeout) {
waypoint_receiving = false;
} else if (waypoint_receiving &&
(tnow - waypoint_timelast_request) > wp_recv_time) {
waypoint_timelast_request = tnow;
send_message(MSG_NEXT_WAYPOINT);
}
hal.util->perf_end(_perf_update);
}
/*
send raw GPS position information (GPS_RAW_INT, GPS2_RAW, GPS_RTK and GPS2_RTK).
returns true if messages fit into transmit buffer, false otherwise.
*/
bool GCS_MAVLINK::send_gps_raw(AP_GPS &gps)
{
if (HAVE_PAYLOAD_SPACE(chan, GPS_RAW_INT)) {
gps.send_mavlink_gps_raw(chan);
} else {
return false;
}
if (gps.highest_supported_status(0) > AP_GPS::GPS_OK_FIX_3D) {
if (HAVE_PAYLOAD_SPACE(chan, GPS_RTK)) {
gps.send_mavlink_gps_rtk(chan);
}
}
if (gps.num_sensors() > 1 && gps.status(1) > AP_GPS::NO_GPS) {
if (HAVE_PAYLOAD_SPACE(chan, GPS2_RAW)) {
gps.send_mavlink_gps2_raw(chan);
}
if (gps.highest_supported_status(1) > AP_GPS::GPS_OK_FIX_3D) {
if (HAVE_PAYLOAD_SPACE(chan, GPS2_RTK)) {
gps.send_mavlink_gps2_rtk(chan);
}
}
}
//TODO: Should check what else managed to get through...
return true;
}
/*
send the SYSTEM_TIME message
*/
void GCS_MAVLINK::send_system_time(AP_GPS &gps)
{
mavlink_msg_system_time_send(
chan,
gps.time_epoch_usec(),
AP_HAL::millis());
}
/*
send RC_CHANNELS messages
*/
void GCS_MAVLINK::send_radio_in(uint8_t receiver_rssi)
{
uint32_t now = AP_HAL::millis();
mavlink_status_t *status = mavlink_get_channel_status(chan);
uint16_t values[18];
memset(values, 0, sizeof(values));
hal.rcin->read(values, 18);
if (status && (status->flags & MAVLINK_STATUS_FLAG_OUT_MAVLINK1)) {
// for mavlink1 send RC_CHANNELS_RAW, for compatibility with OSD implementations
mavlink_msg_rc_channels_raw_send(
chan,
now,
0,
values[0],
values[1],
values[2],
values[3],
values[4],
values[5],
values[6],
values[7],
receiver_rssi);
if (!HAVE_PAYLOAD_SPACE(chan, RC_CHANNELS)) {
// can't fit RC_CHANNELS
return;
}
}
mavlink_msg_rc_channels_send(
chan,
now,
hal.rcin->num_channels(),
values[0],
values[1],
values[2],
values[3],
values[4],
values[5],
values[6],
values[7],
values[8],
values[9],
values[10],
values[11],
values[12],
values[13],
values[14],
values[15],
values[16],
values[17],
receiver_rssi);
}
void GCS_MAVLINK::send_raw_imu(const AP_InertialSensor &ins, const Compass &compass)
{
const Vector3f &accel = ins.get_accel(0);
const Vector3f &gyro = ins.get_gyro(0);
Vector3f mag;
if (compass.get_count() >= 1) {
mag = compass.get_field(0);
} else {
mag.zero();
}
mavlink_msg_raw_imu_send(
chan,
AP_HAL::micros(),
accel.x * 1000.0f / GRAVITY_MSS,
accel.y * 1000.0f / GRAVITY_MSS,
accel.z * 1000.0f / GRAVITY_MSS,
gyro.x * 1000.0f,
gyro.y * 1000.0f,
gyro.z * 1000.0f,
mag.x,
mag.y,
mag.z);
if (ins.get_gyro_count() <= 1 &&
ins.get_accel_count() <= 1 &&
compass.get_count() <= 1) {
return;
}
if (!HAVE_PAYLOAD_SPACE(chan, SCALED_IMU2)) {
return;
}
const Vector3f &accel2 = ins.get_accel(1);
const Vector3f &gyro2 = ins.get_gyro(1);
if (compass.get_count() >= 2) {
mag = compass.get_field(1);
} else {
mag.zero();
}
mavlink_msg_scaled_imu2_send(
chan,
AP_HAL::millis(),
accel2.x * 1000.0f / GRAVITY_MSS,
accel2.y * 1000.0f / GRAVITY_MSS,
accel2.z * 1000.0f / GRAVITY_MSS,
gyro2.x * 1000.0f,
gyro2.y * 1000.0f,
gyro2.z * 1000.0f,
mag.x,
mag.y,
mag.z);
if (ins.get_gyro_count() <= 2 &&
ins.get_accel_count() <= 2 &&
compass.get_count() <= 2) {
return;
}
if (!HAVE_PAYLOAD_SPACE(chan, SCALED_IMU3)) {
return;
}
const Vector3f &accel3 = ins.get_accel(2);
const Vector3f &gyro3 = ins.get_gyro(2);
if (compass.get_count() >= 3) {
mag = compass.get_field(2);
} else {
mag.zero();
}
mavlink_msg_scaled_imu3_send(
chan,
AP_HAL::millis(),
accel3.x * 1000.0f / GRAVITY_MSS,
accel3.y * 1000.0f / GRAVITY_MSS,
accel3.z * 1000.0f / GRAVITY_MSS,
gyro3.x * 1000.0f,
gyro3.y * 1000.0f,
gyro3.z * 1000.0f,
mag.x,
mag.y,
mag.z);
}
void GCS_MAVLINK::send_scaled_pressure(AP_Baro &barometer)
{
uint32_t now = AP_HAL::millis();
float pressure = barometer.get_pressure(0);
mavlink_msg_scaled_pressure_send(
chan,
now,
pressure*0.01f, // hectopascal
(pressure - barometer.get_ground_pressure(0))*0.01f, // hectopascal
barometer.get_temperature(0)*100); // 0.01 degrees C
if (barometer.num_instances() > 1 &&
HAVE_PAYLOAD_SPACE(chan, SCALED_PRESSURE2)) {
pressure = barometer.get_pressure(1);
mavlink_msg_scaled_pressure2_send(
chan,
now,
pressure*0.01f, // hectopascal
(pressure - barometer.get_ground_pressure(1))*0.01f, // hectopascal
barometer.get_temperature(1)*100); // 0.01 degrees C
}
if (barometer.num_instances() > 2 &&
HAVE_PAYLOAD_SPACE(chan, SCALED_PRESSURE3)) {
pressure = barometer.get_pressure(2);
mavlink_msg_scaled_pressure3_send(
chan,
now,
pressure*0.01f, // hectopascal
(pressure - barometer.get_ground_pressure(2))*0.01f, // hectopascal
barometer.get_temperature(2)*100); // 0.01 degrees C
}
}
void GCS_MAVLINK::send_sensor_offsets(const AP_InertialSensor &ins, const Compass &compass, AP_Baro &barometer)
{
// run this message at a much lower rate - otherwise it
// pointlessly wastes quite a lot of bandwidth
static uint8_t counter;
if (counter++ < 10) {
return;
}
counter = 0;
const Vector3f &mag_offsets = compass.get_offsets(0);
const Vector3f &accel_offsets = ins.get_accel_offsets(0);
const Vector3f &gyro_offsets = ins.get_gyro_offsets(0);
mavlink_msg_sensor_offsets_send(chan,
mag_offsets.x,
mag_offsets.y,
mag_offsets.z,
compass.get_declination(),
barometer.get_pressure(),
barometer.get_temperature()*100,
gyro_offsets.x,
gyro_offsets.y,
gyro_offsets.z,
accel_offsets.x,
accel_offsets.y,
accel_offsets.z);
}
void GCS_MAVLINK::send_ahrs(AP_AHRS &ahrs)
{
const Vector3f &omega_I = ahrs.get_gyro_drift();
mavlink_msg_ahrs_send(
chan,
omega_I.x,
omega_I.y,
omega_I.z,
0,
0,
ahrs.get_error_rp(),
ahrs.get_error_yaw());
}
/*
send a statustext text string to specific MAVLink bitmask
*/
void GCS::send_statustext(MAV_SEVERITY severity, uint8_t dest_bitmask, const char *text)
{
if (dataflash_p != nullptr) {
dataflash_p->Log_Write_Message(text);
}
// add statustext message to FrSky lib queue
if (frsky_telemetry_p != NULL) {
frsky_telemetry_p->queue_message(severity, text);
}
// filter destination ports to only allow active ports.
statustext_t statustext{};
statustext.bitmask = (GCS_MAVLINK::active_channel_mask() | GCS_MAVLINK::streaming_channel_mask() ) & dest_bitmask;
if (!statustext.bitmask) {
// nowhere to send
return;
}
statustext.msg.severity = severity;
strncpy(statustext.msg.text, text, sizeof(statustext.msg.text));
// The force push will ensure comm links do not block other comm links forever if they fail.
// If we push to a full buffer then we overwrite the oldest entry, effectively removing the
// block but not until the buffer fills up.
_statustext_queue.push_force(statustext);
// try and send immediately if possible
service_statustext();
AP_Notify *notify = AP_Notify::instance();
if (notify) {
notify->send_text(text);
}
}
/*
send a statustext message to specific MAVLink connections in a bitmask
*/
void GCS::service_statustext(void)
{
// create bitmask of what mavlink ports we should send this text to.
// note, if sending to all ports, we only need to store the bitmask for each and the string only once.
// once we send over a link, clear the port but other busy ports bit may stay allowing for faster links
// to clear the bit and send quickly but slower links to still store the string. Regardless of mixed
// bitrates of ports, a maximum of _status_capacity strings can be buffered. Downside
// is if you have a super slow link mixed with a faster port, if there are _status_capacity
// strings in the slow queue then the next item can not be queued for the faster link
if (_statustext_queue.empty()) {
// nothing to do
return;
}
for (uint8_t idx=0; idx<_status_capacity; ) {
statustext_t *statustext = _statustext_queue[idx];
if (statustext == nullptr) {
break;
}
// try and send to all active mavlink ports listed in the statustext.bitmask
for (uint8_t i=0; ibitmask & chan_bit) {
// something is queued on a port and that's the port index we're looped at
mavlink_channel_t chan_index = (mavlink_channel_t)(MAVLINK_COMM_0+i);
if (HAVE_PAYLOAD_SPACE(chan_index, STATUSTEXT)) {
// we have space so send then clear that channel bit on the mask
mavlink_msg_statustext_send(chan_index, statustext->msg.severity, statustext->msg.text);
statustext->bitmask &= ~chan_bit;
}
}
}
if (statustext->bitmask == 0) {
_statustext_queue.remove(idx);
} else {
// move to next index
idx++;
}
}
}
void GCS::reset_cli_timeout()
{
for (uint8_t i=0; iprintf("%s\n", msg);
if (chan(1).initialised && (chan(1).get_uart() != NULL)) {
chan(1).get_uart()->printf("%s\n", msg);
}
if (num_gcs() > 2 && chan(2).initialised && (chan(2).get_uart() != NULL)) {
chan(2).get_uart()->printf("%s\n", msg);
}
}
}
// report battery2 state
void GCS_MAVLINK::send_battery2(const AP_BattMonitor &battery)
{
if (battery.num_instances() > 1) {
int16_t current;
if (battery.has_current(1)) {
current = battery.current_amps(1) * 100; // 10*mA
} else {
current = -1;
}
mavlink_msg_battery2_send(chan, battery.voltage(1)*1000, current);
}
}
/*
handle a SET_MODE MAVLink message
*/
void GCS_MAVLINK::handle_set_mode(mavlink_message_t* msg, set_mode_fn set_mode)
{
uint8_t result = MAV_RESULT_FAILED;
mavlink_set_mode_t packet;
mavlink_msg_set_mode_decode(msg, &packet);
// only accept custom modes because there is no easy mapping from Mavlink flight modes to AC flight modes
if (packet.base_mode & MAV_MODE_FLAG_CUSTOM_MODE_ENABLED) {
if (set_mode(packet.custom_mode)) {
result = MAV_RESULT_ACCEPTED;
}
} else if (packet.base_mode == MAV_MODE_FLAG_DECODE_POSITION_SAFETY) {
// set the safety switch position. Must be in a command by itself
if (packet.custom_mode == 0) {
// turn safety off (pwm outputs flow to the motors)
hal.rcout->force_safety_off();
result = MAV_RESULT_ACCEPTED;
} else if (packet.custom_mode == 1) {
// turn safety on (no pwm outputs to the motors)
if (hal.rcout->force_safety_on()) {
result = MAV_RESULT_ACCEPTED;
}
}
}
// send ACK or NAK
mavlink_msg_command_ack_send_buf(msg, chan, MAVLINK_MSG_ID_SET_MODE, result);
}
#if AP_AHRS_NAVEKF_AVAILABLE
/*
send OPTICAL_FLOW message
*/
void GCS_MAVLINK::send_opticalflow(AP_AHRS_NavEKF &ahrs, const OpticalFlow &optflow)
{
// exit immediately if no optical flow sensor or not healthy
if (!optflow.healthy()) {
return;
}
// get rates from sensor
const Vector2f &flowRate = optflow.flowRate();
const Vector2f &bodyRate = optflow.bodyRate();
float hagl = 0;
if (ahrs.have_inertial_nav()) {
ahrs.get_hagl(hagl);
}
// populate and send message
mavlink_msg_optical_flow_send(
chan,
AP_HAL::millis(),
0, // sensor id is zero
flowRate.x,
flowRate.y,
bodyRate.x,
bodyRate.y,
optflow.quality(),
hagl, // ground distance (in meters) set to zero
flowRate.x,
flowRate.y);
}
#endif
/*
send AUTOPILOT_VERSION packet
*/
void GCS_MAVLINK::send_autopilot_version(uint8_t major_version, uint8_t minor_version, uint8_t patch_version, uint8_t version_type) const
{
uint32_t flight_sw_version = 0;
uint32_t middleware_sw_version = 0;
uint32_t os_sw_version = 0;
uint32_t board_version = 0;
uint8_t flight_custom_version[8];
uint8_t middleware_custom_version[8];
uint8_t os_custom_version[8];
uint16_t vendor_id = 0;
uint16_t product_id = 0;
uint64_t uid = 0;
flight_sw_version = major_version << (8*3) | \
minor_version << (8*2) | \
patch_version << (8*1) | \
version_type << (8*0);
#if defined(GIT_VERSION)
strncpy((char *)flight_custom_version, GIT_VERSION, 8);
#else
memset(middleware_custom_version,0,8);
#endif
#if defined(PX4_GIT_VERSION)
strncpy((char *)middleware_custom_version, PX4_GIT_VERSION, 8);
#else
memset(middleware_custom_version,0,8);
#endif
#if defined(NUTTX_GIT_VERSION)
strncpy((char *)os_custom_version, NUTTX_GIT_VERSION, 8);
#else
memset(os_custom_version,0,8);
#endif
mavlink_msg_autopilot_version_send(
chan,
hal.util->get_capabilities(),
flight_sw_version,
middleware_sw_version,
os_sw_version,
board_version,
flight_custom_version,
middleware_custom_version,
os_custom_version,
vendor_id,
product_id,
uid
);
}
/*
send LOCAL_POSITION_NED message
*/
void GCS_MAVLINK::send_local_position(const AP_AHRS &ahrs) const
{
Vector3f local_position, velocity;
if (!ahrs.get_relative_position_NED_home(local_position) ||
!ahrs.get_velocity_NED(velocity)) {
// we don't know the position and velocity
return;
}
mavlink_msg_local_position_ned_send(
chan,
AP_HAL::millis(),
local_position.x,
local_position.y,
local_position.z,
velocity.x,
velocity.y,
velocity.z);
}
/*
send VIBRATION message
*/
void GCS_MAVLINK::send_vibration(const AP_InertialSensor &ins) const
{
Vector3f vibration = ins.get_vibration_levels();
mavlink_msg_vibration_send(
chan,
AP_HAL::micros64(),
vibration.x,
vibration.y,
vibration.z,
ins.get_accel_clip_count(0),
ins.get_accel_clip_count(1),
ins.get_accel_clip_count(2));
}
void GCS_MAVLINK::send_home(const Location &home) const
{
if (HAVE_PAYLOAD_SPACE(chan, HOME_POSITION)) {
const float q[4] = {1.0f, 0.0f, 0.0f, 0.0f};
mavlink_msg_home_position_send(
chan,
home.lat,
home.lng,
home.alt * 10,
0.0f, 0.0f, 0.0f,
q,
0.0f, 0.0f, 0.0f);
}
}
/*
wrapper for sending heartbeat
*/
void GCS_MAVLINK::send_heartbeat(uint8_t type, uint8_t base_mode, uint32_t custom_mode, uint8_t system_status)
{
mavlink_msg_heartbeat_send(
chan,
type,
MAV_AUTOPILOT_ARDUPILOTMEGA,
base_mode,
custom_mode,
system_status);
}
float GCS_MAVLINK::adjust_rate_for_stream_trigger(enum streams stream_num)
{
// send at a much lower rate while handling waypoints and
// parameter sends
if ((stream_num != STREAM_PARAMS) &&
(waypoint_receiving || _queued_parameter != nullptr)) {
return 0.25f;
}
return 1.0f;
}
// are we still delaying telemetry to try to avoid Xbee bricking?
bool GCS_MAVLINK::telemetry_delayed() const
{
uint32_t tnow = AP_HAL::millis() >> 10;
if (tnow > telem_delay()) {
return false;
}
if (chan == MAVLINK_COMM_0 && hal.gpio->usb_connected()) {
// this is USB telemetry, so won't be an Xbee
return false;
}
// we're either on the 2nd UART, or no USB cable is connected
// we need to delay telemetry by the TELEM_DELAY time
return true;
}
/*
send SERVO_OUTPUT_RAW
*/
void GCS_MAVLINK::send_servo_output_raw(bool hil)
{
uint16_t values[16] {};
if (hil) {
for (uint8_t i=0; i<16; i++) {
values[i] = SRV_Channels::srv_channel(i)->get_output_pwm();
}
} else {
hal.rcout->read(values, 16);
}
for (uint8_t i=0; i<16; i++) {
if (values[i] == 65535) {
values[i] = 0;
}
}
mavlink_msg_servo_output_raw_send(
chan,
AP_HAL::micros(),
0, // port
values[0], values[1], values[2], values[3],
values[4], values[5], values[6], values[7],
values[8], values[9], values[10], values[11],
values[12], values[13], values[14], values[15]);
}
void GCS_MAVLINK::send_collision_all(const AP_Avoidance::Obstacle &threat, MAV_COLLISION_ACTION behaviour)
{
for (uint8_t i=0; i= MAVLINK_NUM_NON_PAYLOAD_BYTES + MAVLINK_MSG_ID_COLLISION) {
mavlink_msg_collision_send(
chan,
MAV_COLLISION_SRC_ADSB,
threat.src_id,
behaviour,
threat.threat_level,
threat.time_to_closest_approach,
threat.closest_approach_z,
threat.closest_approach_xy
);
}
}
}
}
void GCS_MAVLINK::send_accelcal_vehicle_position(uint32_t position)
{
if (HAVE_PAYLOAD_SPACE(chan, COMMAND_LONG)) {
mavlink_msg_command_long_send(
chan,
0,
0,
MAV_CMD_ACCELCAL_VEHICLE_POS,
0,
(float) position,
0, 0, 0, 0, 0, 0);
}
}
/*
handle a MAV_CMD_PREFLIGHT_REBOOT_SHUTDOWN command
Optionally disable PX4IO overrides. This is done for quadplanes to
prevent the mixer running while rebooting which can start the VTOL
motors. That can be dangerous when a preflight reboot is done with
the pilot close to the aircraft and can also damage the aircraft
*/
uint8_t GCS_MAVLINK::handle_preflight_reboot(const mavlink_command_long_t &packet, bool disable_overrides)
{
if (is_equal(packet.param1,1.0f) || is_equal(packet.param1,3.0f)) {
if (disable_overrides) {
#if CONFIG_HAL_BOARD == HAL_BOARD_PX4 || CONFIG_HAL_BOARD == HAL_BOARD_VRBRAIN
// disable overrides while rebooting
int px4io_fd = open("/dev/px4io", 0);
if (px4io_fd >= 0) {
// disable OVERRIDES so we don't run the mixer while
// rebooting
if (ioctl(px4io_fd, PWM_SERVO_SET_OVERRIDE_OK, 0) != 0) {
hal.console->printf("SET_OVERRIDE_OK failed\n");
}
if (ioctl(px4io_fd, PWM_SERVO_SET_OVERRIDE_IMMEDIATE, 0) != 0) {
hal.console->printf("SET_OVERRIDE_IMMEDIATE failed\n");
}
close(px4io_fd);
}
#endif
}
// force safety on
hal.rcout->force_safety_on();
hal.rcout->force_safety_no_wait();
hal.scheduler->delay(200);
// when packet.param1 == 3 we reboot to hold in bootloader
bool hold_in_bootloader = is_equal(packet.param1,3.0f);
hal.scheduler->reboot(hold_in_bootloader);
return MAV_RESULT_ACCEPTED;
}
return MAV_RESULT_UNSUPPORTED;
}
/*
handle a R/C bind request (for spektrum)
*/
MAV_RESULT GCS_MAVLINK::handle_rc_bind(const mavlink_command_long_t &packet)
{
// initiate bind procedure. We accept the DSM type from either
// param1 or param2 due to a past mixup with what parameter is the
// right one
if (!hal.rcin->rc_bind(packet.param2>0?packet.param2:packet.param1)) {
return MAV_RESULT_FAILED;
}
return MAV_RESULT_ACCEPTED;
}
/*
return a timesync request
Sends back ts1 as received, and tc1 is the local timestamp in usec
*/
void GCS_MAVLINK::handle_timesync(mavlink_message_t *msg)
{
// decode incoming timesync message
mavlink_timesync_t tsync;
mavlink_msg_timesync_decode(msg, &tsync);
// ignore messages in which tc1 field (timestamp 1) has already been filled in
if (tsync.tc1 != 0) {
return;
}
// create new timesync struct with tc1 field as system time in nanoseconds
mavlink_timesync_t rsync;
rsync.tc1 = AP_HAL::micros64() * 1000;
rsync.ts1 = tsync.ts1;
// respond with a timesync message
mavlink_msg_timesync_send(
chan,
rsync.tc1,
rsync.ts1
);
}
void GCS_MAVLINK::handle_statustext(mavlink_message_t *msg)
{
DataFlash_Class *df = DataFlash_Class::instance();
if (df == nullptr) {
return;
}
// ignore any statustext messages not from our GCS:
if (msg->sysid != sysid_my_gcs()) {
return;
}
mavlink_statustext_t packet;
mavlink_msg_statustext_decode(msg, &packet);
char text[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN+1+4] = { 'G','C','S',':'};
memcpy(&text[4], packet.text, MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN);
df->Log_Write_Message(text);
}
void GCS_MAVLINK::handle_common_gps_message(mavlink_message_t *msg)
{
AP_GPS *gps = get_gps();
if (gps == nullptr) {
return;
}
gps->handle_msg(msg);
}
/*
handle messages which don't require vehicle specific data
*/
void GCS_MAVLINK::handle_common_message(mavlink_message_t *msg)
{
switch (msg->msgid) {
case MAVLINK_MSG_ID_SETUP_SIGNING:
handle_setup_signing(msg);
break;
case MAVLINK_MSG_ID_PARAM_REQUEST_READ:
handle_param_request_read(msg);
break;
case MAVLINK_MSG_ID_DEVICE_OP_READ:
handle_device_op_read(msg);
break;
case MAVLINK_MSG_ID_DEVICE_OP_WRITE:
handle_device_op_write(msg);
break;
case MAVLINK_MSG_ID_TIMESYNC:
handle_timesync(msg);
break;
case MAVLINK_MSG_ID_LOG_REQUEST_LIST:
/* fall through */
case MAVLINK_MSG_ID_LOG_REQUEST_DATA:
/* fall through */
case MAVLINK_MSG_ID_LOG_ERASE:
/* fall through */
case MAVLINK_MSG_ID_LOG_REQUEST_END:
/* fall through */
case MAVLINK_MSG_ID_REMOTE_LOG_BLOCK_STATUS:
DataFlash_Class::instance()->handle_mavlink_msg(*this, msg);
break;
case MAVLINK_MSG_ID_MISSION_WRITE_PARTIAL_LIST:
/* fall through */
case MAVLINK_MSG_ID_MISSION_REQUEST_LIST:
/* fall through */
case MAVLINK_MSG_ID_MISSION_COUNT:
/* fall through */
case MAVLINK_MSG_ID_MISSION_CLEAR_ALL:
/* fall through */
case MAVLINK_MSG_ID_MISSION_ITEM:
/* fall through */
case MAVLINK_MSG_ID_MISSION_ITEM_INT:
/* fall through */
case MAVLINK_MSG_ID_MISSION_REQUEST_INT:
/* fall through */
case MAVLINK_MSG_ID_MISSION_REQUEST:
/* fall through */
case MAVLINK_MSG_ID_MISSION_ACK:
/* fall through */
case MAVLINK_MSG_ID_MISSION_SET_CURRENT:
handle_common_mission_message(msg);
break;
case MAVLINK_MSG_ID_GPS_RTCM_DATA:
/* fall through */
case MAVLINK_MSG_ID_GPS_INPUT:
/* fall through */
case MAVLINK_MSG_ID_HIL_GPS:
handle_common_gps_message(msg);
break;
case MAVLINK_MSG_ID_STATUSTEXT:
handle_statustext(msg);
break;
case MAVLINK_MSG_ID_RALLY_POINT:
/* fall through */
case MAVLINK_MSG_ID_RALLY_FETCH_POINT:
handle_common_rally_message(msg);
break;
}
}
void GCS_MAVLINK::handle_common_mission_message(mavlink_message_t *msg)
{
AP_Mission *_mission = get_mission();
if (_mission == nullptr) {
return;
}
switch (msg->msgid) {
case MAVLINK_MSG_ID_MISSION_WRITE_PARTIAL_LIST: // MAV ID: 38
{
handle_mission_write_partial_list(*_mission, msg);
break;
}
// GCS has sent us a mission item, store to EEPROM
case MAVLINK_MSG_ID_MISSION_ITEM: // MAV ID: 39
case MAVLINK_MSG_ID_MISSION_ITEM_INT:
{
if (handle_mission_item(msg, *_mission)) {
DataFlash_Class::instance()->Log_Write_EntireMission(*_mission);
}
break;
}
// read an individual command from EEPROM and send it to the GCS
case MAVLINK_MSG_ID_MISSION_REQUEST_INT:
case MAVLINK_MSG_ID_MISSION_REQUEST: // MAV ID: 40, 51
{
handle_mission_request(*_mission, msg);
break;
}
case MAVLINK_MSG_ID_MISSION_SET_CURRENT: // MAV ID: 41
{
handle_mission_set_current(*_mission, msg);
break;
}
// GCS request the full list of commands, we return just the number and leave the GCS to then request each command individually
case MAVLINK_MSG_ID_MISSION_REQUEST_LIST: // MAV ID: 43
{
handle_mission_request_list(*_mission, msg);
break;
}
// GCS provides the full number of commands it wishes to upload
// individual commands will then be sent from the GCS using the MAVLINK_MSG_ID_MISSION_ITEM message
case MAVLINK_MSG_ID_MISSION_COUNT: // MAV ID: 44
{
handle_mission_count(*_mission, msg);
break;
}
case MAVLINK_MSG_ID_MISSION_CLEAR_ALL: // MAV ID: 45
{
handle_mission_clear_all(*_mission, msg);
break;
}
case MAVLINK_MSG_ID_MISSION_ACK:
/* not used */
break;
}
}
MAV_RESULT GCS_MAVLINK::handle_command_preflight_set_sensor_offsets(const mavlink_command_long_t &packet)
{
Compass *compass = get_compass();
if (compass == nullptr) {
return MAV_RESULT_UNSUPPORTED;
}
uint8_t compassNumber = -1;
if (is_equal(packet.param1, 2.0f)) {
compassNumber = 0;
} else if (is_equal(packet.param1, 5.0f)) {
compassNumber = 1;
} else if (is_equal(packet.param1, 6.0f)) {
compassNumber = 2;
}
if (compassNumber == (uint8_t) -1) {
return MAV_RESULT_FAILED;
}
compass->set_and_save_offsets(compassNumber, packet.param2, packet.param3, packet.param4);
return MAV_RESULT_ACCEPTED;
}
MAV_RESULT GCS_MAVLINK::handle_command_mag_cal(const mavlink_command_long_t &packet)
{
Compass *compass = get_compass();
if (compass == nullptr) {
return MAV_RESULT_UNSUPPORTED;
}
return compass->handle_mag_cal_command(packet);
}
MAV_RESULT GCS_MAVLINK::handle_command_long_message(mavlink_command_long_t &packet)
{
MAV_RESULT result = MAV_RESULT_FAILED;
switch (packet.command) {
case MAV_CMD_DO_START_MAG_CAL:
case MAV_CMD_DO_ACCEPT_MAG_CAL:
case MAV_CMD_DO_CANCEL_MAG_CAL: {
result = handle_command_mag_cal(packet);
break;
}
case MAV_CMD_START_RX_PAIR:
result = handle_rc_bind(packet);
break;
case MAV_CMD_PREFLIGHT_SET_SENSOR_OFFSETS: {
result = handle_command_preflight_set_sensor_offsets(packet);
break;
}
case MAV_CMD_DO_SET_SERVO:
/* fall through */
case MAV_CMD_DO_REPEAT_SERVO:
/* fall through */
case MAV_CMD_DO_SET_RELAY:
/* fall through */
case MAV_CMD_DO_REPEAT_RELAY:
/* fall through */
result = handle_servorelay_message(packet);
break;
default:
result = MAV_RESULT_UNSUPPORTED;
}
return result;
}
GCS &gcs()
{
return *GCS::instance();
}