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);