/**************************************************************************** * * Copyright (c) 2012-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 mavlink_main.h * * MAVLink 2.0 protocol interface definition. * * @author Lorenz Meier * @author Anton Babushkin */ #pragma once #include #include #ifdef __PX4_NUTTX #include #else #include #include #include #endif #if defined(CONFIG_NET) || !defined(__PX4_NUTTX) #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mavlink_command_sender.h" #include "mavlink_events.h" #include "mavlink_messages.h" #include "mavlink_receiver.h" #include "mavlink_shell.h" #include "mavlink_ulog.h" #define DEFAULT_BAUD_RATE 57600 #define DEFAULT_DEVICE_NAME "/dev/ttyS1" #define HASH_PARAM "_HASH_CHECK" #if defined(CONFIG_NET) || defined(__PX4_POSIX) # define MAVLINK_UDP # define DEFAULT_REMOTE_PORT_UDP 14550 ///< GCS port per MAVLink spec #endif // CONFIG_NET || __PX4_POSIX enum class Protocol { SERIAL = 0, #if defined(MAVLINK_UDP) UDP, #endif // MAVLINK_UDP }; using namespace time_literals; class Mavlink final : public ModuleParams { public: /** * Constructor */ Mavlink(); /** * Destructor, also kills the mavlinks task. */ ~Mavlink(); /** * Start the mavlink task. * * @return OK on success. */ static int start(int argc, char *argv[]); bool running() const { return _task_running.load(); } bool should_exit() const { return _task_should_exit.load(); } void request_stop() { _task_should_exit.store(true); _receiver.request_stop(); } /** * Display the mavlink status. */ void display_status(); /** * Display the status of all enabled streams. */ void display_status_streams(); static int stop_command(int argc, char *argv[]); static int stream_command(int argc, char *argv[]); static int instance_count(); static Mavlink *new_instance(); static Mavlink *get_instance_for_device(const char *device_name); mavlink_message_t *get_buffer() { return &_mavlink_buffer; } mavlink_status_t *get_status() { return &_mavlink_status; } /** * Set the MAVLink version * * Currently supporting v1 and v2 * * @param version MAVLink version */ void set_proto_version(unsigned version); static int destroy_all_instances(); static int get_status_all_instances(bool show_streams_status); static bool serial_instance_exists(const char *device_name, Mavlink *self); static bool component_was_seen(int system_id, int component_id, Mavlink *self = nullptr); static void forward_message(const mavlink_message_t *msg, Mavlink *self); bool check_events() const { return _should_check_events.load(); } void check_events_enable() { _should_check_events.store(true); } void check_events_disable() { _should_check_events.store(false); } int get_uart_fd() const { return _uart_fd; } /** * Get the MAVLink system id. * * @return The system ID of this vehicle */ int get_system_id() const { return mavlink_system.sysid; } /** * Get the MAVLink component id. * * @return The component ID of this vehicle */ int get_component_id() const { return mavlink_system.compid; } const char *_device_name{DEFAULT_DEVICE_NAME}; enum MAVLINK_MODE { MAVLINK_MODE_NORMAL = 0, MAVLINK_MODE_CUSTOM, MAVLINK_MODE_ONBOARD, MAVLINK_MODE_OSD, MAVLINK_MODE_MAGIC, MAVLINK_MODE_CONFIG, MAVLINK_MODE_IRIDIUM, MAVLINK_MODE_MINIMAL, MAVLINK_MODE_EXTVISION, MAVLINK_MODE_EXTVISIONMIN, MAVLINK_MODE_GIMBAL, MAVLINK_MODE_ONBOARD_LOW_BANDWIDTH, MAVLINK_MODE_COUNT }; enum BROADCAST_MODE { BROADCAST_MODE_OFF = 0, BROADCAST_MODE_ON, BROADCAST_MODE_MULTICAST }; enum FLOW_CONTROL_MODE { FLOW_CONTROL_OFF = 0, FLOW_CONTROL_AUTO, FLOW_CONTROL_ON }; static const char *mavlink_mode_str(enum MAVLINK_MODE mode) { switch (mode) { case MAVLINK_MODE_NORMAL: return "Normal"; case MAVLINK_MODE_CUSTOM: return "Custom"; case MAVLINK_MODE_ONBOARD: return "Onboard"; case MAVLINK_MODE_OSD: return "OSD"; case MAVLINK_MODE_MAGIC: return "Magic"; case MAVLINK_MODE_CONFIG: return "Config"; case MAVLINK_MODE_IRIDIUM: return "Iridium"; case MAVLINK_MODE_MINIMAL: return "Minimal"; case MAVLINK_MODE_EXTVISION: return "ExtVision"; case MAVLINK_MODE_EXTVISIONMIN: return "ExtVisionMin"; case MAVLINK_MODE_GIMBAL: return "Gimbal"; case MAVLINK_MODE_ONBOARD_LOW_BANDWIDTH: return "OnboardLowBandwidth"; default: return "Unknown"; } } enum MAVLINK_MODE get_mode() { return _mode; } bool get_hil_enabled() { return _hil_enabled; } bool get_use_hil_gps() { return _param_mav_usehilgps.get(); } bool get_forward_externalsp() { return _param_mav_fwdextsp.get(); } bool get_flow_control_enabled() { return _flow_control_mode; } bool get_forwarding_on() { return _forwarding_on; } bool is_connected() { return _tstatus.heartbeat_type_gcs; } #if defined(MAVLINK_UDP) static Mavlink *get_instance_for_network_port(unsigned long port); bool broadcast_enabled() { return _mav_broadcast == BROADCAST_MODE_ON; } bool multicast_enabled() { return _mav_broadcast == BROADCAST_MODE_MULTICAST; } #endif // MAVLINK_UDP /** * Set the boot complete flag on all instances * * Setting the flag unblocks parameter transmissions, which are gated * beforehand to ensure that the system is fully initialized. */ static void set_boot_complete(); /** * Get the free space in the transmit buffer * * @return free space in the UART TX buffer */ unsigned get_free_tx_buf(); static int start_helper(int argc, char *argv[]); /** * Enable / disable Hardware in the Loop simulation mode. * * @param hil_enabled The new HIL enable/disable state. * @return OK if the HIL state changed, ERROR if the * requested change could not be made or was * redundant. */ int set_hil_enabled(bool hil_enabled); /** * Set communication protocol for this mavlink instance */ void set_protocol(Protocol p) { _protocol = p; } /** * This is the beginning of a MAVLINK_START_UART_SEND/MAVLINK_END_UART_SEND transaction */ void send_start(int length); /** * Buffer bytes to send out on the link. */ void send_bytes(const uint8_t *buf, unsigned packet_len); /** * Flush the transmit buffer and send one MAVLink packet */ void send_finish(); /** * Resend message as is, don't change sequence number and CRC. */ void resend_message(mavlink_message_t *msg) { _mavlink_resend_uart(_channel, msg); } void handle_message(const mavlink_message_t *msg); int get_instance_id() const { return _instance_id; } /** * Enable / disable hardware flow control. * * @param enabled True if hardware flow control should be enabled */ int setup_flow_control(enum FLOW_CONTROL_MODE enabled); mavlink_channel_t get_channel() const { return _channel; } void configure_stream_threadsafe(const char *stream_name, float rate = -1.0f); orb_advert_t *get_mavlink_log_pub() { return &_mavlink_log_pub; } /** * Send a status text with loglevel INFO * * @param string the message to send (will be capped by mavlink max string length) */ void send_statustext_info(const char *string); /** * Send a status text with loglevel CRITICAL * * @param string the message to send (will be capped by mavlink max string length) */ void send_statustext_critical(const char *string); /** * Send a status text with loglevel EMERGENCY * * @param string the message to send (will be capped by mavlink max string length) */ void send_statustext_emergency(const char *string); /** * Send a status text with loglevel, the difference from mavlink_log_xxx() is that message sent * only on this mavlink connection. Useful for reporting communication specific, not system-wide info * only to client interested in it. Message will be not sent immediately but queued in buffer as * for mavlink_log_xxx(). * * @param string the message to send (will be capped by mavlink max string length) * @param severity the log level */ void send_statustext(unsigned char severity, const char *string); /** * Send the capabilities of this autopilot in terms of the MAVLink spec */ bool send_autopilot_capabilities(); /** * Send the protocol version of MAVLink */ void send_protocol_version(); List &get_streams() { return _streams; } float get_rate_mult() const { return _rate_mult; } float get_baudrate() { return _baudrate; } /* Functions for waiting to start transmission until message received. */ void set_has_received_messages(bool received_messages) { _received_messages = received_messages; } bool get_has_received_messages() { return _received_messages; } void set_wait_to_transmit(bool wait) { _wait_to_transmit = wait; } bool get_wait_to_transmit() { return _wait_to_transmit; } bool should_transmit() { return (_transmitting_enabled && (!_wait_to_transmit || (_wait_to_transmit && _received_messages))); } bool message_buffer_write(const void *ptr, int size); void lockMessageBufferMutex(void) { pthread_mutex_lock(&_message_buffer_mutex); } void unlockMessageBufferMutex(void) { pthread_mutex_unlock(&_message_buffer_mutex); } /** * Count transmitted bytes */ void count_txbytes(unsigned n) { _bytes_tx += n; }; /** * Count bytes not transmitted because of errors */ void count_txerrbytes(unsigned n) { _bytes_txerr += n; }; /** * Count received bytes */ void count_rxbytes(unsigned n) { _bytes_rx += n; }; /** * Get the receive status of this MAVLink link */ telemetry_status_s &telemetry_status() { return _tstatus; } void telemetry_status_updated() { _tstatus_updated = true; } void set_telemetry_status_type(uint8_t type) { _tstatus.type = type; } void update_radio_status(const radio_status_s &radio_status); unsigned get_system_type() { return _param_mav_type.get(); } Protocol get_protocol() const { return _protocol; } int get_socket_fd() { return _socket_fd; }; #if defined(MAVLINK_UDP) unsigned short get_network_port() { return _network_port; } unsigned short get_remote_port() { return _remote_port; } const in_addr query_netmask_addr(const int socket_fd, const ifreq &ifreq); const in_addr compute_broadcast_addr(const in_addr &host_addr, const in_addr &netmask_addr); struct sockaddr_in &get_client_source_address() { return _src_addr; } void set_client_source_initialized() { _src_addr_initialized = true; } bool get_client_source_initialized() { return _src_addr_initialized; } #endif uint64_t get_start_time() { return _mavlink_start_time; } static bool boot_complete() { return _boot_complete; } bool is_usb_uart() { return _is_usb_uart; } int get_data_rate() { return _datarate; } void set_data_rate(int rate) { if (rate > 0) { _datarate = rate; } } unsigned get_main_loop_delay() const { return _main_loop_delay; } /** get the Mavlink shell. Create a new one if there isn't one. It is *always* created via MavlinkReceiver thread. * Returns nullptr if shell cannot be created */ MavlinkShell *get_shell(); /** close the Mavlink shell if it is open */ void close_shell(); /** get ulog streaming if active, nullptr otherwise */ MavlinkULog *get_ulog_streaming() { return _mavlink_ulog; } void try_start_ulog_streaming(uint8_t target_system, uint8_t target_component) { if (_mavlink_ulog) { return; } _mavlink_ulog = MavlinkULog::try_start(_datarate, 0.7f, target_system, target_component); } const events::SendProtocol &get_events_protocol() const { return _events; }; bool ftp_enabled() const { return _ftp_on; } bool hash_check_enabled() const { return _param_mav_hash_chk_en.get(); } bool forward_heartbeats_enabled() const { return _param_mav_hb_forw_en.get(); } bool odometry_loopback_enabled() const { return _param_mav_odom_lp.get(); } bool failure_injection_enabled() const { return _param_sys_failure_injection_enabled.get(); } struct ping_statistics_s { uint64_t last_ping_time; uint32_t last_ping_seq; uint32_t dropped_packets; float last_rtt; float mean_rtt; float max_rtt; float min_rtt; }; /** * Get the ping statistics of this MAVLink link */ struct ping_statistics_s &get_ping_statistics() { return _ping_stats; } static hrt_abstime &get_first_start_time() { return _first_start_time; } bool radio_status_critical() const { return _radio_status_critical; } private: MavlinkReceiver _receiver; int _instance_id{-1}; int _task_id{-1}; px4::atomic_bool _task_should_exit{false}; px4::atomic_bool _task_running{false}; bool _transmitting_enabled{true}; bool _transmitting_enabled_commanded{false}; bool _first_heartbeat_sent{false}; orb_advert_t _mavlink_log_pub{nullptr}; uORB::Publication _vehicle_command_ack_pub{ORB_ID(vehicle_command_ack)}; uORB::PublicationMulti _telemetry_status_pub{ORB_ID(telemetry_status)}; uORB::Subscription _event_sub{ORB_ID(event)}; uORB::SubscriptionInterval _parameter_update_sub{ORB_ID(parameter_update), 1_s}; uORB::Subscription _vehicle_command_sub{ORB_ID(vehicle_command)}; uORB::Subscription _vehicle_command_ack_sub{ORB_ID(vehicle_command_ack)}; uORB::Subscription _vehicle_status_sub{ORB_ID(vehicle_status)}; static bool _boot_complete; static constexpr int MAVLINK_MIN_INTERVAL{1500}; static constexpr int MAVLINK_MAX_INTERVAL{10000}; static constexpr float MAVLINK_MIN_MULTIPLIER{0.0005f}; mavlink_message_t _mavlink_buffer {}; mavlink_status_t _mavlink_status {}; /* states */ bool _hil_enabled{false}; /**< Hardware In the Loop mode */ bool _is_usb_uart{false}; /**< Port is USB */ bool _wait_to_transmit{false}; /**< Wait to transmit until received messages. */ bool _received_messages{false}; /**< Whether we've received valid mavlink messages. */ px4::atomic_bool _should_check_events{false}; /**< Events subscription: only one MAVLink instance should check */ unsigned _main_loop_delay{1000}; /**< mainloop delay, depends on data rate */ List _streams; MavlinkShell *_mavlink_shell{nullptr}; MavlinkULog *_mavlink_ulog{nullptr}; static events::EventBuffer *_event_buffer; events::SendProtocol _events{*_event_buffer, *this}; MAVLINK_MODE _mode{MAVLINK_MODE_NORMAL}; mavlink_channel_t _channel{MAVLINK_COMM_0}; bool _forwarding_on{false}; bool _ftp_on{false}; bool _use_software_mav_throttling{false}; int _uart_fd{-1}; int _baudrate{57600}; int _datarate{1000}; ///< data rate for normal streams (attitude, position, etc.) float _rate_mult{1.0f}; bool _radio_status_available{false}; bool _radio_status_critical{false}; float _radio_status_mult{1.0f}; /** * If the queue index is not at 0, the queue sending * logic will send parameters from the current index * to len - 1, the end of the param list. */ unsigned int _mavlink_param_queue_index{0}; bool _mavlink_link_termination_allowed{false}; char *_subscribe_to_stream{nullptr}; float _subscribe_to_stream_rate{0.0f}; ///< rate of stream to subscribe to (0=disable, -1=unlimited, -2=default) bool _udp_initialised{false}; FLOW_CONTROL_MODE _flow_control_mode{Mavlink::FLOW_CONTROL_OFF}; uint64_t _last_write_success_time{0}; uint64_t _last_write_try_time{0}; uint64_t _mavlink_start_time{0}; int32_t _protocol_version_switch{-1}; int32_t _protocol_version{0}; unsigned _bytes_tx{0}; unsigned _bytes_txerr{0}; unsigned _bytes_rx{0}; hrt_abstime _bytes_timestamp{0}; #if defined(MAVLINK_UDP) BROADCAST_MODE _mav_broadcast {BROADCAST_MODE_OFF}; sockaddr_in _myaddr {}; sockaddr_in _src_addr {}; sockaddr_in _bcast_addr {}; bool _src_addr_initialized{false}; bool _broadcast_address_found{false}; bool _broadcast_address_not_found_warned{false}; bool _broadcast_failed_warned{false}; unsigned short _network_port{14556}; unsigned short _remote_port{DEFAULT_REMOTE_PORT_UDP}; #endif // MAVLINK_UDP uint8_t _buf[MAVLINK_MAX_PACKET_LEN] {}; unsigned _buf_fill{0}; bool _tx_buffer_low{false}; const char *_interface_name{nullptr}; int _socket_fd{-1}; Protocol _protocol{Protocol::SERIAL}; radio_status_s _rstatus {}; telemetry_status_s _tstatus {}; bool _tstatus_updated{false}; ping_statistics_s _ping_stats {}; struct mavlink_message_buffer { int write_ptr; int read_ptr; int size; char *data; }; mavlink_message_buffer _message_buffer {}; pthread_mutex_t _message_buffer_mutex {}; pthread_mutex_t _send_mutex {}; pthread_mutex_t _radio_status_mutex {}; DEFINE_PARAMETERS( (ParamInt) _param_mav_sys_id, (ParamInt) _param_mav_comp_id, (ParamInt) _param_mav_proto_ver, (ParamInt) _param_sik_radio_id, (ParamInt) _param_mav_type, (ParamBool) _param_mav_usehilgps, (ParamBool) _param_mav_fwdextsp, (ParamBool) _param_mav_hash_chk_en, (ParamBool) _param_mav_hb_forw_en, (ParamBool) _param_mav_odom_lp, (ParamInt) _param_mav_radio_timeout, (ParamInt) _param_sys_hitl, (ParamBool) _param_sys_failure_injection_enabled ) perf_counter_t _loop_perf{perf_alloc(PC_ELAPSED, MODULE_NAME": tx run elapsed")}; /**< loop performance counter */ perf_counter_t _loop_interval_perf{perf_alloc(PC_INTERVAL, MODULE_NAME": tx run interval")}; /**< loop interval performance counter */ perf_counter_t _send_byte_error_perf{perf_alloc(PC_COUNT, MODULE_NAME": send_bytes error")}; /**< send bytes error count */ void mavlink_update_parameters(); int mavlink_open_uart(const int baudrate = DEFAULT_BAUD_RATE, const char *uart_name = DEFAULT_DEVICE_NAME, const FLOW_CONTROL_MODE flow_control = FLOW_CONTROL_AUTO); static constexpr unsigned RADIO_BUFFER_CRITICAL_LOW_PERCENTAGE = 25; static constexpr unsigned RADIO_BUFFER_LOW_PERCENTAGE = 35; static constexpr unsigned RADIO_BUFFER_HALF_PERCENTAGE = 50; static hrt_abstime _first_start_time; /** * Configure a single stream. * @param stream_name * @param rate streaming rate in Hz, -1 = unlimited rate * @return 0 on success, <0 on error */ int configure_stream(const char *stream_name, const float rate = -1.0f); /** * Configure default streams according to _mode for either all streams or only a single * stream. * @param configure_single_stream: if nullptr, configure all streams, else only a single stream * @return 0 on success, <0 on error */ int configure_streams_to_default(const char *configure_single_stream = nullptr); int message_buffer_init(int size); void message_buffer_destroy(); int message_buffer_count(); int message_buffer_is_empty() const { return (_message_buffer.read_ptr == _message_buffer.write_ptr); } int message_buffer_get_ptr(void **ptr, bool *is_part); void message_buffer_mark_read(int n) { _message_buffer.read_ptr = (_message_buffer.read_ptr + n) % _message_buffer.size; } void pass_message(const mavlink_message_t *msg); void publish_telemetry_status(); void check_requested_subscriptions(); /** * Reconfigure a SiK radio if requested by MAV_SIK_RADIO_ID * * This convenience function allows to re-configure a connected * SiK radio without removing it from the main system harness. */ void configure_sik_radio(); /** * Update rate mult so total bitrate will be equal to _datarate. */ void update_rate_mult(); #if defined(MAVLINK_UDP) void find_broadcast_address(); void init_udp(); #endif // MAVLINK_UDP void set_channel(); bool set_instance_id(); /** * Main mavlink task. */ int task_main(int argc, char *argv[]); // Disallow copy construction and move assignment. Mavlink(const Mavlink &) = delete; Mavlink operator=(const Mavlink &) = delete; };