diff --git a/conf/modules/uavcan.xml b/conf/modules/uavcan.xml index de19b90b50..9c3b794f8b 100644 --- a/conf/modules/uavcan.xml +++ b/conf/modules/uavcan.xml @@ -35,9 +35,11 @@ + + diff --git a/sw/airborne/arch/chibios/modules/uavcan/uavcan.c b/sw/airborne/arch/chibios/modules/uavcan/uavcan.c index 0f6758fdca..2d48e2bc2b 100644 --- a/sw/airborne/arch/chibios/modules/uavcan/uavcan.c +++ b/sw/airborne/arch/chibios/modules/uavcan/uavcan.c @@ -27,6 +27,7 @@ #include "uavcan.h" #include "mcu_periph/can.h" #include "modules/core/threads.h" +#include "modules/uavcan/uavcan_allocator.h" #ifndef UAVCAN_NODE_ID #define UAVCAN_NODE_ID 100 @@ -174,7 +175,7 @@ static void uavcan_tx(void* p) 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) { @@ -200,7 +201,12 @@ static bool shouldAcceptTransfer(const CanardInstance *ins __attribute__((unused return true; } } - // No callback found return + + if(source_node_id != 0 && !uavcan_get_node_id_mapping(source_node_id)) { + struct uavcan_iface_t *iface = (struct uavcan_iface_t *)ins->user_reference; + request_node_info(iface); + } + return false; } @@ -242,6 +248,8 @@ void uavcan_init(void) #if UAVCAN_USE_CAN2 uavcanInitIface(&uavcan2); #endif + + uavcan_allocator_init(); } /** diff --git a/sw/airborne/modules/uavcan/uavcan_allocator.c b/sw/airborne/modules/uavcan/uavcan_allocator.c new file mode 100644 index 0000000000..f5ec093b6e --- /dev/null +++ b/sw/airborne/modules/uavcan/uavcan_allocator.c @@ -0,0 +1,269 @@ + +#include "uavcan/uavcan.h" +#include "uavcan/uavcan_allocator.h" +#include "uavcan.protocol.dynamic_node_id.Allocation.h" +#include "uavcan.protocol.GetNodeInfo.h" +#include "uavcan.equipment.air_data.AngleOfAttack.h" +#include "mcu_periph/gpio.h" + + +/** + * Dynamic node ID allocation. + * See https://dronecan.github.io/Specification/6._Application_level_functions/#dynamic-node-id-allocation + */ + + +#ifndef UAVCAN_DYN_NODE_NB +#define UAVCAN_DYN_NODE_NB 10 +#endif + +#define INVALID_STAGE -1 + +static int detectRequestStage(struct uavcan_protocol_dynamic_node_id_Allocation* msg); +static int getExpectedStage(void); +static int findFreeNodeID(const uint8_t preferred); +static bool unique_id_identical(int index); +static void handleAllocationRequest(struct uavcan_iface_t *iface, uint8_t preferred_node_id); +static void id_alloc_uavcan_cb(struct uavcan_iface_t *iface __attribute__((unused)), CanardRxTransfer *transfer); + + +static uavcan_event id_alloc_ev; +static uavcan_event node_info_ev; + + +// keep the correspondance between node id and unique IDs. (even or the fixed ids) +static struct uavcan_node_mapping_t uavcan_node_ids[UAVCAN_DYN_NODE_NB] = {0}; + + + +struct uavcan_unique_id_t current_unique_id = {0}; +uint32_t last_message_timestamp = 0; + + +struct uavcan_node_mapping_t* uavcan_get_node_id_mapping(const uint8_t id) { + for(int i=0; i 0) ? preferred : 125; + while (candidate <= 125) { + if (!uavcan_get_node_id_mapping(candidate)) { + return candidate; + } + candidate++; + } + // Search down + candidate = (preferred > 0) ? preferred : 125; + while (candidate > 0) { + if (!uavcan_get_node_id_mapping(candidate)) { + return candidate; + } + candidate--; + } + // Not found + return -1; +} + + +static int detectRequestStage(struct uavcan_protocol_dynamic_node_id_Allocation* msg) { + if(msg->first_part_of_unique_id) { + return 1; + } + if(msg->unique_id.len == UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_ALLOCATION_MAX_LENGTH_OF_UNIQUE_ID_IN_REQUEST) { + return 2; + } + if(msg->unique_id.len < UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_ALLOCATION_MAX_LENGTH_OF_UNIQUE_ID_IN_REQUEST) { + return 3; + } + return INVALID_STAGE; //invalid +} + +static int getExpectedStage() { + if(current_unique_id.len == 0) { + return 1; + } + if(current_unique_id.len >= UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_ALLOCATION_MAX_LENGTH_OF_UNIQUE_ID_IN_REQUEST*2) { + return 3; + } + if(current_unique_id.len >= UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_ALLOCATION_MAX_LENGTH_OF_UNIQUE_ID_IN_REQUEST) { + return 2; + } + return INVALID_STAGE; //invalid +} + +static bool unique_id_identical(int index) { + return current_unique_id.len == uavcan_node_ids[index].unique_id.len && + memcmp(current_unique_id.data, uavcan_node_ids[index].unique_id.data, current_unique_id.len) == 0; +} + +static void handleAllocationRequest(struct uavcan_iface_t *iface, uint8_t preferred_node_id) { + uint8_t allocated_id = 0; + + for(int i=0; isource_node_id != 0) { + return; + } + + uint32_t timestamp = transfer->timestamp_usec / 1000; + + // Reset the expected stage on timeout + if(timestamp > last_message_timestamp + UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_ALLOCATION_FOLLOWUP_TIMEOUT_MS) { + current_unique_id.len = 0; + } + + // Checking if request stage matches the expected stage + int request_stage = detectRequestStage(&msg); + if(request_stage == INVALID_STAGE) { return; } + if(request_stage != getExpectedStage()) { return; } + if (msg.unique_id.len > (unique_id_capacity - current_unique_id.len)) { return; } + + // Updating the local state + for(int i=0; isource_node_id)) { + struct uavcan_node_mapping_t* mapping = get_free_id_mapping(); + mapping->allocated_id = transfer->source_node_id; + for(int i=0; i<16; i++) { + mapping->unique_id.data[i] = msg.hardware_version.unique_id[i]; + } + mapping->unique_id.len = 16; + } + +} + +void request_node_info(struct uavcan_iface_t *iface) { + struct uavcan_protocol_GetNodeInfoRequest msg; + uint8_t msg_buffer[10]; + uint32_t size = uavcan_protocol_GetNodeInfoRequest_encode(&msg, msg_buffer); + uavcan_broadcast( + iface, + UAVCAN_PROTOCOL_GETNODEINFO_REQUEST_SIGNATURE, UAVCAN_PROTOCOL_GETNODEINFO_REQUEST_ID, + CANARD_TRANSFER_PRIORITY_HIGH, msg_buffer, size); +} + + +void uavcan_allocator_init(void) { + uavcan_bind(UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_ALLOCATION_ID, UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_ALLOCATION_SIGNATURE, + &id_alloc_ev, &id_alloc_uavcan_cb); + + uavcan_bind(UAVCAN_PROTOCOL_GETNODEINFO_RESPONSE_ID, UAVCAN_PROTOCOL_GETNODEINFO_RESPONSE_SIGNATURE, + &node_info_ev, &node_info_resp_cb); +} + + + +void uavcan_allocator_periodic(void) { + +} \ No newline at end of file diff --git a/sw/airborne/modules/uavcan/uavcan_allocator.h b/sw/airborne/modules/uavcan/uavcan_allocator.h new file mode 100644 index 0000000000..0de7671092 --- /dev/null +++ b/sw/airborne/modules/uavcan/uavcan_allocator.h @@ -0,0 +1,24 @@ +#pragma once +#include "inttypes.h" +/** + * Dynamic node ID allocation. + * See https://dronecan.github.io/Specification/6._Application_level_functions/#dynamic-node-id-allocation + */ + +struct uavcan_iface_t; + +struct uavcan_unique_id_t { + uint8_t len; + uint8_t data[16]; +}; + +struct uavcan_node_mapping_t { + struct uavcan_unique_id_t unique_id; + uint8_t allocated_id; +}; + +struct uavcan_node_mapping_t* uavcan_get_node_id_mapping(const uint8_t id); +void request_node_info(struct uavcan_iface_t *iface); + +void uavcan_allocator_init(void); +void uavcan_allocator_periodic(void);