Skip to content

Commit 6d9c1b4

Browse files
samples: bluetooth: OpenDroneID
This application demonstrates ODID Message transmission over Bluetooth in GAP Broadcaster role. Currently this program supports transmitting static drone data via Bluetooth Beacon. Co-authored-by: Mayank Mahajan <[email protected]> Signed-off-by: Sumit Batra <[email protected]>
1 parent f0d0264 commit 6d9c1b4

File tree

7 files changed

+2690
-0
lines changed

7 files changed

+2690
-0
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
5+
project(opendroneid)
6+
7+
target_sources(app PRIVATE src/main.c)
8+
target_sources(app PRIVATE src/opendroneid.c)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
.. zephyr:code-sample:: bluetooth_opendroneid
2+
:name: OpenDroneID
3+
:relevant-api: bluetooth
4+
5+
Advertise ODID Messages using GAP Broadcaster role.
6+
7+
Overview
8+
********
9+
10+
Open Drone ID messages let's you distinguish between the Unmanned Aerial Vehicles.
11+
It also has UAV's real-time location/altitude, UA serial number, operator ID/location etc.
12+
The message format is defined in the ASTM F3411 Remote ID and the
13+
ASD-STAN prEN 4709-002 Direct Remote ID specifications.
14+
15+
This application demonstrates ODID Message transmission over Bluetooth in GAP Broadcaster role.
16+
Currently this program supports transmitting static drone data via Bluetooth Beacon.
17+
But this could easily be extended to simulate a bit more dynamic flight example.
18+
19+
Requirements
20+
************
21+
22+
* BlueZ running on the host, or
23+
* A board with Bluetooth LE support
24+
25+
Building and Running
26+
********************
27+
28+
This sample can be found under :zephyr_file:`samples/bluetooth/opendroneid` in the Zephyr tree.
29+
30+
See :zephyr:code-sample-category:`bluetooth` samples for details.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
CONFIG_BT=y
2+
CONFIG_LOG=y
3+
CONFIG_BT_DEVICE_NAME="Test OpenDroneID"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
sample:
2+
name: Bluetooth ODID
3+
tests:
4+
sample.bluetooth.opendroneid:
5+
harness: bluetooth
6+
platform_allow:
7+
- qemu_x86
8+
- kw45b41z_evk
9+
tags: bluetooth
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
/* SPDX-License-Identifier: Apache-2.0
2+
*
3+
* Copyright (C) 2021, Soren Friis
4+
* Copyright 2024 NXP
5+
*
6+
* Referred from https://github.com/opendroneid/transmitter-linux
7+
*/
8+
9+
#include <zephyr/types.h>
10+
#include <stddef.h>
11+
#include <zephyr/sys/util.h>
12+
13+
#include <zephyr/bluetooth/bluetooth.h>
14+
#include <zephyr/bluetooth/hci.h>
15+
16+
#include "opendroneid.h"
17+
18+
#define DEVICE_NAME CONFIG_BT_DEVICE_NAME
19+
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
20+
21+
#define BT_LE_ADV_PARAM_NCONN BT_LE_ADV_PARAM(BT_LE_ADV_OPT_USE_IDENTITY, 0x20, 0x20, NULL)
22+
23+
static uint8_t payload[29] = {
24+
0xFA, 0xFF, /* 0xFFFA = ASTM International, ASTM Remote ID. */
25+
0x0D, /* AD Application Code within the ASTM address space = ODID. */
26+
0x00, /* message counter starting at 0x00 and wrapping around at 0xFF. */
27+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
28+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* 25-bytes data */
29+
};
30+
31+
static const struct bt_data ad[] = {
32+
{
33+
.type = BT_DATA_SVC_DATA16,
34+
.data_len = ARRAY_SIZE(payload),
35+
.data = payload,
36+
},
37+
};
38+
39+
#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
40+
41+
#define BASIC_ID_POS_ZERO 0
42+
#define BASIC_ID_POS_ONE 1
43+
44+
static void fill_example_data(struct ODID_UAS_Data *uasData)
45+
{
46+
uasData->BasicID[BASIC_ID_POS_ZERO].UAType = ODID_UATYPE_HELICOPTER_OR_MULTIROTOR;
47+
uasData->BasicID[BASIC_ID_POS_ZERO].IDType = ODID_IDTYPE_SERIAL_NUMBER;
48+
char uas_id[] = "112624150A90E3AE1EC0";
49+
50+
strncpy(uasData->BasicID[BASIC_ID_POS_ZERO].UASID, uas_id,
51+
MINIMUM(sizeof(uas_id), sizeof(uasData->BasicID[BASIC_ID_POS_ZERO].UASID)));
52+
53+
uasData->BasicID[BASIC_ID_POS_ONE].UAType = ODID_UATYPE_HELICOPTER_OR_MULTIROTOR;
54+
uasData->BasicID[BASIC_ID_POS_ONE].IDType = ODID_IDTYPE_SPECIFIC_SESSION_ID;
55+
char uas_caa_id[] = "FD3454B778E565C24B70";
56+
57+
strncpy(uasData->BasicID[BASIC_ID_POS_ONE].UASID, uas_caa_id,
58+
MINIMUM(sizeof(uas_caa_id), sizeof(uasData->BasicID[BASIC_ID_POS_ONE].UASID)));
59+
60+
uasData->Auth[0].AuthType = ODID_AUTH_UAS_ID_SIGNATURE;
61+
uasData->Auth[0].DataPage = 0;
62+
uasData->Auth[0].LastPageIndex = 2;
63+
uasData->Auth[0].Length = 63;
64+
uasData->Auth[0].Timestamp = 28000000;
65+
char auth0_data[] = "12345678901234567";
66+
67+
memcpy(uasData->Auth[0].AuthData, auth0_data,
68+
MINIMUM(sizeof(auth0_data), sizeof(uasData->Auth[0].AuthData)));
69+
70+
uasData->Auth[1].AuthType = ODID_AUTH_UAS_ID_SIGNATURE;
71+
uasData->Auth[1].DataPage = 1;
72+
char auth1_data[] = "12345678901234567890123";
73+
74+
memcpy(uasData->Auth[1].AuthData, auth1_data,
75+
MINIMUM(sizeof(auth1_data), sizeof(uasData->Auth[1].AuthData)));
76+
77+
uasData->Auth[2].AuthType = ODID_AUTH_UAS_ID_SIGNATURE;
78+
uasData->Auth[2].DataPage = 2;
79+
char auth2_data[] = "12345678901234567890123";
80+
81+
memcpy(uasData->Auth[2].AuthData, auth2_data,
82+
MINIMUM(sizeof(auth2_data), sizeof(uasData->Auth[2].AuthData)));
83+
84+
uasData->SelfID.DescType = ODID_DESC_TYPE_TEXT;
85+
char description[] = "Drone ID test flight---";
86+
87+
strncpy(uasData->SelfID.Desc, description,
88+
MINIMUM(sizeof(description), sizeof(uasData->SelfID.Desc)));
89+
90+
uasData->System.OperatorLocationType = ODID_OPERATOR_LOCATION_TYPE_TAKEOFF;
91+
uasData->System.ClassificationType = ODID_CLASSIFICATION_TYPE_EU;
92+
uasData->System.OperatorLatitude = uasData->Location.Latitude + 0.001;
93+
uasData->System.OperatorLongitude = uasData->Location.Longitude - 0.001;
94+
uasData->System.AreaCount = 1;
95+
uasData->System.AreaRadius = 0;
96+
uasData->System.AreaCeiling = 0;
97+
uasData->System.AreaFloor = 0;
98+
uasData->System.CategoryEU = ODID_CATEGORY_EU_OPEN;
99+
uasData->System.ClassEU = ODID_CLASS_EU_CLASS_1;
100+
uasData->System.OperatorAltitudeGeo = 20.5f;
101+
uasData->System.Timestamp = 28056789;
102+
103+
uasData->OperatorID.OperatorIdType = ODID_OPERATOR_ID;
104+
char operatorId[] = "FIN87astrdge12k8";
105+
106+
strncpy(uasData->OperatorID.OperatorId, operatorId,
107+
MINIMUM(sizeof(operatorId), sizeof(uasData->OperatorID.OperatorId)));
108+
}
109+
110+
static void fill_example_gps_data(struct ODID_UAS_Data *uasData)
111+
{
112+
uasData->Location.Status = ODID_STATUS_AIRBORNE;
113+
uasData->Location.Direction = 361.f;
114+
uasData->Location.SpeedHorizontal = 0.0f;
115+
uasData->Location.SpeedVertical = 0.35f;
116+
uasData->Location.Latitude = 51.4791;
117+
uasData->Location.Longitude = -0.0013;
118+
uasData->Location.AltitudeBaro = 100;
119+
uasData->Location.AltitudeGeo = 110;
120+
uasData->Location.HeightType = ODID_HEIGHT_REF_OVER_GROUND;
121+
uasData->Location.Height = 80;
122+
uasData->Location.HorizAccuracy = createEnumHorizontalAccuracy(5.5f);
123+
uasData->Location.VertAccuracy = createEnumVerticalAccuracy(9.5f);
124+
uasData->Location.BaroAccuracy = createEnumVerticalAccuracy(0.5f);
125+
uasData->Location.SpeedAccuracy = createEnumSpeedAccuracy(0.5f);
126+
uasData->Location.TSAccuracy = createEnumTimestampAccuracy(0.1f);
127+
uasData->Location.TimeStamp = 360.52f;
128+
}
129+
130+
static struct ODID_UAS_Data uasData;
131+
static union ODID_Message_encoded encoded;
132+
static uint8_t msg_counters[ODID_MSG_COUNTER_AMOUNT];
133+
134+
static void bt_ready(int err)
135+
{
136+
char addr_s[BT_ADDR_LE_STR_LEN];
137+
bt_addr_le_t addr = {0};
138+
size_t count = 1;
139+
140+
if (err) {
141+
printf("Bluetooth init failed (err %d)\n", err);
142+
return;
143+
}
144+
145+
printf("Bluetooth initialized\n");
146+
147+
/* Start advertising */
148+
err = bt_le_adv_start(BT_LE_ADV_PARAM_NCONN, ad, ARRAY_SIZE(ad), NULL, 0);
149+
if (err) {
150+
printf("Advertising failed to start (err %d)\n", err);
151+
return;
152+
}
153+
154+
bt_id_get(&addr, &count);
155+
bt_addr_le_to_str(&addr, addr_s, sizeof(addr_s));
156+
157+
printf("ODID started, advertising as %s\n", addr_s);
158+
}
159+
160+
static void update_payload(uint8_t turn)
161+
{
162+
int err = 0;
163+
164+
switch (turn) {
165+
case 0: /* BasicID */
166+
err = encodeBasicIDMessage((ODID_BasicID_encoded *)&encoded, &uasData.BasicID[0]);
167+
if (err == ODID_SUCCESS) {
168+
memcpy(&payload[4], &encoded, sizeof(ODID_BasicID_encoded));
169+
memcpy(&payload[3], &msg_counters[ODID_MSG_COUNTER_BASIC_ID], 1);
170+
++msg_counters[ODID_MSG_COUNTER_BASIC_ID];
171+
}
172+
break;
173+
case 1: /* Location */
174+
/* Updating location for checking whether messages are dropped or not. */
175+
uasData.Location.Latitude = uasData.Location.Latitude - 0.01;
176+
uasData.Location.Longitude = uasData.Location.Longitude + 0.01;
177+
err = encodeLocationMessage((ODID_Location_encoded *)&encoded, &uasData.Location);
178+
if (err == ODID_SUCCESS) {
179+
memcpy(&payload[4], &encoded, sizeof(ODID_Location_encoded));
180+
memcpy(&payload[3], &msg_counters[ODID_MSG_COUNTER_LOCATION], 1);
181+
++msg_counters[ODID_MSG_COUNTER_LOCATION];
182+
}
183+
break;
184+
case 2: /* Auth */
185+
err = encodeAuthMessage((ODID_Auth_encoded *)&encoded, &uasData.Auth[0]);
186+
if (err == ODID_SUCCESS) {
187+
memcpy(&payload[4], &encoded, sizeof(ODID_Auth_encoded));
188+
memcpy(&payload[3], &msg_counters[ODID_MSG_COUNTER_AUTH], 1);
189+
++msg_counters[ODID_MSG_COUNTER_AUTH];
190+
}
191+
break;
192+
case 3: /* SelfID */
193+
err = encodeSelfIDMessage((ODID_SelfID_encoded *)&encoded, &uasData.SelfID);
194+
if (err == ODID_SUCCESS) {
195+
memcpy(&payload[4], &encoded, sizeof(ODID_SelfID_encoded));
196+
memcpy(&payload[3], &msg_counters[ODID_MSG_COUNTER_SELF_ID], 1);
197+
++msg_counters[ODID_MSG_COUNTER_SELF_ID];
198+
}
199+
break;
200+
case 4: /* System */
201+
err = encodeSystemMessage((ODID_System_encoded *)&encoded, &uasData.System);
202+
if (err == ODID_SUCCESS) {
203+
memcpy(&payload[4], &encoded, sizeof(ODID_System_encoded));
204+
memcpy(&payload[3], &msg_counters[ODID_MSG_COUNTER_SYSTEM], 1);
205+
++msg_counters[ODID_MSG_COUNTER_SYSTEM];
206+
}
207+
break;
208+
case 5: /* OperatorID */
209+
err = encodeOperatorIDMessage((ODID_OperatorID_encoded *)&encoded,
210+
&uasData.OperatorID);
211+
if (err == ODID_SUCCESS) {
212+
memcpy(&payload[4], &encoded, sizeof(ODID_OperatorID_encoded));
213+
memcpy(&payload[3], &msg_counters[ODID_MSG_COUNTER_OPERATOR_ID], 1);
214+
++msg_counters[ODID_MSG_COUNTER_OPERATOR_ID];
215+
}
216+
break;
217+
default:
218+
break;
219+
}
220+
}
221+
222+
int main(void)
223+
{
224+
int err;
225+
226+
printf("Starting ODID Demo\n");
227+
228+
/* Initialize UAS data. */
229+
odid_initUasData(&uasData);
230+
fill_example_data(&uasData);
231+
fill_example_gps_data(&uasData);
232+
memset(&encoded, 0, sizeof(union ODID_Message_encoded));
233+
for (int i = 0; i < ODID_MSG_COUNTER_AMOUNT; ++i) {
234+
msg_counters[i] = 0;
235+
}
236+
237+
/* Initialize the Bluetooth Subsystem */
238+
err = bt_enable(bt_ready);
239+
if (err) {
240+
printf("Bluetooth init failed (err %d)\n", err);
241+
return -1;
242+
}
243+
244+
while (true) {
245+
if (bt_is_ready()) {
246+
break;
247+
}
248+
249+
printf("Bluetooth not ready. Checking again in 100 ms\n");
250+
k_sleep(K_MSEC(100));
251+
}
252+
253+
/* Modify ODID data and update adv data. */
254+
uint8_t tx_counter = 0;
255+
256+
while (true) {
257+
uint8_t turn = tx_counter % 6;
258+
259+
update_payload(turn);
260+
261+
static const struct bt_data ad_new[] = {
262+
{
263+
.type = BT_DATA_SVC_DATA16,
264+
.data_len = ARRAY_SIZE(payload),
265+
.data = payload,
266+
},
267+
};
268+
269+
err = bt_le_adv_update_data(ad_new, ARRAY_SIZE(ad_new), NULL, 0);
270+
if (err) {
271+
printf("Bluetooth update adv data failed (err %d)\n", err);
272+
continue;
273+
}
274+
275+
k_sleep(K_MSEC(100));
276+
277+
++tx_counter;
278+
}
279+
280+
return 0;
281+
}

0 commit comments

Comments
 (0)