Uavcan actuators restyle and telemetry (#2698)

* [uavcan] Fix performance and restyle code

* [uavcan] Add ESC telemetry
This commit is contained in:
Freek van Tienen
2021-04-20 16:33:31 +02:00
committed by GitHub
parent 627ecce31b
commit 304aef2025
7 changed files with 158 additions and 93 deletions
+1
View File
@@ -5,6 +5,7 @@
<description>
UAVCan actuators
</description>
<define name="UAVCAN_ACTUATORS_USE_CURRENT" value="TRUE" description="Enable the usage of the current sensing in the ESC telemetry"/>
</doc>
<dep>
<depends>uavcan</depends>
+4 -4
View File
@@ -5,10 +5,10 @@
<description>
UAVCan interface for transmitting/receiving uavcan messages on CAN busses
</description>
<configure name="UAVCAN_USE_CAN1" value="FALSE" description="Enable UAVCAN on CAN1 interface"/>
<configure name="UAVCAN_USE_CAN2" value="FALSE" description="Enable UAVCAN on CAN2 interface"/>
<define name="UAVCAN_NODE_ID" value="100" description="The UAVCAN node ID of the AP"/>
<define name="UAVCAN_BAUDRATE" value="1000000" description="The baudrate of the CAN interface"/>
<configure name="UAVCAN_USE_CAN1" value="FALSE" description="Enable UAVCAN on CAN1 interface"/>
<configure name="UAVCAN_USE_CAN2" value="FALSE" description="Enable UAVCAN on CAN2 interface"/>
<define name="UAVCAN_NODE_ID" value="100" description="The UAVCAN node ID of the AP"/>
<define name="UAVCAN_BAUDRATE" value="1000000" description="The baudrate of the CAN interface"/>
</doc>
<header>
<file name="uavcan.h" dir="modules/uavcan"/>
+1 -1
View File
@@ -293,7 +293,7 @@
telemetry="telemetry/highspeed_rotorcraft.xml"
flight_plan="flight_plans/rotorcraft_basic.xml"
settings="settings/rotorcraft_basic.xml"
settings_modules="modules/guidance_indi_hybrid.xml modules/air_data.xml modules/ins_ekf2.xml modules/stabilization_indi_simple.xml modules/gps_ubx_ucenter.xml modules/gps.xml modules/imu_common.xml"
settings_modules="modules/air_data.xml modules/gps.xml modules/gps_ubx_ucenter.xml modules/guidance_indi_hybrid.xml modules/guidance_rotorcraft.xml modules/imu_common.xml modules/ins_ekf2.xml modules/nav_basic_rotorcraft.xml modules/stabilization_indi_simple.xml"
gui_color="blue"
/>
<aircraft
@@ -21,7 +21,7 @@
/**
* @file arch/chibios/modules/uavcan/uavcan.c
* Interface from actuators to ChibiOS CAN driver using UAVCan
*
*
*/
#include "uavcan.h"
@@ -45,15 +45,15 @@ static uavcan_event *uavcan_event_hd = NULL;
#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);
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)
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),
@@ -74,15 +74,15 @@ struct uavcan_iface_t uavcan1 = {
#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);
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)
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),
@@ -97,7 +97,8 @@ struct uavcan_iface_t uavcan2 = {
/*
* Receiver thread.
*/
static THD_FUNCTION(uavcan_rx, p) {
static THD_FUNCTION(uavcan_rx, p)
{
event_listener_t el;
CANRxFrame rx_msg;
CanardCANFrame rx_frame;
@@ -106,8 +107,9 @@ static THD_FUNCTION(uavcan_rx, p) {
chRegSetThreadName("uavcan_rx");
chEvtRegister(&iface->can_driver->rxfull_event, &el, EVENT_MASK(0));
while (true) {
if (chEvtWaitAnyTimeout(ALL_EVENTS, TIME_MS2I(100)) == 0)
if (chEvtWaitAnyTimeout(ALL_EVENTS, TIME_MS2I(100)) == 0) {
continue;
}
chMtxLock(&iface->mutex);
// Wait until a CAN message is received
@@ -116,12 +118,11 @@ static THD_FUNCTION(uavcan_rx, p) {
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) {
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);
}
@@ -133,7 +134,8 @@ static THD_FUNCTION(uavcan_rx, p) {
/*
* Transmitter thread.
*/
static THD_FUNCTION(uavcan_tx, p) {
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;
@@ -146,40 +148,38 @@ static THD_FUNCTION(uavcan_tx, p) {
while (true) {
eventmask_t evts = chEvtWaitAnyTimeout(ALL_EVENTS, TIME_MS2I(100));
// Succesfull transmit
if (evts == 0)
if (evts == 0) {
continue;
}
// Transmit error
if(evts & EVENT_MASK(1))
{
if (evts & EVENT_MASK(1)) {
chEvtGetAndClearFlags(&txe);
continue;
}
chMtxLock(&iface->mutex);
for (const CanardCANFrame* txf = NULL; (txf = canardPeekTxQueue(&iface->canard)) != NULL;) {
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) {
if (!canTryTransmitI(iface->can_driver, CAN_ANY_MAILBOX, &tx_msg)) {
err_cnt = 0;
canardPopTxQueue(&iface->canard);
} else {
// After 5 retries giveup
if(err_cnt >= 5) {
// After 5 retries giveup and clean full queue
if (err_cnt >= 5) {
err_cnt = 0;
canardPopTxQueue(&iface->canard);
while (canardPeekTxQueue(&iface->canard)) { 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);
chThdSleepMilliseconds(++err_cnt);
chMtxLock(&iface->mutex);
continue;
}
@@ -191,33 +191,35 @@ static THD_FUNCTION(uavcan_tx, p) {
/**
* Whenever a valid and 'accepted' transfer is received
*/
static void onTransferReceived(CanardInstance* ins, CanardRxTransfer* transfer) {
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)
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,
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))) {
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) {
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;
}
@@ -225,14 +227,15 @@ static bool shouldAcceptTransfer(const CanardInstance* ins __attribute__((unused
/**
* Initialize uavcan interface
*/
static void uavcanInitIface(struct uavcan_iface_t *iface) {
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);
onTransferReceived, shouldAcceptTransfer, iface);
// Update the uavcan node ID
canardSetLocalNodeID(&iface->canard, iface->node_id);
@@ -240,8 +243,8 @@ static void uavcanInitIface(struct uavcan_iface_t *iface) {
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);
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;
}
@@ -253,7 +256,7 @@ void uavcan_init(void)
#if UAVCAN_USE_CAN1
uavcanInitIface(&uavcan1);
#endif
#if UAVCAN_USE_CAN2
#if UAVCAN_USE_CAN2
uavcanInitIface(&uavcan2);
#endif
}
@@ -264,9 +267,9 @@ void uavcan_init(void)
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->data_type_id = data_type_id;
ev->data_type_signature = data_type_signature;
ev->cb = cb;
ev->next = uavcan_event_hd;
// Switch the head
@@ -276,15 +279,17 @@ void uavcan_bind(uint16_t data_type_id, uint64_t data_type_signature, uavcan_eve
/**
* 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;
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);
data_type_signature,
data_type_id, &iface->transfer_id,
priority, payload, payload_len);
chMtxUnlock(&iface->mutex);
chEvtBroadcast(&iface->tx_request);
}
@@ -46,16 +46,16 @@ struct uavcan_iface_t {
uint8_t node_id;
CanardInstance canard;
uint8_t canard_memory_pool[1024*2];
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);
typedef void (*uavcan_callback)(struct uavcan_iface_t *iface, CanardRxTransfer *transfer);
/** Main uavcan event structure for registering/calling callbacks */
/** Main uavcan event structure for registering/calling callbacks */
struct uavcan_event_t {
uint16_t data_type_id;
uint64_t data_type_signature;
@@ -75,7 +75,7 @@ extern struct uavcan_iface_t uavcan2;
/** 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);
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 */
@@ -21,12 +21,17 @@
/**
* @file subsystems/actuators/actuators_uavcan.c
* UAVCan actuators using RAWCOMMAND message and ESC_STATUS telemetry
*
*
*/
#include "actuators_uavcan.h"
#include "subsystems/electrical.h"
/* By default enable the usage of the current sensing in the ESC telemetry */
#ifndef UAVCAN_ACTUATORS_USE_CURRENT
#define UAVCAN_ACTUATORS_USE_CURRENT TRUE
#endif
/* uavcan ESC status telemetry structure */
struct actuators_uavcan_telem_t {
float voltage;
@@ -55,66 +60,120 @@ static struct actuators_uavcan_telem_t uavcan2_telem[SERVOS_UAVCAN2_NB];
#define UAVCAN_EQUIPMENT_ESC_RAWCOMMAND_SIGNATURE (0x217F5C87D7EC951DULL)
#define UAVCAN_EQUIPMENT_ESC_RAWCOMMAND_MAX_SIZE ((285 + 7)/8)
/* private variables */
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);
static uint8_t esc_idx = 0;
// Find the correct telemetry
uint8_t max_id = 0;
uint8_t offset = 0;
struct actuators_uavcan_telem_t *telem = NULL;
#ifdef SERVOS_UAVCAN1_NB
if (esc_idx >= max_id && esc_idx < max_id + SERVOS_UAVCAN1_NB) {
offset = max_id;
telem = uavcan1_telem;
}
max_id += SERVOS_UAVCAN1_NB;
#endif
#ifdef SERVOS_UAVCAN2_NB
if (esc_idx >= max_id && esc_idx < max_id + SERVOS_UAVCAN2_NB) {
offset = max_id;
telem = uavcan2_telem;
}
max_id += SERVOS_UAVCAN2_NB;
#endif
// Safety check
if (telem == NULL) {
esc_idx = 0;
return;
}
uint8_t i = esc_idx - offset;
float power = telem[i].current * telem[i].voltage;
float rpm = telem[i].rpm;
float energy = telem[i].energy;
pprz_msg_send_ESC(trans, dev, AC_ID, &telem[i].current, &electrical.vsupply, &power,
&rpm, &telem[i].voltage, &energy, &esc_idx);
esc_idx++;
if(esc_idx >= ACTUATORS_UAVCAN_NB)
esc_idx = 0;*/
if (esc_idx >= max_id) {
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;
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);
struct actuators_uavcan_telem_t *telem = NULL;
uint8_t max_id = 0;
#ifdef SERVOS_UAVCAN1_NB
if (iface == &uavcan1) {
telem = uavcan1_telem;
max_id = SERVOS_UAVCAN1_NB;
}
#endif
#ifdef SERVOS_UAVCAN2_NB
if (iface == &uavcan2) {
telem = uavcan2_telem;
max_id = SERVOS_UAVCAN2_NB;
}
#endif
canardDecodeScalar(transfer, 105, 5, false, (void *)&esc_idx);
//Could not find the right interface
if (esc_idx > max_id || telem == NULL || max_id == 0) {
return;
}
canardDecodeScalar(transfer, 0, 32, false, (void *)&telem[esc_idx].energy);
canardDecodeScalar(transfer, 32, 16, true, (void *)&tmp_float);
telem[esc_idx].voltage = canardConvertFloat16ToNativeFloat(tmp_float);
canardDecodeScalar(transfer, 48, 16, true, (void *)&tmp_float);
telem[esc_idx].current = canardConvertFloat16ToNativeFloat(tmp_float);
canardDecodeScalar(transfer, 64, 16, true, (void *)&tmp_float);
telem[esc_idx].temperature = canardConvertFloat16ToNativeFloat(tmp_float);
canardDecodeScalar(transfer, 80, 18, true, (void *)&telem[esc_idx].rpm);
#ifdef UAVCAN_ACTUATORS_USE_CURRENT
// Update total current
electrical.current = 0;
for(uint8_t i = 0; i < ACTUATORS_UAVCAN_NB; ++i)
electrical.current += uavcan_telem[i].current;*/
#ifdef SERVOS_UAVCAN1_NB
for (uint8_t i = 0; i < SERVOS_UAVCAN1_NB; ++i) {
electrical.current += uavcan1_telem[i].current;
}
#endif
#ifdef SERVOS_UAVCAN2_NB
for (uint8_t i = 0; i < SERVOS_UAVCAN2_NB; ++i) {
electrical.current += uavcan2_telem[i].current;
}
#endif
#endif
}
/**
* Initialize an uavcan interface
*/
void actuators_uavcan_init(struct uavcan_iface_t* iface __attribute__((unused)))
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;
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);
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
@@ -128,18 +187,18 @@ void actuators_uavcan_init(struct uavcan_iface_t* iface __attribute__((unused)))
/**
* Commit actuator values to the uavcan interface
*/
void actuators_uavcan_commit(struct uavcan_iface_t* iface, int16_t *values, uint8_t nb)
{
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++) {
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);
CANARD_TRANSFER_PRIORITY_HIGH, buffer, (offset + 7) / 8);
}
@@ -26,7 +26,7 @@
#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);
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 */