Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/master/libraries/AP_DroneCAN/AP_DroneCAN.cpp
Views: 1798
/*1* This file is free software: you can redistribute it and/or modify it2* under the terms of the GNU General Public License as published by the3* Free Software Foundation, either version 3 of the License, or4* (at your option) any later version.5*6* This file is distributed in the hope that it will be useful, but7* WITHOUT ANY WARRANTY; without even the implied warranty of8* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.9* See the GNU General Public License for more details.10*11* You should have received a copy of the GNU General Public License along12* with this program. If not, see <http://www.gnu.org/licenses/>.13*14* Author: Eugene Shamaev, Siddharth Bharat Purohit15*/1617#include <AP_Common/AP_Common.h>18#include <AP_HAL/AP_HAL.h>1920#if HAL_ENABLE_DRONECAN_DRIVERS21#include "AP_DroneCAN.h"22#include <GCS_MAVLink/GCS.h>2324#include <AP_BoardConfig/AP_BoardConfig.h>25#include <AP_CANManager/AP_CANManager.h>2627#include <AP_Arming/AP_Arming.h>28#include <AP_GPS/AP_GPS_DroneCAN.h>29#include <AP_Compass/AP_Compass_DroneCAN.h>30#include <AP_Baro/AP_Baro_DroneCAN.h>31#include <AP_Vehicle/AP_Vehicle.h>32#include <AP_BattMonitor/AP_BattMonitor_DroneCAN.h>33#include <AP_Airspeed/AP_Airspeed_DroneCAN.h>34#include <AP_OpticalFlow/AP_OpticalFlow_HereFlow.h>35#include <AP_RangeFinder/AP_RangeFinder_DroneCAN.h>36#include <AP_RCProtocol/AP_RCProtocol_DroneCAN.h>37#include <AP_EFI/AP_EFI_DroneCAN.h>38#include <AP_GPS/AP_GPS_DroneCAN.h>39#include <AP_GPS/AP_GPS.h>40#include <AP_BattMonitor/AP_BattMonitor_DroneCAN.h>41#include <AP_Compass/AP_Compass_DroneCAN.h>42#include <AP_Airspeed/AP_Airspeed_DroneCAN.h>43#include <AP_Proximity/AP_Proximity_DroneCAN.h>44#include <SRV_Channel/SRV_Channel.h>45#include <AP_ADSB/AP_ADSB.h>46#include "AP_DroneCAN_DNA_Server.h"47#include <AP_Logger/AP_Logger.h>48#include <AP_Notify/AP_Notify.h>49#include <AP_OpenDroneID/AP_OpenDroneID.h>50#include <AP_Mount/AP_Mount_Xacti.h>51#include <string.h>52#include <AP_Servo_Telem/AP_Servo_Telem.h>5354#if AP_DRONECAN_SERIAL_ENABLED55#include "AP_DroneCAN_serial.h"56#endif5758#if AP_RELAY_DRONECAN_ENABLED59#include <AP_Relay/AP_Relay.h>60#endif6162#include <AP_TemperatureSensor/AP_TemperatureSensor_DroneCAN.h>6364#include <AP_RPM/RPM_DroneCAN.h>6566extern const AP_HAL::HAL& hal;6768// setup default pool size69#ifndef DRONECAN_NODE_POOL_SIZE70#if HAL_CANFD_SUPPORTED71#define DRONECAN_NODE_POOL_SIZE 1638472#else73#define DRONECAN_NODE_POOL_SIZE 819274#endif75#endif7677#if HAL_CANFD_SUPPORTED78#define DRONECAN_STACK_SIZE 819279#else80#define DRONECAN_STACK_SIZE 409681#endif8283#ifndef AP_DRONECAN_DEFAULT_NODE84#define AP_DRONECAN_DEFAULT_NODE 1085#endif8687#define AP_DRONECAN_GETSET_TIMEOUT_MS 100 // timeout waiting for response from node after 0.1 sec8889#define debug_dronecan(level_debug, fmt, args...) do { AP::can().log_text(level_debug, "DroneCAN", fmt, ##args); } while (0)9091// Translation of all messages from DroneCAN structures into AP structures is done92// in AP_DroneCAN and not in corresponding drivers.93// The overhead of including definitions of DSDL is very high and it is best to94// concentrate in one place.9596// table of user settable CAN bus parameters97const AP_Param::GroupInfo AP_DroneCAN::var_info[] = {98// @Param: NODE99// @DisplayName: Own node ID100// @Description: DroneCAN node ID used by the driver itself on this network101// @Range: 1 125102// @User: Advanced103AP_GROUPINFO("NODE", 1, AP_DroneCAN, _dronecan_node, AP_DRONECAN_DEFAULT_NODE),104105// @Param: SRV_BM106// @DisplayName: Output channels to be transmitted as servo over DroneCAN107// @Description: Bitmask with one set for channel to be transmitted as a servo command over DroneCAN108// @Bitmask: 0: Servo 1, 1: Servo 2, 2: Servo 3, 3: Servo 4, 4: Servo 5, 5: Servo 6, 6: Servo 7, 7: Servo 8, 8: Servo 9, 9: Servo 10, 10: Servo 11, 11: Servo 12, 12: Servo 13, 13: Servo 14, 14: Servo 15, 15: Servo 16, 16: Servo 17, 17: Servo 18, 18: Servo 19, 19: Servo 20, 20: Servo 21, 21: Servo 22, 22: Servo 23, 23: Servo 24, 24: Servo 25, 25: Servo 26, 26: Servo 27, 27: Servo 28, 28: Servo 29, 29: Servo 30, 30: Servo 31, 31: Servo 32109110// @User: Advanced111AP_GROUPINFO("SRV_BM", 2, AP_DroneCAN, _servo_bm, 0),112113// @Param: ESC_BM114// @DisplayName: Output channels to be transmitted as ESC over DroneCAN115// @Description: Bitmask with one set for channel to be transmitted as a ESC command over DroneCAN116// @Bitmask: 0: ESC 1, 1: ESC 2, 2: ESC 3, 3: ESC 4, 4: ESC 5, 5: ESC 6, 6: ESC 7, 7: ESC 8, 8: ESC 9, 9: ESC 10, 10: ESC 11, 11: ESC 12, 12: ESC 13, 13: ESC 14, 14: ESC 15, 15: ESC 16, 16: ESC 17, 17: ESC 18, 18: ESC 19, 19: ESC 20, 20: ESC 21, 21: ESC 22, 22: ESC 23, 23: ESC 24, 24: ESC 25, 25: ESC 26, 26: ESC 27, 27: ESC 28, 28: ESC 29, 29: ESC 30, 30: ESC 31, 31: ESC 32117// @User: Advanced118AP_GROUPINFO("ESC_BM", 3, AP_DroneCAN, _esc_bm, 0),119120// @Param: SRV_RT121// @DisplayName: Servo output rate122// @Description: Maximum transmit rate for servo outputs123// @Range: 1 200124// @Units: Hz125// @User: Advanced126AP_GROUPINFO("SRV_RT", 4, AP_DroneCAN, _servo_rate_hz, 50),127128// @Param: OPTION129// @DisplayName: DroneCAN options130// @Description: Option flags131// @Bitmask: 0:ClearDNADatabase,1:IgnoreDNANodeConflicts,2:EnableCanfd,3:IgnoreDNANodeUnhealthy,4:SendServoAsPWM,5:SendGNSS,6:UseHimarkServo,7:HobbyWingESC,8:EnableStats,9:EnableFlexDebug132// @User: Advanced133AP_GROUPINFO("OPTION", 5, AP_DroneCAN, _options, 0),134135// @Param: NTF_RT136// @DisplayName: Notify State rate137// @Description: Maximum transmit rate for Notify State Message138// @Range: 1 200139// @Units: Hz140// @User: Advanced141AP_GROUPINFO("NTF_RT", 6, AP_DroneCAN, _notify_state_hz, 20),142143// @Param: ESC_OF144// @DisplayName: ESC Output channels offset145// @Description: Offset for ESC numbering in DroneCAN ESC RawCommand messages. This allows for more efficient packing of ESC command messages. If your ESCs are on servo functions 5 to 8 and you set this parameter to 4 then the ESC RawCommand will be sent with the first 4 slots filled. This can be used for more efficient usage of CAN bandwidth146// @Range: 0 18147// @User: Advanced148AP_GROUPINFO("ESC_OF", 7, AP_DroneCAN, _esc_offset, 0),149150// @Param: POOL151// @DisplayName: CAN pool size152// @Description: Amount of memory in bytes to allocate for the DroneCAN memory pool. More memory is needed for higher CAN bus loads153// @Range: 1024 16384154// @User: Advanced155AP_GROUPINFO("POOL", 8, AP_DroneCAN, _pool_size, DRONECAN_NODE_POOL_SIZE),156157// @Param: ESC_RV158// @DisplayName: Bitmask for output channels for reversible ESCs over DroneCAN.159// @Description: Bitmask with one set for each output channel that uses a reversible ESC over DroneCAN. Reversible ESCs use both positive and negative values in RawCommands, with positive commanding the forward direction and negative commanding the reverse direction.160// @Bitmask: 0: ESC 1, 1: ESC 2, 2: ESC 3, 3: ESC 4, 4: ESC 5, 5: ESC 6, 6: ESC 7, 7: ESC 8, 8: ESC 9, 9: ESC 10, 10: ESC 11, 11: ESC 12, 12: ESC 13, 13: ESC 14, 14: ESC 15, 15: ESC 16, 16: ESC 17, 17: ESC 18, 18: ESC 19, 19: ESC 20, 20: ESC 21, 21: ESC 22, 22: ESC 23, 23: ESC 24, 24: ESC 25, 25: ESC 26, 26: ESC 27, 27: ESC 28, 28: ESC 29, 29: ESC 30, 30: ESC 31, 31: ESC 32161// @User: Advanced162AP_GROUPINFO("ESC_RV", 9, AP_DroneCAN, _esc_rv, 0),163164#if AP_RELAY_DRONECAN_ENABLED165// @Param: RLY_RT166// @DisplayName: DroneCAN relay output rate167// @Description: Maximum transmit rate for relay outputs, note that this rate is per message each message does 1 relay, so if with more relays will take longer to update at the same rate, a extra message will be sent when a relay changes state168// @Range: 0 200169// @Units: Hz170// @User: Advanced171AP_GROUPINFO("RLY_RT", 23, AP_DroneCAN, _relay.rate_hz, 0),172#endif173174#if AP_DRONECAN_SERIAL_ENABLED175/*176due to the parameter tree depth limitation we can't use a sub-table for the serial parameters177*/178179// @Param: SER_EN180// @DisplayName: DroneCAN Serial enable181// @Description: Enable DroneCAN virtual serial ports182// @Values: 0:Disabled, 1:Enabled183// @RebootRequired: True184// @User: Advanced185AP_GROUPINFO_FLAGS("SER_EN", 10, AP_DroneCAN, serial.enable, 0, AP_PARAM_FLAG_ENABLE),186187// @Param: S1_NOD188// @DisplayName: Serial CAN remote node number189// @Description: CAN remote node number for serial port190// @Range: 0 127191// @RebootRequired: True192// @User: Advanced193AP_GROUPINFO("S1_NOD", 11, AP_DroneCAN, serial.ports[0].node, 0),194195// @Param: S1_IDX196// @DisplayName: DroneCAN Serial1 index197// @Description: Serial port number on remote CAN node198// @Range: 0 100199// @Values: -1:Disabled,0:Serial0,1:Serial1,2:Serial2,3:Serial3,4:Serial4,5:Serial5,6:Serial6200// @RebootRequired: True201// @User: Advanced202AP_GROUPINFO("S1_IDX", 12, AP_DroneCAN, serial.ports[0].idx, -1),203204// @Param: S1_BD205// @DisplayName: DroneCAN Serial default baud rate206// @Description: Serial baud rate on remote CAN node207// @CopyFieldsFrom: SERIAL1_BAUD208// @RebootRequired: True209// @User: Advanced210AP_GROUPINFO("S1_BD", 13, AP_DroneCAN, serial.ports[0].state.baud, 57600),211212// @Param: S1_PRO213// @DisplayName: Serial protocol of DroneCAN serial port214// @Description: Serial protocol of DroneCAN serial port215// @CopyFieldsFrom: SERIAL1_PROTOCOL216// @RebootRequired: True217// @User: Advanced218AP_GROUPINFO("S1_PRO", 14, AP_DroneCAN, serial.ports[0].state.protocol, -1),219220#if AP_DRONECAN_SERIAL_NUM_PORTS > 1221// @Param: S2_NOD222// @DisplayName: Serial CAN remote node number223// @Description: CAN remote node number for serial port224// @CopyFieldsFrom: CAN_D1_UC_S1_NOD225AP_GROUPINFO("S2_NOD", 15, AP_DroneCAN, serial.ports[1].node, 0),226227// @Param: S2_IDX228// @DisplayName: Serial port number on remote CAN node229// @Description: Serial port number on remote CAN node230// @CopyFieldsFrom: CAN_D1_UC_S1_IDX231AP_GROUPINFO("S2_IDX", 16, AP_DroneCAN, serial.ports[1].idx, -1),232233// @Param: S2_BD234// @DisplayName: DroneCAN Serial default baud rate235// @Description: Serial baud rate on remote CAN node236// @CopyFieldsFrom: CAN_D1_UC_S1_BD237AP_GROUPINFO("S2_BD", 17, AP_DroneCAN, serial.ports[1].state.baud, 57600),238239// @Param: S2_PRO240// @DisplayName: Serial protocol of DroneCAN serial port241// @Description: Serial protocol of DroneCAN serial port242// @CopyFieldsFrom: CAN_D1_UC_S1_PRO243AP_GROUPINFO("S2_PRO", 18, AP_DroneCAN, serial.ports[1].state.protocol, -1),244#endif245246#if AP_DRONECAN_SERIAL_NUM_PORTS > 2247// @Param: S3_NOD248// @DisplayName: Serial CAN remote node number249// @Description: CAN node number for serial port250// @CopyFieldsFrom: CAN_D1_UC_S1_NOD251AP_GROUPINFO("S3_NOD", 19, AP_DroneCAN, serial.ports[2].node, 0),252253// @Param: S3_IDX254// @DisplayName: Serial port number on remote CAN node255// @Description: Serial port number on remote CAN node256// @CopyFieldsFrom: CAN_D1_UC_S1_IDX257AP_GROUPINFO("S3_IDX", 20, AP_DroneCAN, serial.ports[2].idx, 0),258259// @Param: S3_BD260// @DisplayName: Serial baud rate on remote CAN node261// @Description: Serial baud rate on remote CAN node262// @CopyFieldsFrom: CAN_D1_UC_S1_BD263AP_GROUPINFO("S3_BD", 21, AP_DroneCAN, serial.ports[2].state.baud, 57600),264265// @Param: S3_PRO266// @DisplayName: Serial protocol of DroneCAN serial port267// @Description: Serial protocol of DroneCAN serial port268// @CopyFieldsFrom: CAN_D1_UC_S1_PRO269AP_GROUPINFO("S3_PRO", 22, AP_DroneCAN, serial.ports[2].state.protocol, -1),270#endif271#endif // AP_DRONECAN_SERIAL_ENABLED272273// RLY_RT is index 23 but has to be above SER_EN so its not hidden274275AP_GROUPEND276};277278// this is the timeout in milliseconds for periodic message types. We279// set this to 1 to minimise resend of stale msgs280#define CAN_PERIODIC_TX_TIMEOUT_MS 2281282AP_DroneCAN::AP_DroneCAN(const int driver_index) :283_driver_index(driver_index),284canard_iface(driver_index),285_dna_server(*this, canard_iface, driver_index)286{287AP_Param::setup_object_defaults(this, var_info);288289for (uint8_t i = 0; i < DRONECAN_SRV_NUMBER; i++) {290_SRV_conf[i].esc_pending = false;291_SRV_conf[i].servo_pending = false;292}293294debug_dronecan(AP_CANManager::LOG_INFO, "AP_DroneCAN constructed\n\r");295}296297AP_DroneCAN::~AP_DroneCAN()298{299}300301AP_DroneCAN *AP_DroneCAN::get_dronecan(uint8_t driver_index)302{303if (driver_index >= AP::can().get_num_drivers() ||304AP::can().get_driver_type(driver_index) != AP_CAN::Protocol::DroneCAN) {305return nullptr;306}307return static_cast<AP_DroneCAN*>(AP::can().get_driver(driver_index));308}309310bool AP_DroneCAN::add_interface(AP_HAL::CANIface* can_iface)311{312if (!canard_iface.add_interface(can_iface)) {313debug_dronecan(AP_CANManager::LOG_ERROR, "DroneCAN: can't add DroneCAN interface\n\r");314return false;315}316return true;317}318319void AP_DroneCAN::init(uint8_t driver_index, bool enable_filters)320{321if (driver_index != _driver_index) {322debug_dronecan(AP_CANManager::LOG_ERROR, "DroneCAN: init called with wrong driver_index");323return;324}325if (_initialized) {326debug_dronecan(AP_CANManager::LOG_ERROR, "DroneCAN: init called more than once\n\r");327return;328}329uint8_t node = _dronecan_node;330if (node < 1 || node > 125) { // reset to default if invalid331_dronecan_node.set(AP_DRONECAN_DEFAULT_NODE);332node = AP_DRONECAN_DEFAULT_NODE;333}334335node_info_rsp.name.len = hal.util->snprintf((char*)node_info_rsp.name.data, sizeof(node_info_rsp.name.data), "org.ardupilot:%u", driver_index);336337node_info_rsp.software_version.major = AP_DRONECAN_SW_VERS_MAJOR;338node_info_rsp.software_version.minor = AP_DRONECAN_SW_VERS_MINOR;339node_info_rsp.hardware_version.major = AP_DRONECAN_HW_VERS_MAJOR;340node_info_rsp.hardware_version.minor = AP_DRONECAN_HW_VERS_MINOR;341342#if HAL_CANFD_SUPPORTED343if (option_is_set(Options::CANFD_ENABLED)) {344canard_iface.set_canfd(true);345}346#endif347348uint8_t uid_len = sizeof(uavcan_protocol_HardwareVersion::unique_id);349uint8_t unique_id[sizeof(uavcan_protocol_HardwareVersion::unique_id)];350351mem_pool = NEW_NOTHROW uint32_t[_pool_size/sizeof(uint32_t)];352if (mem_pool == nullptr) {353debug_dronecan(AP_CANManager::LOG_ERROR, "DroneCAN: Failed to allocate memory pool\n\r");354return;355}356canard_iface.init(mem_pool, (_pool_size/sizeof(uint32_t))*sizeof(uint32_t), node);357358if (!hal.util->get_system_id_unformatted(unique_id, uid_len)) {359return;360}361unique_id[uid_len - 1] += node;362memcpy(node_info_rsp.hardware_version.unique_id, unique_id, uid_len);363364//Start Servers365if (!_dna_server.init(unique_id, uid_len, node)) {366debug_dronecan(AP_CANManager::LOG_ERROR, "DroneCAN: Failed to start DNA Server\n\r");367return;368}369370// Roundup all subscribers from supported drivers371bool subscribed = true;372#if AP_GPS_DRONECAN_ENABLED373subscribed = subscribed && AP_GPS_DroneCAN::subscribe_msgs(this);374#endif375#if AP_COMPASS_DRONECAN_ENABLED376subscribed = subscribed && AP_Compass_DroneCAN::subscribe_msgs(this);377#endif378#if AP_BARO_DRONECAN_ENABLED379subscribed = subscribed && AP_Baro_DroneCAN::subscribe_msgs(this);380#endif381subscribed = subscribed && AP_BattMonitor_DroneCAN::subscribe_msgs(this);382#if AP_AIRSPEED_DRONECAN_ENABLED383subscribed = subscribed && AP_Airspeed_DroneCAN::subscribe_msgs(this);384#endif385#if AP_OPTICALFLOW_HEREFLOW_ENABLED386subscribed = subscribed && AP_OpticalFlow_HereFlow::subscribe_msgs(this);387#endif388#if AP_RANGEFINDER_DRONECAN_ENABLED389subscribed = subscribed && AP_RangeFinder_DroneCAN::subscribe_msgs(this);390#endif391#if AP_RCPROTOCOL_DRONECAN_ENABLED392subscribed = subscribed && AP_RCProtocol_DroneCAN::subscribe_msgs(this);393#endif394#if AP_EFI_DRONECAN_ENABLED395subscribed = subscribed && AP_EFI_DroneCAN::subscribe_msgs(this);396#endif397398#if AP_PROXIMITY_DRONECAN_ENABLED399subscribed = subscribed && AP_Proximity_DroneCAN::subscribe_msgs(this);400#endif401#if HAL_MOUNT_XACTI_ENABLED402subscribed = subscribed && AP_Mount_Xacti::subscribe_msgs(this);403#endif404#if AP_TEMPERATURE_SENSOR_DRONECAN_ENABLED405subscribed = subscribed && AP_TemperatureSensor_DroneCAN::subscribe_msgs(this);406#endif407#if AP_RPM_DRONECAN_ENABLED408subscribed = subscribed && AP_RPM_DroneCAN::subscribe_msgs(this);409#endif410411if (!subscribed) {412AP_BoardConfig::allocation_error("DroneCAN callback");413}414415act_out_array.set_timeout_ms(5);416act_out_array.set_priority(CANARD_TRANSFER_PRIORITY_HIGH);417418esc_raw.set_timeout_ms(2);419// esc_raw is one higher than high priority to ensure that it is given higher priority over act_out_array420esc_raw.set_priority(CANARD_TRANSFER_PRIORITY_HIGH - 1);421422#if AP_DRONECAN_HOBBYWING_ESC_SUPPORT423esc_hobbywing_raw.set_timeout_ms(2);424esc_hobbywing_raw.set_priority(CANARD_TRANSFER_PRIORITY_HIGH);425#endif426427#if AP_DRONECAN_HIMARK_SERVO_SUPPORT428himark_out.set_timeout_ms(2);429himark_out.set_priority(CANARD_TRANSFER_PRIORITY_HIGH);430#endif431432rgb_led.set_timeout_ms(20);433rgb_led.set_priority(CANARD_TRANSFER_PRIORITY_LOW);434435buzzer.set_timeout_ms(20);436buzzer.set_priority(CANARD_TRANSFER_PRIORITY_LOW);437438safety_state.set_timeout_ms(20);439safety_state.set_priority(CANARD_TRANSFER_PRIORITY_LOW);440441arming_status.set_timeout_ms(20);442arming_status.set_priority(CANARD_TRANSFER_PRIORITY_LOW);443444#if AP_DRONECAN_SEND_GPS445gnss_fix2.set_timeout_ms(20);446gnss_fix2.set_priority(CANARD_TRANSFER_PRIORITY_LOW);447448gnss_auxiliary.set_timeout_ms(20);449gnss_auxiliary.set_priority(CANARD_TRANSFER_PRIORITY_LOW);450451gnss_heading.set_timeout_ms(20);452gnss_heading.set_priority(CANARD_TRANSFER_PRIORITY_LOW);453454gnss_status.set_timeout_ms(20);455gnss_status.set_priority(CANARD_TRANSFER_PRIORITY_LOW);456#endif457458rtcm_stream.set_timeout_ms(20);459rtcm_stream.set_priority(CANARD_TRANSFER_PRIORITY_LOW);460461notify_state.set_timeout_ms(20);462notify_state.set_priority(CANARD_TRANSFER_PRIORITY_LOW);463464#if HAL_MOUNT_XACTI_ENABLED465xacti_copter_att_status.set_timeout_ms(20);466xacti_copter_att_status.set_priority(CANARD_TRANSFER_PRIORITY_LOW);467xacti_gimbal_control_data.set_timeout_ms(20);468xacti_gimbal_control_data.set_priority(CANARD_TRANSFER_PRIORITY_LOW);469xacti_gnss_status.set_timeout_ms(20);470xacti_gnss_status.set_priority(CANARD_TRANSFER_PRIORITY_LOW);471#endif472473#if AP_RELAY_DRONECAN_ENABLED474relay_hardpoint.set_timeout_ms(20);475relay_hardpoint.set_priority(CANARD_TRANSFER_PRIORITY_LOW);476#endif477478param_save_client.set_timeout_ms(20);479param_save_client.set_priority(CANARD_TRANSFER_PRIORITY_LOW);480481param_get_set_client.set_timeout_ms(20);482param_get_set_client.set_priority(CANARD_TRANSFER_PRIORITY_LOW);483484node_status.set_priority(CANARD_TRANSFER_PRIORITY_LOWEST);485node_status.set_timeout_ms(1000);486487protocol_stats.set_priority(CANARD_TRANSFER_PRIORITY_LOWEST);488protocol_stats.set_timeout_ms(3000);489490can_stats.set_priority(CANARD_TRANSFER_PRIORITY_LOWEST);491can_stats.set_timeout_ms(3000);492493node_info_server.set_timeout_ms(20);494495// setup node status496node_status_msg.health = UAVCAN_PROTOCOL_NODESTATUS_HEALTH_OK;497node_status_msg.mode = UAVCAN_PROTOCOL_NODESTATUS_MODE_OPERATIONAL;498node_status_msg.sub_mode = 0;499500// Spin node for device discovery501for (uint8_t i = 0; i < 5; i++) {502send_node_status();503canard_iface.process(1000);504}505506hal.util->snprintf(_thread_name, sizeof(_thread_name), "dronecan_%u", driver_index);507508if (!hal.scheduler->thread_create(FUNCTOR_BIND_MEMBER(&AP_DroneCAN::loop, void), _thread_name, DRONECAN_STACK_SIZE, AP_HAL::Scheduler::PRIORITY_CAN, 0)) {509debug_dronecan(AP_CANManager::LOG_ERROR, "DroneCAN: couldn't create thread\n\r");510return;511}512513#if AP_DRONECAN_SERIAL_ENABLED514serial.init(this);515#endif516517_initialized = true;518debug_dronecan(AP_CANManager::LOG_INFO, "DroneCAN: init done\n\r");519}520521void AP_DroneCAN::loop(void)522{523while (true) {524if (!_initialized) {525hal.scheduler->delay_microseconds(1000);526continue;527}528529// ensure that the DroneCAN thread cannot completely saturate530// the CPU, preventing low priority threads from running531hal.scheduler->delay_microseconds(100);532533canard_iface.process(1);534535safety_state_send();536notify_state_send();537check_parameter_callback_timeout();538send_parameter_request();539send_parameter_save_request();540send_node_status();541_dna_server.verify_nodes();542543#if AP_DRONECAN_SEND_GPS && AP_GPS_DRONECAN_ENABLED544if (option_is_set(AP_DroneCAN::Options::SEND_GNSS) && !AP_GPS_DroneCAN::instance_exists(this)) {545// send if enabled and this interface/driver is not used by the AP_GPS driver546gnss_send_fix();547gnss_send_yaw();548}549#endif550logging();551#if AP_DRONECAN_HOBBYWING_ESC_SUPPORT552hobbywing_ESC_update();553#endif554if (_SRV_armed_mask != 0) {555// we have active servos556uint32_t now = AP_HAL::micros();557const uint32_t servo_period_us = 1000000UL / unsigned(_servo_rate_hz.get());558if (now - _SRV_last_send_us >= servo_period_us) {559_SRV_last_send_us = now;560#if AP_DRONECAN_HIMARK_SERVO_SUPPORT561if (option_is_set(Options::USE_HIMARK_SERVO)) {562SRV_send_himark();563} else564#endif565{566SRV_send_actuator();567}568for (uint8_t i = 0; i < DRONECAN_SRV_NUMBER; i++) {569_SRV_conf[i].servo_pending = false;570}571}572}573574#if AP_DRONECAN_SERIAL_ENABLED575serial.update();576#endif577578#if AP_RELAY_DRONECAN_ENABLED579relay_hardpoint_send();580#endif581}582}583584#if AP_DRONECAN_HOBBYWING_ESC_SUPPORT585void AP_DroneCAN::hobbywing_ESC_update(void)586{587if (hal.util->get_soft_armed()) {588// don't update ID database while disarmed, as it can cause589// some hobbywing ESCs to stutter590return;591}592uint32_t now = AP_HAL::millis();593if (now - hobbywing.last_GetId_send_ms >= 1000U) {594hobbywing.last_GetId_send_ms = now;595com_hobbywing_esc_GetEscID msg;596msg.payload.len = 1;597msg.payload.data[0] = 0;598esc_hobbywing_GetEscID.broadcast(msg);599}600}601602/*603handle hobbywing GetEscID reply. This gets us the mapping between CAN NodeID and throttle channel604*/605void AP_DroneCAN::handle_hobbywing_GetEscID(const CanardRxTransfer& transfer, const com_hobbywing_esc_GetEscID& msg)606{607if (msg.payload.len == 2 &&608msg.payload.data[0] == transfer.source_node_id) {609// throttle channel is 2nd payload byte610const uint8_t thr_channel = msg.payload.data[1];611if (thr_channel > 0 && thr_channel <= HOBBYWING_MAX_ESC) {612hobbywing.thr_chan[thr_channel-1] = transfer.source_node_id;613}614}615}616617/*618find the ESC index given a CAN node ID619*/620bool AP_DroneCAN::hobbywing_find_esc_index(uint8_t node_id, uint8_t &esc_index) const621{622for (uint8_t i=0; i<HOBBYWING_MAX_ESC; i++) {623if (hobbywing.thr_chan[i] == node_id) {624const uint8_t esc_offset = constrain_int16(_esc_offset.get(), 0, DRONECAN_SRV_NUMBER);625esc_index = i + esc_offset;626return true;627}628}629return false;630}631632/*633handle hobbywing StatusMsg1 reply634*/635void AP_DroneCAN::handle_hobbywing_StatusMsg1(const CanardRxTransfer& transfer, const com_hobbywing_esc_StatusMsg1& msg)636{637uint8_t esc_index;638if (hobbywing_find_esc_index(transfer.source_node_id, esc_index)) {639update_rpm(esc_index, msg.rpm);640}641}642643/*644handle hobbywing StatusMsg2 reply645*/646void AP_DroneCAN::handle_hobbywing_StatusMsg2(const CanardRxTransfer& transfer, const com_hobbywing_esc_StatusMsg2& msg)647{648uint8_t esc_index;649if (hobbywing_find_esc_index(transfer.source_node_id, esc_index)) {650TelemetryData t {651.temperature_cdeg = int16_t(msg.temperature*100),652.voltage = msg.input_voltage*0.1f,653.current = msg.current*0.1f,654};655update_telem_data(esc_index, t,656AP_ESC_Telem_Backend::TelemetryType::CURRENT|657AP_ESC_Telem_Backend::TelemetryType::VOLTAGE|658AP_ESC_Telem_Backend::TelemetryType::TEMPERATURE);659}660661}662#endif // AP_DRONECAN_HOBBYWING_ESC_SUPPORT663664void AP_DroneCAN::send_node_status(void)665{666const uint32_t now = AP_HAL::millis();667if (now - _node_status_last_send_ms < 1000) {668return;669}670_node_status_last_send_ms = now;671node_status_msg.uptime_sec = now / 1000;672node_status.broadcast(node_status_msg);673674if (option_is_set(Options::ENABLE_STATS)) {675// also send protocol and can stats676protocol_stats.broadcast(canard_iface.protocol_stats);677678// get can stats679for (uint8_t i=0; i<canard_iface.num_ifaces; i++) {680if (canard_iface.ifaces[i] == nullptr) {681continue;682}683auto* iface = hal.can[0];684for (uint8_t j=0; j<HAL_NUM_CAN_IFACES; j++) {685if (hal.can[j] == canard_iface.ifaces[i]) {686iface = hal.can[j];687break;688}689}690auto* bus_stats = iface->get_statistics();691if (bus_stats == nullptr) {692continue;693}694dronecan_protocol_CanStats can_stats_msg;695can_stats_msg.interface = i;696can_stats_msg.tx_requests = bus_stats->tx_requests;697can_stats_msg.tx_rejected = bus_stats->tx_rejected;698can_stats_msg.tx_overflow = bus_stats->tx_overflow;699can_stats_msg.tx_success = bus_stats->tx_success;700can_stats_msg.tx_timedout = bus_stats->tx_timedout;701can_stats_msg.tx_abort = bus_stats->tx_abort;702can_stats_msg.rx_received = bus_stats->rx_received;703can_stats_msg.rx_overflow = bus_stats->rx_overflow;704can_stats_msg.rx_errors = bus_stats->rx_errors;705can_stats_msg.busoff_errors = bus_stats->num_busoff_err;706can_stats.broadcast(can_stats_msg);707}708}709}710711void AP_DroneCAN::handle_node_info_request(const CanardRxTransfer& transfer, const uavcan_protocol_GetNodeInfoRequest& req)712{713node_info_rsp.status = node_status_msg;714node_info_rsp.status.uptime_sec = AP_HAL::millis() / 1000;715716node_info_server.respond(transfer, node_info_rsp);717}718719int16_t AP_DroneCAN::scale_esc_output(uint8_t idx){720static const int16_t cmd_max = ((1<<13)-1);721float scaled = hal.rcout->scale_esc_to_unity(_SRV_conf[idx].pulse);722// Prevent invalid values (from misconfigured scaling parameters) from sending non-zero commands723if (!isfinite(scaled)) {724return 0;725}726scaled = constrain_float(scaled, -1, 1);727//Check if this channel has a reversible ESC. If it does, we can send negative commands.728if ((((uint32_t) 1) << idx) & _esc_rv) {729scaled *= cmd_max;730} else {731scaled = cmd_max * (scaled + 1.0) / 2.0;732}733734return static_cast<int16_t>(scaled);735}736737///// SRV output /////738739void AP_DroneCAN::SRV_send_actuator(void)740{741uint8_t starting_servo = 0;742bool repeat_send;743744WITH_SEMAPHORE(SRV_sem);745746do {747repeat_send = false;748uavcan_equipment_actuator_ArrayCommand msg;749750uint8_t i;751// DroneCAN can hold maximum of 15 commands in one frame752for (i = 0; starting_servo < DRONECAN_SRV_NUMBER && i < 15; starting_servo++) {753uavcan_equipment_actuator_Command cmd;754755/*756* Servo output uses a range of 1000-2000 PWM for scaling.757* This converts output PWM from [1000:2000] range to [-1:1] range that758* is passed to servo as unitless type via DroneCAN.759* This approach allows for MIN/TRIM/MAX values to be used fully on760* autopilot side and for servo it should have the setup to provide maximum761* physically possible throws at [-1:1] limits.762*/763764if (_SRV_conf[starting_servo].servo_pending && ((((uint32_t) 1) << starting_servo) & _SRV_armed_mask)) {765cmd.actuator_id = starting_servo + 1;766767if (option_is_set(Options::USE_ACTUATOR_PWM)) {768cmd.command_type = UAVCAN_EQUIPMENT_ACTUATOR_COMMAND_COMMAND_TYPE_PWM;769cmd.command_value = _SRV_conf[starting_servo].pulse;770} else {771cmd.command_type = UAVCAN_EQUIPMENT_ACTUATOR_COMMAND_COMMAND_TYPE_UNITLESS;772cmd.command_value = constrain_float(((float) _SRV_conf[starting_servo].pulse - 1000.0) / 500.0 - 1.0, -1.0, 1.0);773}774775msg.commands.data[i] = cmd;776777i++;778}779}780msg.commands.len = i;781if (i > 0) {782if (act_out_array.broadcast(msg) > 0) {783_srv_send_count++;784} else {785_fail_send_count++;786}787788if (i == 15) {789repeat_send = true;790}791}792} while (repeat_send);793}794795#if AP_DRONECAN_HIMARK_SERVO_SUPPORT796/*797Himark servo output. This uses com.himark.servo.ServoCmd packets798*/799void AP_DroneCAN::SRV_send_himark(void)800{801WITH_SEMAPHORE(SRV_sem);802803// ServoCmd can hold maximum of 17 commands. First find the highest pending servo < 17804int8_t highest_to_send = -1;805for (int8_t i = 16; i >= 0; i--) {806if (_SRV_conf[i].servo_pending && ((1U<<i) & _SRV_armed_mask) != 0) {807highest_to_send = i;808break;809}810}811if (highest_to_send == -1) {812// nothing to send813return;814}815com_himark_servo_ServoCmd msg {};816817for (uint8_t i = 0; i <= highest_to_send; i++) {818if ((1U<<i) & _SRV_armed_mask) {819const uint16_t pulse = constrain_int16(_SRV_conf[i].pulse - 1000, 0, 1000);820msg.cmd.data[i] = pulse;821}822}823msg.cmd.len = highest_to_send+1;824825himark_out.broadcast(msg);826}827#endif // AP_DRONECAN_HIMARK_SERVO_SUPPORT828829void AP_DroneCAN::SRV_send_esc(void)830{831uavcan_equipment_esc_RawCommand esc_msg;832833uint8_t active_esc_num = 0, max_esc_num = 0;834uint8_t k = 0;835836// esc offset allows for efficient packing of higher ESC numbers in RawCommand837const uint8_t esc_offset = constrain_int16(_esc_offset.get(), 0, DRONECAN_SRV_NUMBER);838839// find out how many esc we have enabled and if they are active at all840for (uint8_t i = esc_offset; i < DRONECAN_SRV_NUMBER; i++) {841if ((((uint32_t) 1) << i) & _ESC_armed_mask) {842max_esc_num = i + 1;843if (_SRV_conf[i].esc_pending) {844active_esc_num++;845}846}847}848849// if at least one is active (update) we need to send to all850if (active_esc_num > 0) {851k = 0;852const bool armed = hal.util->get_soft_armed();853for (uint8_t i = esc_offset; i < max_esc_num && k < 20; i++) {854if (armed && ((((uint32_t) 1U) << i) & _ESC_armed_mask)) {855esc_msg.cmd.data[k] = scale_esc_output(i);856} else {857esc_msg.cmd.data[k] = static_cast<unsigned>(0);858}859860k++;861}862esc_msg.cmd.len = k;863864if (esc_raw.broadcast(esc_msg)) {865_esc_send_count++;866} else {867_fail_send_count++;868}869// immediately push data to CAN bus870canard_iface.processTx(true);871}872873for (uint8_t i = 0; i < DRONECAN_SRV_NUMBER; i++) {874_SRV_conf[i].esc_pending = false;875}876}877878#if AP_DRONECAN_HOBBYWING_ESC_SUPPORT879/*880support for Hobbywing DroneCAN ESCs881*/882void AP_DroneCAN::SRV_send_esc_hobbywing(void)883{884com_hobbywing_esc_RawCommand esc_msg;885886uint8_t active_esc_num = 0, max_esc_num = 0;887uint8_t k = 0;888889// esc offset allows for efficient packing of higher ESC numbers in RawCommand890const uint8_t esc_offset = constrain_int16(_esc_offset.get(), 0, DRONECAN_SRV_NUMBER);891892// find out how many esc we have enabled and if they are active at all893for (uint8_t i = esc_offset; i < DRONECAN_SRV_NUMBER; i++) {894if ((((uint32_t) 1) << i) & _ESC_armed_mask) {895max_esc_num = i + 1;896if (_SRV_conf[i].esc_pending) {897active_esc_num++;898}899}900}901902// if at least one is active (update) we need to send to all903if (active_esc_num > 0) {904k = 0;905const bool armed = hal.util->get_soft_armed();906for (uint8_t i = esc_offset; i < max_esc_num && k < 20; i++) {907if (armed && ((((uint32_t) 1U) << i) & _ESC_armed_mask)) {908esc_msg.command.data[k] = scale_esc_output(i);909} else {910esc_msg.command.data[k] = static_cast<unsigned>(0);911}912913k++;914}915esc_msg.command.len = k;916917if (esc_hobbywing_raw.broadcast(esc_msg)) {918_esc_send_count++;919} else {920_fail_send_count++;921}922// immediately push data to CAN bus923canard_iface.processTx(true);924}925}926#endif // AP_DRONECAN_HOBBYWING_ESC_SUPPORT927928void AP_DroneCAN::SRV_push_servos()929{930WITH_SEMAPHORE(SRV_sem);931932for (uint8_t i = 0; i < DRONECAN_SRV_NUMBER; i++) {933// Check if this channels has any function assigned934if (SRV_Channels::channel_function(i) >= SRV_Channel::k_none) {935_SRV_conf[i].pulse = SRV_Channels::srv_channel(i)->get_output_pwm();936_SRV_conf[i].esc_pending = true;937_SRV_conf[i].servo_pending = true;938}939}940941uint32_t servo_armed_mask = _servo_bm;942uint32_t esc_armed_mask = _esc_bm;943const bool safety_off = hal.util->safety_switch_state() != AP_HAL::Util::SAFETY_DISARMED;944if (!safety_off) {945AP_BoardConfig *boardconfig = AP_BoardConfig::get_singleton();946if (boardconfig != nullptr) {947const uint32_t safety_mask = boardconfig->get_safety_mask();948servo_armed_mask &= safety_mask;949esc_armed_mask &= safety_mask;950} else {951servo_armed_mask = 0;952esc_armed_mask = 0;953}954}955_SRV_armed_mask = servo_armed_mask;956_ESC_armed_mask = esc_armed_mask;957958if (_ESC_armed_mask != 0) {959// push ESCs as fast as we can960#if AP_DRONECAN_HOBBYWING_ESC_SUPPORT961if (option_is_set(Options::USE_HOBBYWING_ESC)) {962SRV_send_esc_hobbywing();963} else964#endif965{966SRV_send_esc();967}968}969}970971// notify state send972void AP_DroneCAN::notify_state_send()973{974uint32_t now = AP_HAL::millis();975976if (_notify_state_hz == 0 || (now - _last_notify_state_ms) < uint32_t(1000 / _notify_state_hz)) {977return;978}979980ardupilot_indication_NotifyState msg;981msg.vehicle_state = 0;982if (AP_Notify::flags.initialising) {983msg.vehicle_state |= 1 << ARDUPILOT_INDICATION_NOTIFYSTATE_VEHICLE_STATE_INITIALISING;984}985if (AP_Notify::flags.armed) {986msg.vehicle_state |= 1 << ARDUPILOT_INDICATION_NOTIFYSTATE_VEHICLE_STATE_ARMED;987}988if (AP_Notify::flags.flying) {989msg.vehicle_state |= 1 << ARDUPILOT_INDICATION_NOTIFYSTATE_VEHICLE_STATE_FLYING;990}991if (AP_Notify::flags.compass_cal_running) {992msg.vehicle_state |= 1 << ARDUPILOT_INDICATION_NOTIFYSTATE_VEHICLE_STATE_MAGCAL_RUN;993}994if (AP_Notify::flags.ekf_bad) {995msg.vehicle_state |= 1 << ARDUPILOT_INDICATION_NOTIFYSTATE_VEHICLE_STATE_EKF_BAD;996}997if (AP_Notify::flags.esc_calibration) {998msg.vehicle_state |= 1 << ARDUPILOT_INDICATION_NOTIFYSTATE_VEHICLE_STATE_ESC_CALIBRATION;999}1000if (AP_Notify::flags.failsafe_battery) {1001msg.vehicle_state |= 1 << ARDUPILOT_INDICATION_NOTIFYSTATE_VEHICLE_STATE_FAILSAFE_BATT;1002}1003if (AP_Notify::flags.failsafe_gcs) {1004msg.vehicle_state |= 1 << ARDUPILOT_INDICATION_NOTIFYSTATE_VEHICLE_STATE_FAILSAFE_GCS;1005}1006if (AP_Notify::flags.failsafe_radio) {1007msg.vehicle_state |= 1 << ARDUPILOT_INDICATION_NOTIFYSTATE_VEHICLE_STATE_FAILSAFE_RADIO;1008}1009if (AP_Notify::flags.firmware_update) {1010msg.vehicle_state |= 1 << ARDUPILOT_INDICATION_NOTIFYSTATE_VEHICLE_STATE_FW_UPDATE;1011}1012if (AP_Notify::flags.gps_fusion) {1013msg.vehicle_state |= 1 << ARDUPILOT_INDICATION_NOTIFYSTATE_VEHICLE_STATE_GPS_FUSION;1014}1015if (AP_Notify::flags.gps_glitching) {1016msg.vehicle_state |= 1 << ARDUPILOT_INDICATION_NOTIFYSTATE_VEHICLE_STATE_GPS_GLITCH;1017}1018if (AP_Notify::flags.have_pos_abs) {1019msg.vehicle_state |= 1 << ARDUPILOT_INDICATION_NOTIFYSTATE_VEHICLE_STATE_POS_ABS_AVAIL;1020}1021if (AP_Notify::flags.leak_detected) {1022msg.vehicle_state |= 1 << ARDUPILOT_INDICATION_NOTIFYSTATE_VEHICLE_STATE_LEAK_DET;1023}1024if (AP_Notify::flags.parachute_release) {1025msg.vehicle_state |= 1 << ARDUPILOT_INDICATION_NOTIFYSTATE_VEHICLE_STATE_CHUTE_RELEASED;1026}1027if (AP_Notify::flags.powering_off) {1028msg.vehicle_state |= 1 << ARDUPILOT_INDICATION_NOTIFYSTATE_VEHICLE_STATE_POWERING_OFF;1029}1030if (AP_Notify::flags.pre_arm_check) {1031msg.vehicle_state |= 1 << ARDUPILOT_INDICATION_NOTIFYSTATE_VEHICLE_STATE_PREARM;1032}1033if (AP_Notify::flags.pre_arm_gps_check) {1034msg.vehicle_state |= 1 << ARDUPILOT_INDICATION_NOTIFYSTATE_VEHICLE_STATE_PREARM_GPS;1035}1036if (AP_Notify::flags.save_trim) {1037msg.vehicle_state |= 1 << ARDUPILOT_INDICATION_NOTIFYSTATE_VEHICLE_STATE_SAVE_TRIM;1038}1039if (AP_Notify::flags.vehicle_lost) {1040msg.vehicle_state |= 1 << ARDUPILOT_INDICATION_NOTIFYSTATE_VEHICLE_STATE_LOST;1041}1042if (AP_Notify::flags.video_recording) {1043msg.vehicle_state |= 1 << ARDUPILOT_INDICATION_NOTIFYSTATE_VEHICLE_STATE_VIDEO_RECORDING;1044}1045if (AP_Notify::flags.waiting_for_throw) {1046msg.vehicle_state |= 1 << ARDUPILOT_INDICATION_NOTIFYSTATE_VEHICLE_STATE_THROW_READY;1047}10481049#ifndef HAL_BUILD_AP_PERIPH1050const AP_Vehicle* vehicle = AP::vehicle();1051if (vehicle != nullptr) {1052if (vehicle->is_landing()) {1053msg.vehicle_state |= 1 << ARDUPILOT_INDICATION_NOTIFYSTATE_VEHICLE_STATE_IS_LANDING;1054}1055if (vehicle->is_taking_off()) {1056msg.vehicle_state |= 1 << ARDUPILOT_INDICATION_NOTIFYSTATE_VEHICLE_STATE_IS_TAKING_OFF;1057}1058}1059#endif // HAL_BUILD_AP_PERIPH10601061msg.aux_data_type = ARDUPILOT_INDICATION_NOTIFYSTATE_VEHICLE_YAW_EARTH_CENTIDEGREES;1062uint16_t yaw_cd = (uint16_t)(360.0f - degrees(AP::ahrs().get_yaw()))*100.0f;1063const uint8_t *data = (uint8_t *)&yaw_cd;1064for (uint8_t i=0; i<2; i++) {1065msg.aux_data.data[i] = data[i];1066}1067msg.aux_data.len = 2;1068notify_state.broadcast(msg);1069_last_notify_state_ms = AP_HAL::millis();1070}10711072#if AP_DRONECAN_SEND_GPS1073void AP_DroneCAN::gnss_send_fix()1074{1075const AP_GPS &gps = AP::gps();10761077const uint32_t gps_lib_time_ms = gps.last_message_time_ms();1078if (_gnss.last_gps_lib_fix_ms == gps_lib_time_ms) {1079return;1080}1081_gnss.last_gps_lib_fix_ms = gps_lib_time_ms;10821083/*1084send Fix2 packet1085*/10861087uavcan_equipment_gnss_Fix2 pkt {};1088const Location &loc = gps.location();1089const Vector3f &vel = gps.velocity();10901091pkt.timestamp.usec = AP_HAL::micros64();1092pkt.gnss_timestamp.usec = gps.time_epoch_usec();1093if (pkt.gnss_timestamp.usec == 0) {1094pkt.gnss_time_standard = UAVCAN_EQUIPMENT_GNSS_FIX2_GNSS_TIME_STANDARD_NONE;1095} else {1096pkt.gnss_time_standard = UAVCAN_EQUIPMENT_GNSS_FIX2_GNSS_TIME_STANDARD_UTC;1097}1098pkt.longitude_deg_1e8 = uint64_t(loc.lng) * 10ULL;1099pkt.latitude_deg_1e8 = uint64_t(loc.lat) * 10ULL;1100pkt.height_ellipsoid_mm = loc.alt * 10;1101pkt.height_msl_mm = loc.alt * 10;1102for (uint8_t i=0; i<3; i++) {1103pkt.ned_velocity[i] = vel[i];1104}1105pkt.sats_used = gps.num_sats();1106switch (gps.status()) {1107case AP_GPS::GPS_Status::NO_GPS:1108case AP_GPS::GPS_Status::NO_FIX:1109pkt.status = UAVCAN_EQUIPMENT_GNSS_FIX2_STATUS_NO_FIX;1110pkt.mode = UAVCAN_EQUIPMENT_GNSS_FIX2_MODE_SINGLE;1111pkt.sub_mode = UAVCAN_EQUIPMENT_GNSS_FIX2_SUB_MODE_DGPS_OTHER;1112break;1113case AP_GPS::GPS_Status::GPS_OK_FIX_2D:1114pkt.status = UAVCAN_EQUIPMENT_GNSS_FIX2_STATUS_2D_FIX;1115pkt.mode = UAVCAN_EQUIPMENT_GNSS_FIX2_MODE_SINGLE;1116pkt.sub_mode = UAVCAN_EQUIPMENT_GNSS_FIX2_SUB_MODE_DGPS_OTHER;1117break;1118case AP_GPS::GPS_Status::GPS_OK_FIX_3D:1119pkt.status = UAVCAN_EQUIPMENT_GNSS_FIX2_STATUS_3D_FIX;1120pkt.mode = UAVCAN_EQUIPMENT_GNSS_FIX2_MODE_SINGLE;1121pkt.sub_mode = UAVCAN_EQUIPMENT_GNSS_FIX2_SUB_MODE_DGPS_OTHER;1122break;1123case AP_GPS::GPS_Status::GPS_OK_FIX_3D_DGPS:1124pkt.status = UAVCAN_EQUIPMENT_GNSS_FIX2_STATUS_3D_FIX;1125pkt.mode = UAVCAN_EQUIPMENT_GNSS_FIX2_MODE_DGPS;1126pkt.sub_mode = UAVCAN_EQUIPMENT_GNSS_FIX2_SUB_MODE_DGPS_SBAS;1127break;1128case AP_GPS::GPS_Status::GPS_OK_FIX_3D_RTK_FLOAT:1129pkt.status = UAVCAN_EQUIPMENT_GNSS_FIX2_STATUS_3D_FIX;1130pkt.mode = UAVCAN_EQUIPMENT_GNSS_FIX2_MODE_RTK;1131pkt.sub_mode = UAVCAN_EQUIPMENT_GNSS_FIX2_SUB_MODE_RTK_FLOAT;1132break;1133case AP_GPS::GPS_Status::GPS_OK_FIX_3D_RTK_FIXED:1134pkt.status = UAVCAN_EQUIPMENT_GNSS_FIX2_STATUS_3D_FIX;1135pkt.mode = UAVCAN_EQUIPMENT_GNSS_FIX2_MODE_RTK;1136pkt.sub_mode = UAVCAN_EQUIPMENT_GNSS_FIX2_SUB_MODE_RTK_FIXED;1137break;1138}11391140pkt.covariance.len = 6;1141float hacc;1142if (gps.horizontal_accuracy(hacc)) {1143pkt.covariance.data[0] = pkt.covariance.data[1] = sq(hacc);1144}1145float vacc;1146if (gps.vertical_accuracy(vacc)) {1147pkt.covariance.data[2] = sq(vacc);1148}1149float sacc;1150if (gps.speed_accuracy(sacc)) {1151const float vc3 = sq(sacc);1152pkt.covariance.data[3] = pkt.covariance.data[4] = pkt.covariance.data[5] = vc3;1153}11541155gnss_fix2.broadcast(pkt);1156115711581159const uint32_t now_ms = AP_HAL::millis();1160if (now_ms - _gnss.last_send_status_ms >= 1000) {1161_gnss.last_send_status_ms = now_ms;11621163/*1164send aux packet1165*/1166uavcan_equipment_gnss_Auxiliary pkt_auxiliary {};1167pkt_auxiliary.hdop = gps.get_hdop() * 0.01;1168pkt_auxiliary.vdop = gps.get_vdop() * 0.01;11691170gnss_auxiliary.broadcast(pkt_auxiliary);117111721173/*1174send Status packet1175*/1176ardupilot_gnss_Status pkt_status {};1177pkt_status.healthy = gps.is_healthy();1178if (gps.logging_present() && gps.logging_enabled() && !gps.logging_failed()) {1179pkt_status.status |= ARDUPILOT_GNSS_STATUS_STATUS_LOGGING;1180}1181uint8_t idx; // unused1182if (pkt_status.healthy && !gps.first_unconfigured_gps(idx)) {1183pkt_status.status |= ARDUPILOT_GNSS_STATUS_STATUS_ARMABLE;1184}11851186uint32_t error_codes;1187if (gps.get_error_codes(error_codes)) {1188pkt_status.error_codes = error_codes;1189}11901191gnss_status.broadcast(pkt_status);1192}1193}11941195void AP_DroneCAN::gnss_send_yaw()1196{1197const AP_GPS &gps = AP::gps();11981199if (!gps.have_gps_yaw()) {1200return;1201}12021203float yaw_deg, yaw_acc_deg;1204uint32_t yaw_time_ms;1205if (!gps.gps_yaw_deg(yaw_deg, yaw_acc_deg, yaw_time_ms) && yaw_time_ms != _gnss.last_lib_yaw_time_ms) {1206return;1207}12081209_gnss.last_lib_yaw_time_ms = yaw_time_ms;12101211ardupilot_gnss_Heading pkt_heading {};1212pkt_heading.heading_valid = true;1213pkt_heading.heading_accuracy_valid = is_positive(yaw_acc_deg);1214pkt_heading.heading_rad = radians(yaw_deg);1215pkt_heading.heading_accuracy_rad = radians(yaw_acc_deg);12161217gnss_heading.broadcast(pkt_heading);1218}1219#endif // AP_DRONECAN_SEND_GPS12201221// SafetyState send1222void AP_DroneCAN::safety_state_send()1223{1224uint32_t now = AP_HAL::millis();1225if (now - _last_safety_state_ms < 500) {1226// update at 2Hz1227return;1228}1229_last_safety_state_ms = now;12301231{ // handle SafetyState1232ardupilot_indication_SafetyState safety_msg;1233auto state = hal.util->safety_switch_state();1234if (_SRV_armed_mask != 0 || _ESC_armed_mask != 0) {1235// if we are outputting any servos or ESCs due to1236// BRD_SAFETY_MASK then we need to advertise safety as1237// off, this changes LEDs to indicate unsafe and allows1238// AP_Periph ESCs and servos to run1239state = AP_HAL::Util::SAFETY_ARMED;1240}1241switch (state) {1242case AP_HAL::Util::SAFETY_ARMED:1243safety_msg.status = ARDUPILOT_INDICATION_SAFETYSTATE_STATUS_SAFETY_OFF;1244safety_state.broadcast(safety_msg);1245break;1246case AP_HAL::Util::SAFETY_DISARMED:1247safety_msg.status = ARDUPILOT_INDICATION_SAFETYSTATE_STATUS_SAFETY_ON;1248safety_state.broadcast(safety_msg);1249break;1250default:1251// nothing to send1252break;1253}1254}12551256{ // handle ArmingStatus1257uavcan_equipment_safety_ArmingStatus arming_msg;1258arming_msg.status = hal.util->get_soft_armed() ? UAVCAN_EQUIPMENT_SAFETY_ARMINGSTATUS_STATUS_FULLY_ARMED :1259UAVCAN_EQUIPMENT_SAFETY_ARMINGSTATUS_STATUS_DISARMED;1260arming_status.broadcast(arming_msg);1261}1262}12631264// Send relay outputs with hardpoint msg1265#if AP_RELAY_DRONECAN_ENABLED1266void AP_DroneCAN::relay_hardpoint_send()1267{1268const uint32_t now = AP_HAL::millis();1269if ((_relay.rate_hz == 0) || ((now - _relay.last_send_ms) < uint32_t(1000 / _relay.rate_hz))) {1270// Rate limit per user config1271return;1272}1273_relay.last_send_ms = now;12741275AP_Relay *relay = AP::relay();1276if (relay == nullptr) {1277return;1278}12791280uavcan_equipment_hardpoint_Command msg {};12811282// Relay will populate the next command to send and update the last index1283// This will cycle through each relay in turn1284if (relay->dronecan.populate_next_command(_relay.last_index, msg)) {1285relay_hardpoint.broadcast(msg);1286}12871288}1289#endif // AP_RELAY_DRONECAN_ENABLED12901291/*1292handle Button message1293*/1294void AP_DroneCAN::handle_button(const CanardRxTransfer& transfer, const ardupilot_indication_Button& msg)1295{1296switch (msg.button) {1297case ARDUPILOT_INDICATION_BUTTON_BUTTON_SAFETY: {1298AP_BoardConfig *brdconfig = AP_BoardConfig::get_singleton();1299if (brdconfig && brdconfig->safety_button_handle_pressed(msg.press_time)) {1300AP_HAL::Util::safety_state state = hal.util->safety_switch_state();1301if (state == AP_HAL::Util::SAFETY_ARMED) {1302hal.rcout->force_safety_on();1303} else {1304hal.rcout->force_safety_off();1305}1306}1307break;1308}1309}1310}13111312/*1313handle traffic report1314*/1315void AP_DroneCAN::handle_traffic_report(const CanardRxTransfer& transfer, const ardupilot_equipment_trafficmonitor_TrafficReport& msg)1316{1317#if HAL_ADSB_ENABLED1318AP_ADSB *adsb = AP::ADSB();1319if (!adsb || !adsb->enabled()) {1320// ADSB not enabled1321return;1322}13231324AP_ADSB::adsb_vehicle_t vehicle;1325mavlink_adsb_vehicle_t &pkt = vehicle.info;13261327pkt.ICAO_address = msg.icao_address;1328pkt.tslc = msg.tslc;1329pkt.lat = msg.latitude_deg_1e7;1330pkt.lon = msg.longitude_deg_1e7;1331pkt.altitude = msg.alt_m * 1000;1332pkt.heading = degrees(msg.heading) * 100;1333pkt.hor_velocity = norm(msg.velocity[0], msg.velocity[1]) * 100;1334pkt.ver_velocity = -msg.velocity[2] * 100;1335pkt.squawk = msg.squawk;1336for (uint8_t i=0; i<9; i++) {1337pkt.callsign[i] = msg.callsign[i];1338}1339pkt.emitter_type = msg.traffic_type;13401341if (msg.alt_type == ARDUPILOT_EQUIPMENT_TRAFFICMONITOR_TRAFFICREPORT_ALT_TYPE_PRESSURE_AMSL) {1342pkt.flags |= ADSB_FLAGS_VALID_ALTITUDE;1343pkt.altitude_type = ADSB_ALTITUDE_TYPE_PRESSURE_QNH;1344} else if (msg.alt_type == ARDUPILOT_EQUIPMENT_TRAFFICMONITOR_TRAFFICREPORT_ALT_TYPE_WGS84) {1345pkt.flags |= ADSB_FLAGS_VALID_ALTITUDE;1346pkt.altitude_type = ADSB_ALTITUDE_TYPE_GEOMETRIC;1347}13481349if (msg.lat_lon_valid) {1350pkt.flags |= ADSB_FLAGS_VALID_COORDS;1351}1352if (msg.heading_valid) {1353pkt.flags |= ADSB_FLAGS_VALID_HEADING;1354}1355if (msg.velocity_valid) {1356pkt.flags |= ADSB_FLAGS_VALID_VELOCITY;1357}1358if (msg.callsign_valid) {1359pkt.flags |= ADSB_FLAGS_VALID_CALLSIGN;1360}1361if (msg.ident_valid) {1362pkt.flags |= ADSB_FLAGS_VALID_SQUAWK;1363}1364if (msg.simulated_report) {1365pkt.flags |= ADSB_FLAGS_SIMULATED;1366}1367if (msg.vertical_velocity_valid) {1368pkt.flags |= ADSB_FLAGS_VERTICAL_VELOCITY_VALID;1369}1370if (msg.baro_valid) {1371pkt.flags |= ADSB_FLAGS_BARO_VALID;1372}13731374vehicle.last_update_ms = AP_HAL::millis() - (vehicle.info.tslc * 1000);1375adsb->handle_adsb_vehicle(vehicle);1376#endif1377}13781379/*1380handle actuator status message1381*/1382#if AP_SERVO_TELEM_ENABLED1383void AP_DroneCAN::handle_actuator_status(const CanardRxTransfer& transfer, const uavcan_equipment_actuator_Status& msg)1384{1385AP_Servo_Telem *servo_telem = AP_Servo_Telem::get_singleton();1386if (servo_telem == nullptr) {1387return;1388}13891390const AP_Servo_Telem::TelemetryData telem_data {1391.measured_position = ToDeg(msg.position),1392.force = msg.force,1393.speed = msg.speed,1394.duty_cycle = msg.power_rating_pct,1395.present_types = AP_Servo_Telem::TelemetryData::Types::MEASURED_POSITION |1396AP_Servo_Telem::TelemetryData::Types::FORCE |1397AP_Servo_Telem::TelemetryData::Types::SPEED |1398AP_Servo_Telem::TelemetryData::Types::DUTY_CYCLE1399};14001401servo_telem->update_telem_data(msg.actuator_id, telem_data);1402}1403#endif14041405#if AP_DRONECAN_HIMARK_SERVO_SUPPORT && AP_SERVO_TELEM_ENABLED1406/*1407handle himark ServoInfo message1408*/1409void AP_DroneCAN::handle_himark_servoinfo(const CanardRxTransfer& transfer, const com_himark_servo_ServoInfo &msg)1410{1411AP_Servo_Telem *servo_telem = AP_Servo_Telem::get_singleton();1412if (servo_telem == nullptr) {1413return;1414}14151416const AP_Servo_Telem::TelemetryData telem_data {1417.command_position = msg.pos_cmd * 0.01,1418.measured_position = msg.pos_sensor * 0.01,1419.voltage = msg.voltage * 0.01,1420.current = msg.current * 0.01,1421.motor_temperature_cdeg = int16_t(((msg.motor_temp * 0.2) - 40) * 100),1422.pcb_temperature_cdeg = int16_t(((msg.pcb_temp * 0.2) - 40) * 100),1423.status_flags = msg.error_status,1424.present_types = AP_Servo_Telem::TelemetryData::Types::COMMANDED_POSITION |1425AP_Servo_Telem::TelemetryData::Types::MEASURED_POSITION |1426AP_Servo_Telem::TelemetryData::Types::VOLTAGE |1427AP_Servo_Telem::TelemetryData::Types::CURRENT |1428AP_Servo_Telem::TelemetryData::Types::MOTOR_TEMP |1429AP_Servo_Telem::TelemetryData::Types::PCB_TEMP |1430AP_Servo_Telem::TelemetryData::Types::STATUS1431};14321433servo_telem->update_telem_data(msg.servo_id, telem_data);1434}1435#endif // AP_DRONECAN_HIMARK_SERVO_SUPPORT14361437#if AP_DRONECAN_VOLZ_FEEDBACK_ENABLED1438void AP_DroneCAN::handle_actuator_status_Volz(const CanardRxTransfer& transfer, const com_volz_servo_ActuatorStatus& msg)1439{1440AP_Servo_Telem *servo_telem = AP_Servo_Telem::get_singleton();1441if (servo_telem == nullptr) {1442return;1443}14441445const AP_Servo_Telem::TelemetryData telem_data {1446.measured_position = ToDeg(msg.actual_position),1447.voltage = msg.voltage * 0.2,1448.current = msg.current * 0.025,1449.duty_cycle = uint8_t(msg.motor_pwm * (100.0/255.0)),1450.motor_temperature_cdeg = int16_t((msg.motor_temperature - 50) * 100),1451.present_types = AP_Servo_Telem::TelemetryData::Types::MEASURED_POSITION |1452AP_Servo_Telem::TelemetryData::Types::VOLTAGE |1453AP_Servo_Telem::TelemetryData::Types::CURRENT |1454AP_Servo_Telem::TelemetryData::Types::DUTY_CYCLE |1455AP_Servo_Telem::TelemetryData::Types::MOTOR_TEMP1456};14571458servo_telem->update_telem_data(msg.actuator_id, telem_data);1459}1460#endif14611462/*1463handle ESC status message1464*/1465void AP_DroneCAN::handle_ESC_status(const CanardRxTransfer& transfer, const uavcan_equipment_esc_Status& msg)1466{1467#if HAL_WITH_ESC_TELEM1468const uint8_t esc_offset = constrain_int16(_esc_offset.get(), 0, DRONECAN_SRV_NUMBER);1469const uint8_t esc_index = msg.esc_index + esc_offset;14701471if (!is_esc_data_index_valid(esc_index)) {1472return;1473}14741475TelemetryData t {1476.temperature_cdeg = int16_t((KELVIN_TO_C(msg.temperature)) * 100),1477.voltage = msg.voltage,1478.current = msg.current,1479#if AP_EXTENDED_ESC_TELEM_ENABLED1480.power_percentage = msg.power_rating_pct,1481#endif1482};14831484update_rpm(esc_index, msg.rpm, msg.error_count);1485update_telem_data(esc_index, t,1486(isnan(msg.current) ? 0 : AP_ESC_Telem_Backend::TelemetryType::CURRENT)1487| (isnan(msg.voltage) ? 0 : AP_ESC_Telem_Backend::TelemetryType::VOLTAGE)1488| (isnan(msg.temperature) ? 0 : AP_ESC_Telem_Backend::TelemetryType::TEMPERATURE)1489#if AP_EXTENDED_ESC_TELEM_ENABLED1490| AP_ESC_Telem_Backend::TelemetryType::POWER_PERCENTAGE1491#endif1492);1493#endif // HAL_WITH_ESC_TELEM1494}14951496#if AP_EXTENDED_ESC_TELEM_ENABLED1497/*1498handle Extended ESC status message1499*/1500void AP_DroneCAN::handle_esc_ext_status(const CanardRxTransfer& transfer, const uavcan_equipment_esc_StatusExtended& msg)1501{1502const uint8_t esc_offset = constrain_int16(_esc_offset.get(), 0, DRONECAN_SRV_NUMBER);1503const uint8_t esc_index = msg.esc_index + esc_offset;15041505if (!is_esc_data_index_valid(esc_index)) {1506return;1507}15081509TelemetryData telemetryData {1510.motor_temp_cdeg = (int16_t)(msg.motor_temperature_degC * 100),1511.input_duty = msg.input_pct,1512.output_duty = msg.output_pct,1513.flags = msg.status_flags,1514};15151516update_telem_data(esc_index, telemetryData,1517AP_ESC_Telem_Backend::TelemetryType::MOTOR_TEMPERATURE1518| AP_ESC_Telem_Backend::TelemetryType::INPUT_DUTY1519| AP_ESC_Telem_Backend::TelemetryType::OUTPUT_DUTY1520| AP_ESC_Telem_Backend::TelemetryType::FLAGS);1521}1522#endif // AP_EXTENDED_ESC_TELEM_ENABLED15231524bool AP_DroneCAN::is_esc_data_index_valid(const uint8_t index) {1525if (index > DRONECAN_SRV_NUMBER) {1526// printf("DroneCAN: invalid esc index: %d. max index allowed: %d\n\r", index, DRONECAN_SRV_NUMBER);1527return false;1528}1529return true;1530}15311532#if AP_SCRIPTING_ENABLED1533/*1534handle FlexDebug message, holding a copy locally for a lua script to access1535*/1536void AP_DroneCAN::handle_FlexDebug(const CanardRxTransfer& transfer, const dronecan_protocol_FlexDebug &msg)1537{1538if (!option_is_set(Options::ENABLE_FLEX_DEBUG)) {1539return;1540}15411542// find an existing element in the list1543const uint8_t source_node = transfer.source_node_id;1544for (auto *p = flexDebug_list; p != nullptr; p = p->next) {1545if (p->node_id == source_node && p->msg.id == msg.id) {1546p->msg = msg;1547p->timestamp_us = uint32_t(transfer.timestamp_usec);1548return;1549}1550}15511552// new message ID, add to the list. Note that this gets called1553// only from one thread, so no lock needed1554auto *p = NEW_NOTHROW FlexDebug;1555if (p == nullptr) {1556return;1557}1558p->node_id = source_node;1559p->msg = msg;1560p->timestamp_us = uint32_t(transfer.timestamp_usec);1561p->next = flexDebug_list;15621563// link into the list1564flexDebug_list = p;1565}15661567/*1568get the last FlexDebug message from a node1569*/1570bool AP_DroneCAN::get_FlexDebug(uint8_t node_id, uint16_t msg_id, uint32_t ×tamp_us, dronecan_protocol_FlexDebug &msg) const1571{1572for (const auto *p = flexDebug_list; p != nullptr; p = p->next) {1573if (p->node_id == node_id && p->msg.id == msg_id) {1574if (timestamp_us == p->timestamp_us) {1575// stale message1576return false;1577}1578timestamp_us = p->timestamp_us;1579msg = p->msg;1580return true;1581}1582}1583return false;1584}15851586#endif // AP_SCRIPTING_ENABLED15871588/*1589handle LogMessage debug1590*/1591void AP_DroneCAN::handle_debug(const CanardRxTransfer& transfer, const uavcan_protocol_debug_LogMessage& msg)1592{1593#if AP_HAVE_GCS_SEND_TEXT1594const auto log_level = AP::can().get_log_level();1595const auto msg_level = msg.level.value;1596bool send_mavlink = false;15971598if (log_level != AP_CANManager::LOG_NONE) {1599// log to onboard log and mavlink1600enum MAV_SEVERITY mavlink_level = MAV_SEVERITY_INFO;1601switch (msg_level) {1602case UAVCAN_PROTOCOL_DEBUG_LOGLEVEL_DEBUG:1603mavlink_level = MAV_SEVERITY_DEBUG;1604send_mavlink = uint8_t(log_level) >= uint8_t(AP_CANManager::LogLevel::LOG_DEBUG);1605break;1606case UAVCAN_PROTOCOL_DEBUG_LOGLEVEL_INFO:1607mavlink_level = MAV_SEVERITY_INFO;1608send_mavlink = uint8_t(log_level) >= uint8_t(AP_CANManager::LogLevel::LOG_INFO);1609break;1610case UAVCAN_PROTOCOL_DEBUG_LOGLEVEL_WARNING:1611mavlink_level = MAV_SEVERITY_WARNING;1612send_mavlink = uint8_t(log_level) >= uint8_t(AP_CANManager::LogLevel::LOG_WARNING);1613break;1614default:1615send_mavlink = uint8_t(log_level) >= uint8_t(AP_CANManager::LogLevel::LOG_ERROR);1616mavlink_level = MAV_SEVERITY_ERROR;1617break;1618}1619if (send_mavlink) {1620// when we send as MAVLink it also gets logged locally, so1621// we return to avoid a duplicate1622GCS_SEND_TEXT(mavlink_level, "CAN[%u] %s", transfer.source_node_id, msg.text.data);1623return;1624}1625}1626#endif // AP_HAVE_GCS_SEND_TEXT16271628#if HAL_LOGGING_ENABLED1629// always log locally if we have logging enabled1630AP::logger().Write_MessageF("CAN[%u] %s", transfer.source_node_id, msg.text.data);1631#endif1632}16331634/*1635check for parameter get/set response timeout1636*/1637void AP_DroneCAN::check_parameter_callback_timeout()1638{1639WITH_SEMAPHORE(_param_sem);16401641// return immediately if not waiting for get/set parameter response1642if (param_request_sent_ms == 0) {1643return;1644}16451646const uint32_t now_ms = AP_HAL::millis();1647if (now_ms - param_request_sent_ms > AP_DRONECAN_GETSET_TIMEOUT_MS) {1648param_request_sent_ms = 0;1649param_int_cb = nullptr;1650param_float_cb = nullptr;1651param_string_cb = nullptr;1652}1653}16541655/*1656send any queued request to get/set parameter1657called from loop1658*/1659void AP_DroneCAN::send_parameter_request()1660{1661WITH_SEMAPHORE(_param_sem);1662if (param_request_sent) {1663return;1664}1665param_get_set_client.request(param_request_node_id, param_getset_req);1666param_request_sent = true;1667}16681669/*1670set named float parameter on node1671*/1672bool AP_DroneCAN::set_parameter_on_node(uint8_t node_id, const char *name, float value, ParamGetSetFloatCb *cb)1673{1674WITH_SEMAPHORE(_param_sem);16751676// fail if waiting for any previous get/set request1677if (param_int_cb != nullptr ||1678param_float_cb != nullptr ||1679param_string_cb != nullptr) {1680return false;1681}1682param_getset_req.index = 0;1683param_getset_req.name.len = strncpy_noterm((char*)param_getset_req.name.data, name, sizeof(param_getset_req.name.data)-1);1684param_getset_req.value.real_value = value;1685param_getset_req.value.union_tag = UAVCAN_PROTOCOL_PARAM_VALUE_REAL_VALUE;1686param_float_cb = cb;1687param_request_sent = false;1688param_request_sent_ms = AP_HAL::millis();1689param_request_node_id = node_id;1690return true;1691}16921693/*1694set named integer parameter on node1695*/1696bool AP_DroneCAN::set_parameter_on_node(uint8_t node_id, const char *name, int32_t value, ParamGetSetIntCb *cb)1697{1698WITH_SEMAPHORE(_param_sem);16991700// fail if waiting for any previous get/set request1701if (param_int_cb != nullptr ||1702param_float_cb != nullptr ||1703param_string_cb != nullptr) {1704return false;1705}1706param_getset_req.index = 0;1707param_getset_req.name.len = strncpy_noterm((char*)param_getset_req.name.data, name, sizeof(param_getset_req.name.data)-1);1708param_getset_req.value.integer_value = value;1709param_getset_req.value.union_tag = UAVCAN_PROTOCOL_PARAM_VALUE_INTEGER_VALUE;1710param_int_cb = cb;1711param_request_sent = false;1712param_request_sent_ms = AP_HAL::millis();1713param_request_node_id = node_id;1714return true;1715}17161717/*1718set named string parameter on node1719*/1720bool AP_DroneCAN::set_parameter_on_node(uint8_t node_id, const char *name, const string &value, ParamGetSetStringCb *cb)1721{1722WITH_SEMAPHORE(_param_sem);17231724// fail if waiting for any previous get/set request1725if (param_int_cb != nullptr ||1726param_float_cb != nullptr ||1727param_string_cb != nullptr) {1728return false;1729}1730param_getset_req.index = 0;1731param_getset_req.name.len = strncpy_noterm((char*)param_getset_req.name.data, name, sizeof(param_getset_req.name.data)-1);1732memcpy(¶m_getset_req.value.string_value, (const void*)&value, sizeof(value));1733param_getset_req.value.union_tag = UAVCAN_PROTOCOL_PARAM_VALUE_STRING_VALUE;1734param_string_cb = cb;1735param_request_sent = false;1736param_request_sent_ms = AP_HAL::millis();1737param_request_node_id = node_id;1738return true;1739}17401741/*1742get named float parameter on node1743*/1744bool AP_DroneCAN::get_parameter_on_node(uint8_t node_id, const char *name, ParamGetSetFloatCb *cb)1745{1746WITH_SEMAPHORE(_param_sem);17471748// fail if waiting for any previous get/set request1749if (param_int_cb != nullptr ||1750param_float_cb != nullptr ||1751param_string_cb != nullptr) {1752return false;1753}1754param_getset_req.index = 0;1755param_getset_req.name.len = strncpy_noterm((char*)param_getset_req.name.data, name, sizeof(param_getset_req.name.data));1756param_getset_req.value.union_tag = UAVCAN_PROTOCOL_PARAM_VALUE_EMPTY;1757param_float_cb = cb;1758param_request_sent = false;1759param_request_sent_ms = AP_HAL::millis();1760param_request_node_id = node_id;1761return true;1762}17631764/*1765get named integer parameter on node1766*/1767bool AP_DroneCAN::get_parameter_on_node(uint8_t node_id, const char *name, ParamGetSetIntCb *cb)1768{1769WITH_SEMAPHORE(_param_sem);17701771// fail if waiting for any previous get/set request1772if (param_int_cb != nullptr ||1773param_float_cb != nullptr ||1774param_string_cb != nullptr) {1775return false;1776}1777param_getset_req.index = 0;1778param_getset_req.name.len = strncpy_noterm((char*)param_getset_req.name.data, name, sizeof(param_getset_req.name.data));1779param_getset_req.value.union_tag = UAVCAN_PROTOCOL_PARAM_VALUE_EMPTY;1780param_int_cb = cb;1781param_request_sent = false;1782param_request_sent_ms = AP_HAL::millis();1783param_request_node_id = node_id;1784return true;1785}17861787/*1788get named string parameter on node1789*/1790bool AP_DroneCAN::get_parameter_on_node(uint8_t node_id, const char *name, ParamGetSetStringCb *cb)1791{1792WITH_SEMAPHORE(_param_sem);17931794// fail if waiting for any previous get/set request1795if (param_int_cb != nullptr ||1796param_float_cb != nullptr ||1797param_string_cb != nullptr) {1798return false;1799}1800param_getset_req.index = 0;1801param_getset_req.name.len = strncpy_noterm((char*)param_getset_req.name.data, name, sizeof(param_getset_req.name.data));1802param_getset_req.value.union_tag = UAVCAN_PROTOCOL_PARAM_VALUE_EMPTY;1803param_string_cb = cb;1804param_request_sent = false;1805param_request_sent_ms = AP_HAL::millis();1806param_request_node_id = node_id;1807return true;1808}18091810void AP_DroneCAN::handle_param_get_set_response(const CanardRxTransfer& transfer, const uavcan_protocol_param_GetSetResponse& rsp)1811{1812WITH_SEMAPHORE(_param_sem);1813if (!param_int_cb &&1814!param_float_cb &&1815!param_string_cb) {1816return;1817}1818if ((rsp.value.union_tag == UAVCAN_PROTOCOL_PARAM_VALUE_INTEGER_VALUE) && param_int_cb) {1819int32_t val = rsp.value.integer_value;1820if ((*param_int_cb)(this, transfer.source_node_id, (const char*)rsp.name.data, val)) {1821// we want the parameter to be set with val1822param_getset_req.index = 0;1823memcpy(param_getset_req.name.data, rsp.name.data, rsp.name.len);1824param_getset_req.value.integer_value = val;1825param_getset_req.value.union_tag = UAVCAN_PROTOCOL_PARAM_VALUE_INTEGER_VALUE;1826param_request_sent = false;1827param_request_sent_ms = AP_HAL::millis();1828param_request_node_id = transfer.source_node_id;1829return;1830}1831} else if ((rsp.value.union_tag == UAVCAN_PROTOCOL_PARAM_VALUE_REAL_VALUE) && param_float_cb) {1832float val = rsp.value.real_value;1833if ((*param_float_cb)(this, transfer.source_node_id, (const char*)rsp.name.data, val)) {1834// we want the parameter to be set with val1835param_getset_req.index = 0;1836memcpy(param_getset_req.name.data, rsp.name.data, rsp.name.len);1837param_getset_req.value.real_value = val;1838param_getset_req.value.union_tag = UAVCAN_PROTOCOL_PARAM_VALUE_REAL_VALUE;1839param_request_sent = false;1840param_request_sent_ms = AP_HAL::millis();1841param_request_node_id = transfer.source_node_id;1842return;1843}1844} else if ((rsp.value.union_tag == UAVCAN_PROTOCOL_PARAM_VALUE_STRING_VALUE) && param_string_cb) {1845string val;1846memcpy(&val, &rsp.value.string_value, sizeof(val));1847if ((*param_string_cb)(this, transfer.source_node_id, (const char*)rsp.name.data, val)) {1848// we want the parameter to be set with val1849param_getset_req.index = 0;1850memcpy(param_getset_req.name.data, rsp.name.data, rsp.name.len);1851memcpy(¶m_getset_req.value.string_value, &val, sizeof(val));1852param_getset_req.value.union_tag = UAVCAN_PROTOCOL_PARAM_VALUE_STRING_VALUE;1853param_request_sent = false;1854param_request_sent_ms = AP_HAL::millis();1855param_request_node_id = transfer.source_node_id;1856return;1857}1858}18591860param_request_sent_ms = 0;1861param_int_cb = nullptr;1862param_float_cb = nullptr;1863param_string_cb = nullptr;1864}18651866void AP_DroneCAN::send_parameter_save_request()1867{1868WITH_SEMAPHORE(_param_save_sem);1869if (param_save_request_sent) {1870return;1871}1872param_save_client.request(param_save_request_node_id, param_save_req);1873param_save_request_sent = true;1874}18751876bool AP_DroneCAN::save_parameters_on_node(uint8_t node_id, ParamSaveCb *cb)1877{1878WITH_SEMAPHORE(_param_save_sem);1879if (save_param_cb != nullptr) {1880//busy1881return false;1882}18831884param_save_req.opcode = UAVCAN_PROTOCOL_PARAM_EXECUTEOPCODE_REQUEST_OPCODE_SAVE;1885param_save_request_sent = false;1886param_save_request_node_id = node_id;1887save_param_cb = cb;1888return true;1889}18901891// handle parameter save request response1892void AP_DroneCAN::handle_param_save_response(const CanardRxTransfer& transfer, const uavcan_protocol_param_ExecuteOpcodeResponse& rsp)1893{1894WITH_SEMAPHORE(_param_save_sem);1895if (!save_param_cb) {1896return;1897}1898(*save_param_cb)(this, transfer.source_node_id, rsp.ok);1899save_param_cb = nullptr;1900}19011902// Send Reboot command1903// Note: Do not call this from outside DroneCAN thread context,1904// THIS IS NOT A THREAD SAFE API!1905void AP_DroneCAN::send_reboot_request(uint8_t node_id)1906{1907uavcan_protocol_RestartNodeRequest request;1908request.magic_number = UAVCAN_PROTOCOL_RESTARTNODE_REQUEST_MAGIC_NUMBER;1909restart_node_client.request(node_id, request);1910}19111912// check if a option is set and if it is then reset it to 0.1913// return true if it was set1914bool AP_DroneCAN::check_and_reset_option(Options option)1915{1916bool ret = option_is_set(option);1917if (ret) {1918_options.set_and_save(int16_t(_options.get() & ~uint16_t(option)));1919}1920return ret;1921}19221923// handle prearm check1924bool AP_DroneCAN::prearm_check(char* fail_msg, uint8_t fail_msg_len) const1925{1926// forward this to DNA_Server1927return _dna_server.prearm_check(fail_msg, fail_msg_len);1928}19291930/*1931periodic logging1932*/1933void AP_DroneCAN::logging(void)1934{1935#if HAL_LOGGING_ENABLED1936const uint32_t now_ms = AP_HAL::millis();1937if (now_ms - last_log_ms < 1000) {1938return;1939}1940last_log_ms = now_ms;1941if (HAL_NUM_CAN_IFACES <= _driver_index) {1942// no interface?1943return;1944}1945const auto *iface = hal.can[_driver_index];1946if (iface == nullptr) {1947return;1948}1949const auto *stats = iface->get_statistics();1950if (stats == nullptr) {1951// statistics not implemented on this interface1952return;1953}1954const auto &s = *stats;19551956// @LoggerMessage: CANS1957// @Description: CAN Bus Statistics1958// @Field: TimeUS: Time since system startup1959// @Field: I: driver index1960// @Field: T: transmit success count1961// @Field: Trq: transmit request count1962// @Field: Trej: transmit reject count1963// @Field: Tov: transmit overflow count1964// @Field: Tto: transmit timeout count1965// @Field: Tab: transmit abort count1966// @Field: R: receive count1967// @Field: Rov: receive overflow count1968// @Field: Rer: receive error count1969// @Field: Bo: bus offset error count1970// @Field: Etx: ESC successful send count1971// @Field: Stx: Servo successful send count1972// @Field: Ftx: ESC/Servo failed-to-send count1973AP::logger().WriteStreaming("CANS",1974"TimeUS,I,T,Trq,Trej,Tov,Tto,Tab,R,Rov,Rer,Bo,Etx,Stx,Ftx",1975"s#-------------",1976"F--------------",1977"QBIIIIIIIIIIIII",1978AP_HAL::micros64(),1979_driver_index,1980s.tx_success,1981s.tx_requests,1982s.tx_rejected,1983s.tx_overflow,1984s.tx_timedout,1985s.tx_abort,1986s.rx_received,1987s.rx_overflow,1988s.rx_errors,1989s.num_busoff_err,1990_esc_send_count,1991_srv_send_count,1992_fail_send_count);1993#endif // HAL_LOGGING_ENABLED1994}19951996// add an 11 bit auxillary driver1997bool AP_DroneCAN::add_11bit_driver(CANSensor *sensor)1998{1999return canard_iface.add_11bit_driver(sensor);2000}20012002// handler for outgoing frames for auxillary drivers2003bool AP_DroneCAN::write_aux_frame(AP_HAL::CANFrame &out_frame, const uint32_t timeout_us)2004{2005if (out_frame.isExtended()) {2006// don't allow extended frames to be sent by auxillary driver2007return false;2008}2009return canard_iface.write_aux_frame(out_frame, timeout_us);2010}20112012#endif // HAL_NUM_CAN_IFACES201320142015