diff --git a/.gitmodules b/.gitmodules
index e386fc5260..71b3af1e13 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -50,3 +50,6 @@
[submodule "sw/ext/matrix"]
path = sw/ext/matrix
url = https://github.com/PX4/Matrix.git
+[submodule "sw/ext/libcanard"]
+ path = sw/ext/libcanard
+ url = https://github.com/UAVCAN/libcanard.git
diff --git a/conf/airframes/tudelft/neddrone4.xml b/conf/airframes/tudelft/neddrone4.xml
new file mode 100644
index 0000000000..dce6ce8a07
--- /dev/null
+++ b/conf/airframes/tudelft/neddrone4.xml
@@ -0,0 +1,457 @@
+
+
+
+
+
+ Neddrone4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/conf/modules/actuators_uavcan.xml b/conf/modules/actuators_uavcan.xml
new file mode 100644
index 0000000000..df9bed532b
--- /dev/null
+++ b/conf/modules/actuators_uavcan.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+ UAVCan actuators
+
+
+
+ uavcan
+
+
+
+
+
+
+
diff --git a/conf/modules/uavcan.xml b/conf/modules/uavcan.xml
new file mode 100644
index 0000000000..e95efd3667
--- /dev/null
+++ b/conf/modules/uavcan.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+ UAVCan interface for transmitting/receiving uavcan messages on CAN busses
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/conf/simulator/jsbsim/aircraft/Systems/aerodynamics_nederdrone.xml b/conf/simulator/jsbsim/aircraft/Systems/aerodynamics_nederdrone.xml
new file mode 100644
index 0000000000..d02fd3e7ab
--- /dev/null
+++ b/conf/simulator/jsbsim/aircraft/Systems/aerodynamics_nederdrone.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+ Lift due to alpha
+
+ aero/qbar-psf
+ metrics/Sw-sqft
+
+ aero/alpha-deg
+
+ -180 0.14
+ -140 -0.80
+ -110 -0.63
+ -102 -0.74
+ -98 -0.64
+ -90 0.07
+ -79 1.15
+ -76 1.2
+ -72 1.1
+ -45 0.8
+ 0 0.0
+ 45 0.8
+ 90 0.0
+ 135 -0.8
+ 180 0.0
+
+
+
+
+
+
+
+
+
+
+ Drag
+
+ aero/qbar-psf
+ metrics/Sw-sqft
+
+ aero/alpha-deg
+
+ -180 1.7
+ -160 1.62
+ -100 0.4
+ -95 0.27
+ -92 0.24
+ -88 0.225
+ -83 0.235
+ -78 0.29
+ -65 0.6128
+ -20 1.36
+ -4 1.45
+ 15 1.35
+ 30 1.12
+
+
+ 1.2
+
+
+
+
+
+
+
+
+ Side force due to beta
+
+ aero/qbar-psf
+ metrics/Sw-sqft
+ aero/beta-rad
+ -4
+
+
+
+
+
+
\ No newline at end of file
diff --git a/conf/simulator/jsbsim/aircraft/nederdrone.xml b/conf/simulator/jsbsim/aircraft/nederdrone.xml
new file mode 100644
index 0000000000..b6e282f89e
--- /dev/null
+++ b/conf/simulator/jsbsim/aircraft/nederdrone.xml
@@ -0,0 +1,606 @@
+
+
+
+
+
+ Ewoud Smeur
+ 23-04-2020
+ Version 0.9 - beta
+ Nederdrone
+
+
+
+ 1
+ 2
+ 0.25
+ 0
+ 0
+ 0
+ 0
+ 90
+
+ 0
+ 0
+ 0
+
+
+ 0
+ 0
+ 0
+
+
+ 0
+ 0
+ 0
+
+
+
+
+ 2.25
+ 0.3322
+ 4.0
+ 0.
+ 0.
+ 0.
+ 13.0
+
+ 0
+ 0
+ 0
+
+
+
+
+
+
+ 0.41
+ 0.2
+ -0.11
+
+ 0.8
+ 0.5
+ 500
+ 100
+ 1000
+ 0.0
+ NONE
+ 0
+
+
+
+
+ 0.41
+ -0.2
+ -0.11
+
+ 0.8
+ 0.5
+ 500
+ 100
+ 1000
+ 0.0
+ NONE
+ 0
+
+
+
+
+ 0.11
+ 0.2
+ 0.41
+
+ 0.8
+ 0.5
+ 500
+ 100
+ 1000
+ 0.0
+ NONE
+ 0
+
+
+
+
+ 0.11
+ -0.2
+ 0.41
+
+ 0.8
+ 0.5
+ 500
+ 100
+ 1000
+ 0.0
+ NONE
+ 0
+
+
+
+
+
+
+
+
+ fcs/m1
+
+ 0
+ 1
+
+ 18.0
+
+
+
+ fcs/m2
+
+ 0
+ 1
+
+ 18.0
+
+
+
+ fcs/m3
+
+ 0
+ 1
+
+ 18.0
+
+
+
+ fcs/m4
+
+ 0
+ 1
+
+ 18.0
+
+
+
+ fcs/m5
+
+ 0
+ 1
+
+ 18.0
+
+
+
+ fcs/m6
+
+ 0
+ 1
+
+ 18.0
+
+
+
+ fcs/m7
+
+ 0
+ 1
+
+ 18.0
+
+
+
+ fcs/m8
+
+ 0
+ 1
+
+ 18.0
+
+
+
+ fcs/m9
+
+ 0
+ 1
+
+ 18.0
+
+
+
+ fcs/m10
+
+ 0
+ 1
+
+ 18.0
+
+
+
+ fcs/m11
+
+ 0
+ 1
+
+ 18.0
+
+
+
+ fcs/m12
+
+ 0
+ 1
+
+ 18.0
+
+
+
+
+
+
+
+ fcs/m1
+ fcs/m2
+ fcs/m3
+ fcs/m4
+ fcs/m5
+ fcs/m6
+ fcs/m7
+ fcs/m8
+ fcs/m9
+ fcs/m10
+ fcs/m11
+ fcs/m12
+ fcs/m1_lag
+ fcs/m2_lag
+ fcs/m3_lag
+ fcs/m4_lag
+ fcs/m5_lag
+ fcs/m6_lag
+ fcs/m7_lag
+ fcs/m8_lag
+ fcs/m9_lag
+ fcs/m10_lag
+ fcs/m11_lag
+ fcs/m12_lag
+ fcs/ail1
+ fcs/ail2
+ fcs/ail3
+ fcs/ail4
+ fcs/flap1
+ fcs/flap2
+ fcs/flap3
+ fcs/flap4
+
+
+
+
+
+
+
+ fcs/m1_lag
+ 3.822
+
+
+
+ -0.141
+ -1.105
+ 0
+
+
+ 0
+ 0
+ -1
+
+
+
+
+
+ fcs/m2_lag
+ 3.822
+
+
+
+ -0.141
+ -0.735
+ 0
+
+
+ 0
+ 0
+ -1
+
+
+
+
+
+ fcs/m3_lag
+ 3.822
+
+
+
+ -0.141
+ -0.320
+ 0
+
+
+ 0
+ 0
+ -1
+
+
+
+
+
+ fcs/m4_lag
+ 3.822
+
+
+
+ -0.141
+ 0.320
+ 0
+
+
+ 0
+ 0
+ -1
+
+
+
+
+
+ fcs/m5_lag
+ 3.822
+
+
+
+ -0.141
+ 0.735
+ 0
+
+
+ 0
+ 0
+ -1
+
+
+
+
+
+ fcs/m6_lag
+ 3.822
+
+
+
+ -0.141
+ 1.105
+ 0
+
+
+ 0
+ 0
+ -1
+
+
+
+
+
+ fcs/m7_lag
+ 3.822
+
+
+
+ 0.141
+ 1.105
+ 0
+
+
+ 0
+ 0
+ -1
+
+
+
+
+
+ fcs/m8_lag
+ 3.822
+
+
+
+ 0.141
+ 0.735
+ 0
+
+
+ 0
+ 0
+ -1
+
+
+
+
+
+ fcs/m9_lag
+ 3.822
+
+
+
+ 0.141
+ 0.320
+ 0
+
+
+ 0
+ 0
+ -1
+
+
+
+
+
+ fcs/m10_lag
+ 3.822
+
+
+
+ 0.141
+ -0.320
+ 0
+
+
+ 0
+ 0
+ -1
+
+
+
+
+
+ fcs/m11_lag
+ 3.822
+
+
+
+ 0.141
+ -0.735
+ 0
+
+
+ 0
+ 0
+ -1
+
+
+
+
+
+ fcs/m12_lag
+ 3.822
+
+
+
+ 0.141
+ -1.105
+ 0
+
+
+ 0
+ 0
+ -1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ fcs/m1_lag
+ fcs/m3_lag
+ fcs/m5_lag
+ fcs/m7_lag
+ fcs/m9_lag
+ fcs/m11_lag
+
+
+ fcs/m2_lag
+ fcs/m4_lag
+ fcs/m6_lag
+ fcs/m8_lag
+ fcs/m10_lag
+ fcs/m12_lag
+
+ -1
+
+
+ 1.2
+
+
+
+ 0
+ -1
+ 0
+
+
+ 1
+ 0
+ 0
+
+
+
+
+
+
+ fcs/m1_lag
+ fcs/m3_lag
+ fcs/m5_lag
+ fcs/m7_lag
+ fcs/m9_lag
+ fcs/m11_lag
+
+
+ fcs/m2_lag
+ fcs/m4_lag
+ fcs/m6_lag
+ fcs/m8_lag
+ fcs/m10_lag
+ fcs/m12_lag
+
+ -1
+
+
+ 1.2
+
+
+ 0
+ 1
+ 0
+
+
+ -1
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/conf/telemetry/highspeed_rotorcraft.xml b/conf/telemetry/highspeed_rotorcraft.xml
new file mode 100644
index 0000000000..48028d1e5d
--- /dev/null
+++ b/conf/telemetry/highspeed_rotorcraft.xml
@@ -0,0 +1,230 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/conf/userconf/tudelft/conf.xml b/conf/userconf/tudelft/conf.xml
index 14bd8aac06..9a6235c893 100644
--- a/conf/userconf/tudelft/conf.xml
+++ b/conf/userconf/tudelft/conf.xml
@@ -285,6 +285,17 @@
gui_color="white"
release="997fa535902c4d8f73bc34c02c862652cd47cae5"
/>
+
+ *
+ * This file is part of paparazzi.
+ *
+ * paparazzi 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 2, or (at your option)
+ * any later version.
+ *
+ * paparazzi 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 paparazzi; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/**
+ * @file arch/chibios/modules/uavcan/uavcan.c
+ * Interface from actuators to ChibiOS CAN driver using UAVCan
+ *
+ */
+
+#include "uavcan.h"
+
+#ifndef UAVCAN_NODE_ID
+#define UAVCAN_NODE_ID 100
+#endif
+
+#ifndef UAVCAN_BAUDRATE
+#define UAVCAN_BAUDRATE 1000000
+#endif
+
+static uavcan_event *uavcan_event_hd = NULL;
+
+#if UAVCAN_USE_CAN1
+#ifndef UAVCAN_CAN1_NODE_ID
+#define UAVCAN_CAN1_NODE_ID UAVCAN_NODE_ID
+#endif
+
+#ifndef UAVCAN_CAN1_BAUDRATE
+#define UAVCAN_CAN1_BAUDRATE UAVCAN_BAUDRATE
+#endif
+
+static THD_WORKING_AREA(uavcan1_rx_wa, 1024*2);
+static THD_WORKING_AREA(uavcan1_tx_wa, 1024*2);
+
+struct uavcan_iface_t uavcan1 = {
+ .can_driver = &CAND1,
+ .can_cfg = {
+ CAN_MCR_ABOM | CAN_MCR_AWUM | CAN_MCR_TXFP,
+ CAN_BTR_SJW(0) | CAN_BTR_TS2(1) |
+ CAN_BTR_TS1(14) | CAN_BTR_BRP((STM32_PCLK1/18)/UAVCAN_CAN1_BAUDRATE - 1)
+ },
+ .thread_rx_wa = uavcan1_rx_wa,
+ .thread_rx_wa_size = sizeof(uavcan1_rx_wa),
+ .thread_tx_wa = uavcan1_tx_wa,
+ .thread_tx_wa_size = sizeof(uavcan1_tx_wa),
+ .node_id = UAVCAN_CAN1_NODE_ID,
+ .transfer_id = 0,
+ .initialized = false
+};
+#endif
+
+#if UAVCAN_USE_CAN2
+#ifndef UAVCAN_CAN2_NODE_ID
+#define UAVCAN_CAN2_NODE_ID UAVCAN_NODE_ID
+#endif
+
+#ifndef UAVCAN_CAN2_BAUDRATE
+#define UAVCAN_CAN2_BAUDRATE UAVCAN_BAUDRATE
+#endif
+
+static THD_WORKING_AREA(uavcan2_rx_wa, 1024*2);
+static THD_WORKING_AREA(uavcan2_tx_wa, 1024*2);
+
+struct uavcan_iface_t uavcan2 = {
+ .can_driver = &CAND2,
+ .can_cfg = {
+ CAN_MCR_ABOM | CAN_MCR_AWUM | CAN_MCR_TXFP,
+ CAN_BTR_SJW(0) | CAN_BTR_TS2(1) |
+ CAN_BTR_TS1(14) | CAN_BTR_BRP((STM32_PCLK1/18)/UAVCAN_CAN2_BAUDRATE - 1)
+ },
+ .thread_rx_wa = uavcan2_rx_wa,
+ .thread_rx_wa_size = sizeof(uavcan2_rx_wa),
+ .thread_tx_wa = uavcan2_tx_wa,
+ .thread_tx_wa_size = sizeof(uavcan2_tx_wa),
+ .node_id = UAVCAN_CAN2_NODE_ID,
+ .transfer_id = 0,
+ .initialized = false
+};
+#endif
+
+/*
+ * Receiver thread.
+ */
+static THD_FUNCTION(uavcan_rx, p) {
+ event_listener_t el;
+ CANRxFrame rx_msg;
+ CanardCANFrame rx_frame;
+ struct uavcan_iface_t *iface = (struct uavcan_iface_t *)p;
+
+ chRegSetThreadName("uavcan_rx");
+ chEvtRegister(&iface->can_driver->rxfull_event, &el, EVENT_MASK(0));
+ while (true) {
+ if (chEvtWaitAnyTimeout(ALL_EVENTS, TIME_MS2I(100)) == 0)
+ continue;
+ chMtxLock(&iface->mutex);
+
+ // Wait until a CAN message is received
+ while (canReceive(iface->can_driver, CAN_ANY_MAILBOX, &rx_msg, TIME_IMMEDIATE) == MSG_OK) {
+ // Process message.
+ const uint32_t timestamp = TIME_I2US(chVTGetSystemTimeX());
+ memcpy(rx_frame.data, rx_msg.data8, 8);
+ rx_frame.data_len = rx_msg.DLC;
+ if(rx_msg.IDE) {
+ rx_frame.id = CANARD_CAN_FRAME_EFF | rx_msg.EID;
+ } else {
+ rx_frame.id = rx_msg.SID;
+ }
+
+ // Let canard handle the frame
+ canardHandleRxFrame(&iface->canard, &rx_frame, timestamp);
+ }
+ chMtxUnlock(&iface->mutex);
+ }
+ chEvtUnregister(&iface->can_driver->rxfull_event, &el);
+}
+
+/*
+ * Transmitter thread.
+ */
+static THD_FUNCTION(uavcan_tx, p) {
+ event_listener_t txc, txe, txr;
+ struct uavcan_iface_t *iface = (struct uavcan_iface_t *)p;
+ uint8_t err_cnt = 0;
+
+ chRegSetThreadName("uavcan_tx");
+ chEvtRegister(&iface->can_driver->txempty_event, &txc, EVENT_MASK(0));
+ chEvtRegister(&iface->can_driver->error_event, &txe, EVENT_MASK(1));
+ chEvtRegister(&iface->tx_request, &txr, EVENT_MASK(2));
+
+ while (true) {
+ eventmask_t evts = chEvtWaitAnyTimeout(ALL_EVENTS, TIME_MS2I(100));
+ // Succesfull transmit
+ if (evts == 0)
+ continue;
+
+ // Transmit error
+ if(evts & EVENT_MASK(1))
+ {
+ chEvtGetAndClearFlags(&txe);
+ continue;
+ }
+
+ chMtxLock(&iface->mutex);
+ for (const CanardCANFrame* txf = NULL; (txf = canardPeekTxQueue(&iface->canard)) != NULL;) {
+ CANTxFrame tx_msg;
+ tx_msg.DLC = txf->data_len;
+ memcpy(tx_msg.data8, txf->data, 8);
+ tx_msg.EID = txf->id & CANARD_CAN_EXT_ID_MASK;
+ tx_msg.IDE = CAN_IDE_EXT;
+ tx_msg.RTR = CAN_RTR_DATA;
+ if (canTransmit(iface->can_driver, CAN_ANY_MAILBOX, &tx_msg, TIME_IMMEDIATE) == MSG_OK) {
+ err_cnt = 0;
+ canardPopTxQueue(&iface->canard);
+ } else {
+ // After 5 retries giveup
+ if(err_cnt >= 5) {
+ err_cnt = 0;
+ canardPopTxQueue(&iface->canard);
+ continue;
+ }
+
+ // Timeout - just exit and try again later
+ chMtxUnlock(&iface->mutex);
+ err_cnt++;
+ canardPopTxQueue(&iface->canard); //FIXME (This needs to be here, don't know why)
+ chThdSleepMilliseconds(err_cnt * 5);
+ chMtxLock(&iface->mutex);
+ continue;
+ }
+ }
+ chMtxUnlock(&iface->mutex);
+ }
+}
+
+/**
+ * Whenever a valid and 'accepted' transfer is received
+ */
+static void onTransferReceived(CanardInstance* ins, CanardRxTransfer* transfer) {
+ struct uavcan_iface_t *iface = (struct uavcan_iface_t *)ins->user_reference;
+
+ // Go through all registered callbacks and call function callback if found
+ for(uavcan_event *ev = uavcan_event_hd; ev; ev = ev->next) {
+ if(transfer->data_type_id == ev->data_type_id)
+ ev->cb(iface, transfer);
+ }
+}
+
+/**
+ * If we should accept this transfer
+ */
+static bool shouldAcceptTransfer(const CanardInstance* ins __attribute__((unused)),
+ uint64_t* out_data_type_signature,
+ uint16_t data_type_id,
+ CanardTransferType transfer_type __attribute__((unused)),
+ uint8_t source_node_id __attribute__((unused))) {
+
+ // Go through all registered callbacks and return signature if found
+ for(uavcan_event *ev = uavcan_event_hd; ev; ev = ev->next) {
+ if(data_type_id == ev->data_type_id) {
+ *out_data_type_signature = ev->data_type_signature;
+ return true;
+ }
+ }
+
+ // No callback found return
+ return false;
+}
+
+/**
+ * Initialize uavcan interface
+ */
+static void uavcanInitIface(struct uavcan_iface_t *iface) {
+ // Initialize mutexes/events for multithread locking
+ chMtxObjectInit(&iface->mutex);
+ chEvtObjectInit(&iface->tx_request);
+
+ // Initialize canard
+ canardInit(&iface->canard, iface->canard_memory_pool, sizeof(iface->canard_memory_pool),
+ onTransferReceived, shouldAcceptTransfer, iface);
+ // Update the uavcan node ID
+ canardSetLocalNodeID(&iface->canard, iface->node_id);
+
+ // Start the can interface
+ canStart(iface->can_driver, &iface->can_cfg);
+
+ // Start the receiver and transmitter thread
+ chThdCreateStatic(iface->thread_rx_wa, iface->thread_rx_wa_size, NORMALPRIO + 8, uavcan_rx, (void*)iface);
+ chThdCreateStatic(iface->thread_tx_wa, iface->thread_tx_wa_size, NORMALPRIO + 7, uavcan_tx, (void*)iface);
+ iface->initialized = true;
+}
+
+/**
+ * Initialize all uavcan interfaces
+ */
+void uavcan_init(void)
+{
+#if UAVCAN_USE_CAN1
+ uavcanInitIface(&uavcan1);
+#endif
+#if UAVCAN_USE_CAN2
+ uavcanInitIface(&uavcan2);
+#endif
+}
+
+/**
+ * Bind to a receiving message from uavcan
+ */
+void uavcan_bind(uint16_t data_type_id, uint64_t data_type_signature, uavcan_event *ev, uavcan_callback cb)
+{
+ // Configure the event
+ ev->data_type_id = data_type_id,
+ ev->data_type_signature = data_type_signature,
+ ev->cb = cb,
+ ev->next = uavcan_event_hd;
+
+ // Switch the head
+ uavcan_event_hd = ev;
+}
+
+/**
+ * Broadcast an uavcan message to a specific interface
+ */
+void uavcan_broadcast(struct uavcan_iface_t *iface, uint64_t data_type_signature, uint16_t data_type_id, uint8_t priority, const void* payload,
+ uint16_t payload_len) {
+ if(!iface->initialized) return;
+
+ chMtxLock(&iface->mutex);
+ canardBroadcast(&iface->canard,
+ data_type_signature,
+ data_type_id, &iface->transfer_id,
+ priority, payload, payload_len);
+ chMtxUnlock(&iface->mutex);
+ chEvtBroadcast(&iface->tx_request);
+}
diff --git a/sw/airborne/arch/chibios/modules/uavcan/uavcan.h b/sw/airborne/arch/chibios/modules/uavcan/uavcan.h
new file mode 100644
index 0000000000..aa844b2f10
--- /dev/null
+++ b/sw/airborne/arch/chibios/modules/uavcan/uavcan.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2021 Freek van Tienen
+ *
+ * This file is part of paparazzi.
+ *
+ * paparazzi 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 2, or (at your option)
+ * any later version.
+ *
+ * paparazzi 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 paparazzi; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/**
+ * @file arch/chibios/modules/uavcan/uavcan.h
+ * Interface with uavcan using the Chibios can interfaces.
+ * This uses multithreading and starts a transmit and receive thread per interface.
+ */
+#ifndef MODULES_UAVCAN_ARCH_H
+#define MODULES_UAVCAN_ARCH_H
+
+#include
+#include
+#include
+
+/** uavcan interface structure */
+struct uavcan_iface_t {
+ CANDriver *can_driver;
+ CANConfig can_cfg;
+
+ event_source_t tx_request;
+ mutex_t mutex;
+ void *thread_rx_wa;
+ void *thread_tx_wa;
+ void *thread_uavcan_wa;
+ size_t thread_rx_wa_size;
+ size_t thread_tx_wa_size;
+ size_t thread_uavcan_wa_size;
+
+ uint8_t node_id;
+ CanardInstance canard;
+ uint8_t canard_memory_pool[1024*2];
+
+ uint8_t transfer_id;
+ bool initialized;
+};
+
+/** Generic uavcan callback definition */
+typedef void (*uavcan_callback)(struct uavcan_iface_t *iface, CanardRxTransfer* transfer);
+
+/** Main uavcan event structure for registering/calling callbacks */
+struct uavcan_event_t {
+ uint16_t data_type_id;
+ uint64_t data_type_signature;
+ uavcan_callback cb;
+ struct uavcan_event_t *next;
+};
+typedef struct uavcan_event_t uavcan_event;
+
+/** uavcan interfaces */
+#if UAVCAN_USE_CAN1
+extern struct uavcan_iface_t uavcan1;
+#endif
+#if UAVCAN_USE_CAN2
+extern struct uavcan_iface_t uavcan2;
+#endif
+
+/** uavcan external functions */
+void uavcan_init(void);
+void uavcan_bind(uint16_t data_type_id, uint64_t data_type_signature, uavcan_event *ev, uavcan_callback cb);
+void uavcan_broadcast(struct uavcan_iface_t *iface, uint64_t data_type_signature, uint16_t data_type_id, uint8_t priority,
+ const void* payload, uint16_t payload_len);
+
+#endif /* MODULES_UAVCAN_ARCH_H */
\ No newline at end of file
diff --git a/sw/airborne/arch/sim/modules/uavcan/uavcan.c b/sw/airborne/arch/sim/modules/uavcan/uavcan.c
new file mode 100644
index 0000000000..5462a511d2
--- /dev/null
+++ b/sw/airborne/arch/sim/modules/uavcan/uavcan.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 Freek van Tienen
+ *
+ * This file is part of paparazzi.
+ *
+ * paparazzi 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 2, or (at your option)
+ * any later version.
+ *
+ * paparazzi 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 paparazzi; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/**
+ * @file arch/chibios/subsystems/actuators/actuators_uavcan_arch.c
+ * Interface from actuators to ChibiOS CAN driver using UAVCan
+ *
+ */
+
+#include "uavcan.h"
+
+/** uavcan interfaces */
+#if UAVCAN_USE_CAN1
+struct uavcan_iface_t uavcan1;
+#endif
+#if UAVCAN_USE_CAN2
+struct uavcan_iface_t uavcan2;
+#endif
+
+/** uavcan external functions */
+void uavcan_init(void) {}
+void uavcan_bind(uint16_t data_type_id, uint64_t data_type_signature, uavcan_event *ev, uavcan_callback cb) {}
+void uavcan_broadcast(struct uavcan_iface_t *iface, uint64_t data_type_signature, uint16_t data_type_id, uint8_t priority,
+ const void* payload, uint16_t payload_len) {}
\ No newline at end of file
diff --git a/sw/airborne/arch/sim/modules/uavcan/uavcan.h b/sw/airborne/arch/sim/modules/uavcan/uavcan.h
new file mode 100644
index 0000000000..0d719f3644
--- /dev/null
+++ b/sw/airborne/arch/sim/modules/uavcan/uavcan.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 Freek van Tienen
+ *
+ * This file is part of paparazzi.
+ *
+ * paparazzi 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 2, or (at your option)
+ * any later version.
+ *
+ * paparazzi 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 paparazzi; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/**
+ * @file arch/sim/modules/uavcan/uavcan.h
+ * Stub interface for simulation
+ */
+#ifndef MODULES_UAVCAN_ARCH_H
+#define MODULES_UAVCAN_ARCH_H
+
+#include
+#include
+
+/** uavcan interface structure */
+struct uavcan_iface_t {};
+
+/** Generic uavcan callback definition */
+struct CanardRxTransfer_t {};
+typedef struct CanardRxTransfer_t CanardRxTransfer;
+typedef void (*uavcan_callback)(struct uavcan_iface_t *iface, CanardRxTransfer* transfer);
+
+/** Main uavcan event structure for registering/calling callbacks */
+struct uavcan_event_t {};
+typedef struct uavcan_event_t uavcan_event;
+
+/** uavcan interfaces */
+#if UAVCAN_USE_CAN1
+extern struct uavcan_iface_t uavcan1;
+#endif
+#if UAVCAN_USE_CAN2
+extern struct uavcan_iface_t uavcan2;
+#endif
+
+/** uavcan external functions */
+void uavcan_init(void);
+void uavcan_bind(uint16_t data_type_id, uint64_t data_type_signature, uavcan_event *ev, uavcan_callback cb);
+void uavcan_broadcast(struct uavcan_iface_t *iface, uint64_t data_type_signature, uint16_t data_type_id, uint8_t priority,
+ const void* payload, uint16_t payload_len);
+
+#endif /* MODULES_UAVCAN_ARCH_H */
\ No newline at end of file
diff --git a/sw/airborne/arch/sim/subsystems/actuators/actuators_uavcan_arch.c b/sw/airborne/arch/sim/subsystems/actuators/actuators_uavcan_arch.c
new file mode 100644
index 0000000000..286d3faa15
--- /dev/null
+++ b/sw/airborne/arch/sim/subsystems/actuators/actuators_uavcan_arch.c
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 Freek van Tienen
+ *
+ * This file is part of Paparazzi.
+ *
+ * Paparazzi 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 2, or (at your option)
+ * any later version.
+ *
+ * Paparazzi 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 Paparazzi; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/** @file arch/sim/subsystems/actuators/actuators_uavcan_arch.c
+ * dummy servos handling for sim
+ */
+
+#include "subsystems/actuators/actuators_uavcan_arch.h"
+
+void actuators_uavcan_arch_init(void)
+{
+
+}
\ No newline at end of file
diff --git a/sw/airborne/arch/sim/subsystems/actuators/actuators_uavcan_arch.h b/sw/airborne/arch/sim/subsystems/actuators/actuators_uavcan_arch.h
new file mode 100644
index 0000000000..dd1018aac4
--- /dev/null
+++ b/sw/airborne/arch/sim/subsystems/actuators/actuators_uavcan_arch.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 Freek van Tienen
+ *
+ * This file is part of Paparazzi.
+ *
+ * Paparazzi 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 2, or (at your option)
+ * any later version.
+ *
+ * Paparazzi 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 Paparazzi; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/** @file arch/sim/subsystems/actuators/actuators_uavcan_arch.h
+ * dummy servos handling for sim
+ */
+
+#ifndef ACTUATORS_UAVCAN_ARCH_H
+#define ACTUATORS_UAVCAN_ARCH_H
+
+#define SERVOS_TICS_OF_USEC(_v) (_v)
+
+#define ActuatorUavcanSet(_i, _v) {}
+#define ActuatorsUavcanCommit() {}
+
+extern void actuators_uavcan_arch_init(void);
+
+#endif /* ACTUATORS_UAVCAN_ARCH_H */
diff --git a/sw/airborne/boards/px4fmu/chibios/v5.0/mcuconf.h b/sw/airborne/boards/px4fmu/chibios/v5.0/mcuconf.h
index 0b89117860..e84e3b68f5 100644
--- a/sw/airborne/boards/px4fmu/chibios/v5.0/mcuconf.h
+++ b/sw/airborne/boards/px4fmu/chibios/v5.0/mcuconf.h
@@ -238,7 +238,7 @@
#endif
#define STM32_I2C_BUSY_TIMEOUT 50
#define STM32_I2C_I2C1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 0)
-#define STM32_I2C_I2C1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6)
+#define STM32_I2C_I2C1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7)
#define STM32_I2C_I2C2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
#define STM32_I2C_I2C2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7)
#define STM32_I2C_I2C3_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
diff --git a/sw/airborne/math/pprz_algebra_int.h b/sw/airborne/math/pprz_algebra_int.h
index c752a88b01..9dcba98470 100644
--- a/sw/airborne/math/pprz_algebra_int.h
+++ b/sw/airborne/math/pprz_algebra_int.h
@@ -612,6 +612,13 @@ static inline void int32_vect_zero(int32_t *a, const int n)
for (i = 0; i < n; i++) { a[i] = 0.; }
}
+/** a = 0 */
+static inline void int16_vect_zero(int16_t *a, const int n)
+{
+ int i;
+ for (i = 0; i < n; i++) { a[i] = 0.; }
+}
+
/** a = v * ones(n,1) */
static inline void int32_vect_set_value(int32_t *a, const int32_t v, const int n)
{
diff --git a/sw/airborne/subsystems/actuators/actuators_uavcan.c b/sw/airborne/subsystems/actuators/actuators_uavcan.c
new file mode 100644
index 0000000000..079a60678a
--- /dev/null
+++ b/sw/airborne/subsystems/actuators/actuators_uavcan.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2021 Freek van Tienen
+ *
+ * This file is part of paparazzi.
+ *
+ * paparazzi 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 2, or (at your option)
+ * any later version.
+ *
+ * paparazzi 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 paparazzi; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/**
+ * @file subsystems/actuators/actuators_uavcan.c
+ * UAVCan actuators using RAWCOMMAND message and ESC_STATUS telemetry
+ *
+ */
+
+#include "actuators_uavcan.h"
+#include "subsystems/electrical.h"
+
+/* uavcan ESC status telemetry structure */
+struct actuators_uavcan_telem_t {
+ float voltage;
+ float current;
+ float temperature;
+ int32_t rpm;
+ uint32_t energy;
+};
+
+#ifdef SERVOS_UAVCAN1_NB
+int16_t actuators_uavcan1_values[SERVOS_UAVCAN1_NB];
+static struct actuators_uavcan_telem_t uavcan1_telem[SERVOS_UAVCAN1_NB];
+#endif
+#ifdef SERVOS_UAVCAN2_NB
+int16_t actuators_uavcan2_values[SERVOS_UAVCAN2_NB];
+static struct actuators_uavcan_telem_t uavcan2_telem[SERVOS_UAVCAN2_NB];
+#endif
+
+/* uavcan EQUIPMENT_ESC_STATUS message definition */
+#define UAVCAN_EQUIPMENT_ESC_STATUS_ID 1034
+#define UAVCAN_EQUIPMENT_ESC_STATUS_SIGNATURE (0xA9AF28AEA2FBB254ULL)
+#define UAVCAN_EQUIPMENT_ESC_STATUS_MAX_SIZE ((110 + 7)/8)
+
+/* uavcan EQUIPMENT_ESC_RAWCOMMAND message definition */
+#define UAVCAN_EQUIPMENT_ESC_RAWCOMMAND_ID 1030
+#define UAVCAN_EQUIPMENT_ESC_RAWCOMMAND_SIGNATURE (0x217F5C87D7EC951DULL)
+#define UAVCAN_EQUIPMENT_ESC_RAWCOMMAND_MAX_SIZE ((285 + 7)/8)
+
+static bool actuators_uavcan_initialized = false;
+static uavcan_event esc_status_ev;
+
+
+#if PERIODIC_TELEMETRY
+#include "subsystems/datalink/telemetry.h"
+
+static void actuators_uavcan_send_esc(struct transport_tx *trans, struct link_device *dev)
+{
+ /*static uint8_t esc_idx = 0;
+ float power = uavcan_telem[esc_idx].current * uavcan_telem[esc_idx].voltage;
+ float rpm = uavcan_telem[esc_idx].rpm;
+ float energy = uavcan_telem[esc_idx].energy;
+ pprz_msg_send_ESC(trans, dev, AC_ID, &uavcan_telem[esc_idx].current, &electrical.vsupply, &power,
+ &rpm, &uavcan_telem[esc_idx].voltage, &energy, &esc_idx);
+ esc_idx++;
+
+ if(esc_idx >= ACTUATORS_UAVCAN_NB)
+ esc_idx = 0;*/
+}
+#endif
+
+/**
+ * Whevener an ESC_STATUS message from the EQUIPMENT group is received
+ */
+static void actuators_uavcan_esc_status_cb(struct uavcan_iface_t *iface, CanardRxTransfer* transfer) {
+ /*uint8_t esc_idx;
+ uint16_t tmp_float;
+
+ canardDecodeScalar(transfer, 105, 5, false, (void*)&esc_idx);
+ if(iface == &can2_iface)
+ esc_idx += 10;
+ if(esc_idx >= ACTUATORS_UAVCAN_NB)
+ break;
+
+ canardDecodeScalar(transfer, 0, 32, false, (void*)&uavcan_telem[esc_idx].energy);
+ canardDecodeScalar(transfer, 32, 16, true, (void*)&tmp_float);
+ uavcan_telem[esc_idx].voltage = canardConvertFloat16ToNativeFloat(tmp_float);
+ canardDecodeScalar(transfer, 48, 16, true, (void*)&tmp_float);
+ uavcan_telem[esc_idx].current = canardConvertFloat16ToNativeFloat(tmp_float);
+ canardDecodeScalar(transfer, 64, 16, true, (void*)&tmp_float);
+ uavcan_telem[esc_idx].temperature = canardConvertFloat16ToNativeFloat(tmp_float);
+ canardDecodeScalar(transfer, 80, 18, true, (void*)&uavcan_telem[esc_idx].rpm);
+
+ // Update total current
+ electrical.current = 0;
+ for(uint8_t i = 0; i < ACTUATORS_UAVCAN_NB; ++i)
+ electrical.current += uavcan_telem[i].current;*/
+}
+
+/**
+ * Initialize an uavcan interface
+ */
+void actuators_uavcan_init(struct uavcan_iface_t* iface __attribute__((unused)))
+{
+ // Check if not already initialized (for multiple interfaces, needs only 1)
+ if(actuators_uavcan_initialized) return;
+
+ // Bind uavcan ESC_STATUS message from EQUIPMENT
+ uavcan_bind(UAVCAN_EQUIPMENT_ESC_STATUS_ID, UAVCAN_EQUIPMENT_ESC_STATUS_SIGNATURE, &esc_status_ev, &actuators_uavcan_esc_status_cb);
+
+ // Configure telemetry
+#if PERIODIC_TELEMETRY
+ register_periodic_telemetry(DefaultPeriodic, PPRZ_MSG_ID_ESC, actuators_uavcan_send_esc);
+#endif
+
+ // Set initialization
+ actuators_uavcan_initialized = true;
+}
+
+/**
+ * Commit actuator values to the uavcan interface
+ */
+void actuators_uavcan_commit(struct uavcan_iface_t* iface, int16_t *values, uint8_t nb)
+{
+ uint8_t buffer[UAVCAN_EQUIPMENT_ESC_RAWCOMMAND_MAX_SIZE];
+ uint32_t offset = 0;
+
+ // Encode the values as 14-bit signed integers
+ for(uint8_t i = 0; i < nb; i++) {
+ canardEncodeScalar(buffer, offset, 14, (void *)&values[i]);
+ offset += 14;
+ }
+
+ // Broadcast the raw command message on the interface
+ uavcan_broadcast(iface, UAVCAN_EQUIPMENT_ESC_RAWCOMMAND_SIGNATURE, UAVCAN_EQUIPMENT_ESC_RAWCOMMAND_ID,
+ CANARD_TRANSFER_PRIORITY_HIGH, buffer, (offset+7)/8);
+}
diff --git a/sw/airborne/subsystems/actuators/actuators_uavcan.h b/sw/airborne/subsystems/actuators/actuators_uavcan.h
new file mode 100644
index 0000000000..91ae8576be
--- /dev/null
+++ b/sw/airborne/subsystems/actuators/actuators_uavcan.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 Freek van Tienen
+ *
+ * This file is part of Paparazzi.
+ *
+ * Paparazzi 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 2, or (at your option)
+ * any later version.
+ *
+ * Paparazzi 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 Paparazzi; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ACTUATORS_UAVCAN_H
+#define ACTUATORS_UAVCAN_H
+
+#include "modules/uavcan/uavcan.h"
+#include BOARD_CONFIG
+
+/* External functions */
+extern void actuators_uavcan_init(struct uavcan_iface_t* iface);
+extern void actuators_uavcan_commit(struct uavcan_iface_t* iface, int16_t *values, uint8_t nb);
+
+#endif /* ACTUATORS_UAVCAN_H */
\ No newline at end of file
diff --git a/sw/airborne/subsystems/actuators/actuators_uavcan1.h b/sw/airborne/subsystems/actuators/actuators_uavcan1.h
new file mode 100644
index 0000000000..f087d07944
--- /dev/null
+++ b/sw/airborne/subsystems/actuators/actuators_uavcan1.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2021 Freek van Tienen
+ *
+ * This file is part of Paparazzi.
+ *
+ * Paparazzi 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 2, or (at your option)
+ * any later version.
+ *
+ * Paparazzi 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 Paparazzi; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ACTUATORS_UAVCAN1_H
+#define ACTUATORS_UAVCAN1_H
+
+#include "actuators_uavcan.h"
+
+/** Stub file needed per uavcan interface because of generator */
+extern int16_t actuators_uavcan1_values[SERVOS_UAVCAN1_NB];
+
+#define ActuatorsUavcan1Init() actuators_uavcan_init(&uavcan1)
+#define ActuatorUavcan1Set(_i, _v) { actuators_uavcan1_values[_i] = _v; }
+#define ActuatorsUavcan1Commit() actuators_uavcan_commit(&uavcan1, actuators_uavcan1_values, SERVOS_UAVCAN1_NB)
+
+#endif /* ACTUATORS_UAVCAN1_H */
\ No newline at end of file
diff --git a/sw/airborne/subsystems/actuators/actuators_uavcan2.h b/sw/airborne/subsystems/actuators/actuators_uavcan2.h
new file mode 100644
index 0000000000..301d3bf0fb
--- /dev/null
+++ b/sw/airborne/subsystems/actuators/actuators_uavcan2.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2021 Freek van Tienen
+ *
+ * This file is part of Paparazzi.
+ *
+ * Paparazzi 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 2, or (at your option)
+ * any later version.
+ *
+ * Paparazzi 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 Paparazzi; see the file COPYING. If not, write to
+ * the Free Software Foundation, 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ACTUATORS_UAVCAN2_H
+#define ACTUATORS_UAVCAN2_H
+
+#include "actuators_uavcan.h"
+
+/** Stub file needed per interface because of generator */
+extern int16_t actuators_uavcan2_values[SERVOS_UAVCAN2_NB];
+
+#define ActuatorsUavcan2Init() actuators_uavcan_init(&uavcan2)
+#define ActuatorUavcan2Set(_i, _v) { actuators_uavcan2_values[_i] = _v; }
+#define ActuatorsUavcan2Commit() actuators_uavcan_commit(&uavcan2, actuators_uavcan2_values, SERVOS_UAVCAN2_NB)
+
+#endif /* ACTUATORS_UAVCAN2_H */
\ No newline at end of file
diff --git a/sw/ext/libcanard b/sw/ext/libcanard
new file mode 160000
index 0000000000..4244b8c0b3
--- /dev/null
+++ b/sw/ext/libcanard
@@ -0,0 +1 @@
+Subproject commit 4244b8c0b303ac02a20fd603844179809bee59b3