Skip to content

Commit 5fc0195

Browse files
committed
Add can classic build option
1 parent 961171b commit 5fc0195

File tree

3 files changed

+114
-6
lines changed

3 files changed

+114
-6
lines changed

differential_pressure_sensor/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ add_definitions(
2929
-DNODE_NAME="org.opencyphal.demos.differential_pressure"
3030
)
3131

32+
option(CAN_CLASSIC "Build for can classic" ON) # Enabled by default
33+
34+
if(CAN_CLASSIC)
35+
add_definitions(-DCAN_CLASSIC)
36+
endif(CAN_CLASSIC)
37+
38+
3239
# Transpile DSDL into C using Nunavut. Install Nunavut as follows: pip install nunavut.
3340
# Alternatively, you can invoke the transpiler manually or use https://nunaweb.opencyphal.org.
3441
find_package(nnvg REQUIRED)

differential_pressure_sensor/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ This demo supports only Cyphal/CAN at the moment, but it can be extended to supp
1414
You will need GNU/Linux, CMake, a C11 compiler, [Yakut](https://github.com/OpenCyphal/yakut),
1515
and [SocketCAN utils](https://github.com/linux-can/can-utils).
1616

17-
Build the demo as follows:
17+
Build the demo as follows
18+
(the default builds for can-fd, you can pass `-DCAN_CLASSIC=y` to cmake to build for can classic):
1819

1920
```bash
2021
git clone --recursive https://github.com/OpenCyphal/demos

differential_pressure_sensor/src/main.c

Lines changed: 105 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,11 @@
2424
#include <uavcan/node/port/List_0_1.h>
2525
#include <uavcan/_register/Access_1_0.h>
2626
#include <uavcan/_register/List_1_0.h>
27+
#ifdef CAN_CLASSIC
28+
#include <uavcan/pnp/NodeIDAllocationData_1_0.h>
29+
#else
2730
#include <uavcan/pnp/NodeIDAllocationData_2_0.h>
28-
31+
#endif /* CAN_CLASSIC */
2932
// Use /sample/ instead of /unit/ if you need timestamping.
3033
#include <uavcan/si/unit/pressure/Scalar_1_0.h>
3134
#include <uavcan/si/unit/temperature/Scalar_1_0.h>
@@ -43,6 +46,43 @@
4346
/// For CAN FD the queue can be smaller.
4447
#define CAN_TX_QUEUE_CAPACITY 100
4548

49+
#ifdef CAN_CLASSIC
50+
#define CRC64WE_POLY 0x92F0E1EAABEA3693
51+
#define CRC64WE_INIT 0xFFFFFFFFFFFFFFFF
52+
#define CRC64WE_XOROUT 0xFFFFFFFFFFFFFFFF
53+
54+
static uint64_t crc64we_table[256];
55+
56+
static void init_crc64we_table()
57+
{
58+
for (uint64_t i = 0; i < 256; i++)
59+
{
60+
uint64_t crc = i;
61+
for (uint64_t j = 0; j < 8; j++)
62+
{
63+
if (crc & 1)
64+
crc = (crc >> 1) ^ CRC64WE_POLY;
65+
else
66+
crc = crc >> 1;
67+
}
68+
crc64we_table[i] = crc;
69+
}
70+
}
71+
72+
static uint64_t crc64we(const uint8_t* data, size_t length)
73+
{
74+
uint64_t crc = CRC64WE_INIT;
75+
76+
for (size_t i = 0; i < length; i++)
77+
{
78+
uint8_t byte = data[i];
79+
crc = crc64we_table[(crc ^ byte) & 0xFF] ^ (crc >> 8);
80+
}
81+
82+
return crc ^ CRC64WE_XOROUT;
83+
}
84+
#endif /* CAN_CLASSIC */
85+
4686
/// We keep the state of the application here. Feel free to use static variables instead if desired.
4787
typedef struct State
4888
{
@@ -255,20 +295,35 @@ static void handle1HzLoop(State* const state, const CanardMicrosecond monotonic_
255295
// Note that a high-integrity/safety-certified application is unlikely to be able to rely on this feature.
256296
if (rand() > RAND_MAX / 2) // NOLINT
257297
{
258-
// Note that this will only work over CAN FD. If you need to run PnP over Classic CAN, use message v1.0.
298+
#ifdef CAN_CLASSIC
299+
uavcan_pnp_NodeIDAllocationData_1_0 msg = {0};
300+
uint8_t uid[uavcan_node_GetInfo_Response_1_0_unique_id_ARRAY_CAPACITY_] = {0};
301+
getUniqueID(uid);
302+
init_crc64we_table();
303+
uint64_t uid_hash = crc64we(uid, sizeof uid);
304+
memcpy(&msg.unique_id_hash, &uid_hash, 5);
305+
uint8_t serialized[uavcan_pnp_NodeIDAllocationData_1_0_SERIALIZATION_BUFFER_SIZE_BYTES_] = {0};
306+
size_t serialized_size = sizeof(serialized);
307+
const int8_t err = uavcan_pnp_NodeIDAllocationData_1_0_serialize_(&msg, &serialized[0], &serialized_size);
308+
#else
259309
uavcan_pnp_NodeIDAllocationData_2_0 msg = {0};
260310
msg.node_id.value = UINT16_MAX;
261311
getUniqueID(msg.unique_id);
262312
uint8_t serialized[uavcan_pnp_NodeIDAllocationData_2_0_SERIALIZATION_BUFFER_SIZE_BYTES_] = {0};
263313
size_t serialized_size = sizeof(serialized);
264-
const int8_t err = uavcan_pnp_NodeIDAllocationData_2_0_serialize_(&msg, &serialized[0], &serialized_size);
314+
const int8_t err = uavcan_pnp_NodeIDAllocationData_2_0_serialize_(&msg, &serialized[0], &serialized_size);
315+
#endif /* CAN_CLASSIC */
265316
assert(err >= 0);
266317
if (err >= 0)
267318
{
268319
const CanardTransferMetadata meta = {
269320
.priority = CanardPrioritySlow,
270321
.transfer_kind = CanardTransferKindMessage,
322+
#ifdef CAN_CLASSIC
323+
.port_id = uavcan_pnp_NodeIDAllocationData_1_0_FIXED_PORT_ID_,
324+
#else
271325
.port_id = uavcan_pnp_NodeIDAllocationData_2_0_FIXED_PORT_ID_,
326+
#endif
272327
.remote_node_id = CANARD_NODE_ID_UNSET,
273328
.transfer_id = (CanardTransferID) (state->next_transfer_id.uavcan_pnp_allocation++),
274329
};
@@ -381,7 +436,33 @@ static void handle01HzLoop(State* const state, const CanardMicrosecond monotonic
381436
}
382437
}
383438
}
384-
439+
#ifdef CAN_CLASSIC
440+
static void processMessagePlugAndPlayNodeIDAllocation(State* const state,
441+
const uavcan_pnp_NodeIDAllocationData_1_0* const msg)
442+
{
443+
uint8_t uid[uavcan_node_GetInfo_Response_1_0_unique_id_ARRAY_CAPACITY_] = {0};
444+
getUniqueID(uid);
445+
init_crc64we_table();
446+
uint64_t uid_hash = crc64we(uid, sizeof uid);
447+
if (msg->allocated_node_id.count > 0 && (msg->allocated_node_id.elements[0].value <= CANARD_NODE_ID_MAX) &&
448+
(memcmp(&uid_hash, &msg->unique_id_hash, 5 /*sizeof(uid)*/) == 0))
449+
{
450+
printf("Got PnP node-ID allocation: %d\n", msg->allocated_node_id.elements[0].value);
451+
state->canard.node_id = (CanardNodeID) msg->allocated_node_id.elements[0].value;
452+
// Store the value into the non-volatile storage.
453+
uavcan_register_Value_1_0 reg = {0};
454+
uavcan_register_Value_1_0_select_natural16_(&reg);
455+
reg.natural16.value.elements[0] = msg->allocated_node_id.elements[0].value;
456+
reg.natural16.value.count = 1;
457+
registerWrite("uavcan.node.id", &reg);
458+
// We no longer need the subscriber, drop it to free up the resources (both memory and CPU time).
459+
(void) canardRxUnsubscribe(&state->canard,
460+
CanardTransferKindMessage,
461+
uavcan_pnp_NodeIDAllocationData_1_0_FIXED_PORT_ID_);
462+
}
463+
// Otherwise, ignore it: either it is a request from another node or it is a response to another node.
464+
}
465+
#else
385466
static void processMessagePlugAndPlayNodeIDAllocation(State* const state,
386467
const uavcan_pnp_NodeIDAllocationData_2_0* const msg)
387468
{
@@ -404,6 +485,7 @@ static void processMessagePlugAndPlayNodeIDAllocation(State* const
404485
}
405486
// Otherwise, ignore it: either it is a request from another node or it is a response to another node.
406487
}
488+
#endif /* CAN_CLASSIC */
407489

408490
static uavcan_node_ExecuteCommand_Response_1_1 processRequestExecuteCommand(
409491
const uavcan_node_ExecuteCommand_Request_1_1* req)
@@ -525,10 +607,17 @@ static void processReceivedTransfer(State* const state, const CanardRxTransfer*
525607
if (transfer->metadata.transfer_kind == CanardTransferKindMessage)
526608
{
527609
size_t size = transfer->payload_size;
610+
#ifdef CAN_CLASSIC
611+
if (transfer->metadata.port_id == uavcan_pnp_NodeIDAllocationData_1_0_FIXED_PORT_ID_)
612+
{
613+
uavcan_pnp_NodeIDAllocationData_1_0 msg = {0};
614+
if (uavcan_pnp_NodeIDAllocationData_1_0_deserialize_(&msg, transfer->payload, &size) >= 0)
615+
#else
528616
if (transfer->metadata.port_id == uavcan_pnp_NodeIDAllocationData_2_0_FIXED_PORT_ID_)
529617
{
530618
uavcan_pnp_NodeIDAllocationData_2_0 msg = {0};
531619
if (uavcan_pnp_NodeIDAllocationData_2_0_deserialize_(&msg, transfer->payload, &size) >= 0)
620+
#endif /* CAN_CLASSIC */
532621
{
533622
processMessagePlugAndPlayNodeIDAllocation(state, &msg);
534623
}
@@ -698,18 +787,24 @@ int main(const int argc, char* const argv[])
698787
// Configure the transport by reading the appropriate standard registers.
699788
uavcan_register_Value_1_0_select_natural16_(&val);
700789
val.natural16.value.count = 1;
790+
#ifdef CAN_CLASSIC
791+
val.natural16.value.elements[0] = CANARD_MTU_CAN_CLASSIC;
792+
#else
701793
val.natural16.value.elements[0] = CANARD_MTU_CAN_FD;
794+
#endif
702795
registerRead("uavcan.can.mtu", &val);
703796
assert(uavcan_register_Value_1_0_is_natural16_(&val) && (val.natural16.value.count == 1));
704797
// We also need the bitrate configuration register. In this demo we can't really use it but an embedded application
705798
// should define "uavcan.can.bitrate" of type natural32[2]; the second value is 0/ignored if CAN FD not supported.
799+
const char iface_name[] = "vcan0";
706800
const int sock[CAN_REDUNDANCY_FACTOR] = {
707-
socketcanOpen("vcan0", val.natural16.value.elements[0] > CANARD_MTU_CAN_CLASSIC) //
801+
socketcanOpen(iface_name, val.natural16.value.elements[0] > CANARD_MTU_CAN_CLASSIC) //
708802
};
709803
for (uint8_t ifidx = 0; ifidx < CAN_REDUNDANCY_FACTOR; ifidx++)
710804
{
711805
if (sock[ifidx] < 0)
712806
{
807+
printf("Unable to open can interface '%s'", iface_name);
713808
return -sock[ifidx];
714809
}
715810
state.canard_tx_queues[ifidx] = canardTxInit(CAN_TX_QUEUE_CAPACITY, val.natural16.value.elements[0]);
@@ -734,8 +829,13 @@ int main(const int argc, char* const argv[])
734829
const int8_t res = //
735830
canardRxSubscribe(&state.canard,
736831
CanardTransferKindMessage,
832+
#ifdef CAN_CLASSIC
833+
uavcan_pnp_NodeIDAllocationData_1_0_FIXED_PORT_ID_,
834+
uavcan_pnp_NodeIDAllocationData_1_0_EXTENT_BYTES_,
835+
#else
737836
uavcan_pnp_NodeIDAllocationData_2_0_FIXED_PORT_ID_,
738837
uavcan_pnp_NodeIDAllocationData_2_0_EXTENT_BYTES_,
838+
#endif
739839
CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
740840
&rx);
741841
if (res < 0)

0 commit comments

Comments
 (0)