Skip to content

Commit 3adf184

Browse files
bluetooth: OpenDroneID sample application w/ GAP Broadcaster Role.
Adding sample app for demonstrating transmission of OpenDroneID. Signed-off-by: Mayank Mahajan <mayankmahajan.x@nxp.com>
1 parent 2a1fa8e commit 3adf184

File tree

7 files changed

+2677
-0
lines changed

7 files changed

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

0 commit comments

Comments
 (0)