aboutsummaryrefslogtreecommitdiff
path: root/drive-sdk/src
diff options
context:
space:
mode:
Diffstat (limited to 'drive-sdk/src')
-rw-r--r--drive-sdk/src/CMakeLists.txt22
-rw-r--r--drive-sdk/src/advertisement.c148
-rw-r--r--drive-sdk/src/advertisement.h145
-rw-r--r--drive-sdk/src/anki_util.c62
-rw-r--r--drive-sdk/src/anki_util.h29
-rw-r--r--drive-sdk/src/common.h48
-rw-r--r--drive-sdk/src/eir.c54
-rw-r--r--drive-sdk/src/eir.h56
-rw-r--r--drive-sdk/src/protocol.c246
-rw-r--r--drive-sdk/src/protocol.h559
-rw-r--r--drive-sdk/src/uuid.c25
-rw-r--r--drive-sdk/src/uuid.h50
-rw-r--r--drive-sdk/src/vehicle_gatt_profile.h30
13 files changed, 1474 insertions, 0 deletions
diff --git a/drive-sdk/src/CMakeLists.txt b/drive-sdk/src/CMakeLists.txt
new file mode 100644
index 0000000..b326665
--- /dev/null
+++ b/drive-sdk/src/CMakeLists.txt
@@ -0,0 +1,22 @@
+# Include the directory itself as a path to include directories
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(drivekit_SOURCES
+ eir.c eir.h
+ anki_util.c
+ advertisement.c advertisement.h
+ uuid.c uuid.h
+ protocol.c protocol.h
+)
+
+
+# For a large number of source files you can create it in a simpler way
+# using file() function:
+# file(GLOB drivekit_SOURCES *.cpp)
+
+add_library(ankidrive ${drivekit_SOURCES})
+
+install(TARGETS ankidrive
+ LIBRARY DESTINATION lib
+ ARCHIVE DESTINATION lib
+)
diff --git a/drive-sdk/src/advertisement.c b/drive-sdk/src/advertisement.c
new file mode 100644
index 0000000..e5cbfe9
--- /dev/null
+++ b/drive-sdk/src/advertisement.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2014 Anki, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "eir.h"
+#include "advertisement.h"
+#include "vehicle_gatt_profile.h"
+#include "uuid.h"
+#include "anki_util.h"
+
+#define VEHICLE_STATE_FULL_BATTERY (1 << 4)
+#define VEHICLE_STATE_LOW_BATTERY (1 << 5)
+#define VEHICLE_STATE_ON_CHARGER (1 << 6)
+#define IS_VEHICLE_STATE_SET(state, flag) ( ((state) & (flag)) == (flag) )
+
+int is_anki_vehicle_service_uuid(uuid128_t *a);
+
+uint8_t anki_vehicle_parse_adv_record(const uint8_t *scan_data, const size_t scan_data_len, anki_vehicle_adv_t *anki_vehicle_adv)
+{
+ // no data to parse
+ if (scan_data == NULL)
+ return 1;
+
+ int i = 0;
+ while (i < scan_data_len) {
+ uint8_t len = scan_data[i++];
+ if (len == 0) break;
+
+ ble_adv_record_type_t type = scan_data[i];
+ if (type == ADV_TYPE_INVALID) break;
+
+ const uint8_t *data = &scan_data[i+1];
+ uint8_t data_len = len - 1;
+ switch(type) {
+ case ADV_TYPE_INVALID:
+ return -1;
+ break;
+ case ADV_TYPE_FLAGS:
+ if (anki_vehicle_adv != NULL)
+ memmove(&anki_vehicle_adv->flags, data, data_len);
+ break;
+ case ADV_TYPE_UUID_128:
+ {
+ if ((data_len == sizeof(uuid128_t)) &&
+ !is_anki_vehicle_service_uuid((uuid128_t*)data) )
+ return 2;
+
+ if (anki_vehicle_adv != NULL) {
+ memmove(&anki_vehicle_adv->service_id, data, data_len);
+ } else {
+ return 0;
+ }
+ break;
+ }
+ case ADV_TYPE_LOCAL_NAME:
+ if (anki_vehicle_adv != NULL) {
+ anki_vehicle_parse_local_name(data, data_len, &anki_vehicle_adv->local_name);
+ }
+ break;
+ case ADV_TYPE_TX_POWER:
+ if (anki_vehicle_adv != NULL)
+ memmove(&anki_vehicle_adv->tx_power, data, data_len);
+ break;
+ case ADV_TYPE_MANUFACTURER_DATA:
+ if (anki_vehicle_adv != NULL) {
+ anki_vehicle_parse_mfg_data(data, data_len, &anki_vehicle_adv->mfg_data);
+ }
+ break;
+ default:
+ return 3;
+ printf("\nunknown type: %d\n", type);
+ hexdump("data: ", 80, data, data_len);
+ break;
+ }
+
+ i += len;
+ }
+
+ return ((anki_vehicle_adv != NULL) && is_anki_vehicle_service_uuid(&anki_vehicle_adv->service_id)) ? 0 : 2;
+}
+
+uint8_t anki_vehicle_adv_record_has_anki_uuid(const uint8_t *scan_data, const size_t scan_data_len)
+{
+ return (anki_vehicle_parse_adv_record(scan_data, scan_data_len, NULL) == 0);
+}
+
+int is_anki_vehicle_service_uuid(uuid128_t *a)
+{
+ static const uint8_t ANKI_SERVICE_UUID_BYTES[16] = ANKI_SERVICE_UUID_LE;
+ return (uuid128_cmp(a, (uuid128_t *)ANKI_SERVICE_UUID_BYTES) == 0);
+}
+
+uint8_t anki_vehicle_parse_mfg_data(const uint8_t *bytes, uint8_t len, anki_vehicle_adv_mfg_t *mfg_data)
+{
+ if (bytes == NULL || len != 8) return 1;
+
+ // bytes stored on the vehicle in Big Endian order
+ // This assumes the host is little endian
+
+ mfg_data->product_id = (bytes[1] | (bytes[0] << 8));
+ mfg_data->_reserved = bytes[2];
+ mfg_data->model_id = bytes[3];
+ mfg_data->identifier = (bytes[7] | (bytes[6] << 8) | (bytes[5] << 16) | (bytes[4] << 24));
+
+ return 0;
+}
+
+uint8_t anki_vehicle_parse_local_name(const uint8_t *bytes, uint8_t len, anki_vehicle_adv_info_t *local_name)
+{
+ if (len == 0) return 1;
+
+ uint8_t state = (uint8_t)bytes[0] & 0xff;
+ local_name->state.full_battery = IS_VEHICLE_STATE_SET(state, VEHICLE_STATE_FULL_BATTERY);
+ local_name->state.low_battery = IS_VEHICLE_STATE_SET(state, VEHICLE_STATE_LOW_BATTERY);
+ local_name->state.on_charger = IS_VEHICLE_STATE_SET(state, VEHICLE_STATE_ON_CHARGER);
+
+ if (len > 1) {
+ local_name->version = bytes[1];
+ }
+
+ if (len > 2) {
+ local_name->version |= (bytes[2] << 8);
+ }
+
+ if (len > 8) {
+ uint8_t name_len = len - 8;
+ memset(local_name->name, 0, (sizeof local_name->name));
+ memmove(local_name->name, &bytes[8], name_len);
+ }
+
+ return 0;
+}
diff --git a/drive-sdk/src/advertisement.h b/drive-sdk/src/advertisement.h
new file mode 100644
index 0000000..9b192e2
--- /dev/null
+++ b/drive-sdk/src/advertisement.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2014 Anki, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_advertisement_h
+#define INCLUDE_advertisement_h
+
+#include <stdint.h>
+#include "common.h"
+#include "uuid.h"
+
+ANKI_BEGIN_DECL
+
+/**
+ * The state of a vehicle recorded in the advertising packet.
+ *
+ * - full_battery: The vehicle battery is fully charged
+ * - low_battery: The vehicle battery has a low charge and will die soon
+ * - on_charger: The vehicle is currently on the charger
+ */
+typedef struct anki_vehicle_adv_state {
+ uint8_t _reserved:4;
+ uint8_t full_battery:1; // 4: TRUE if Car has full battery
+ uint8_t low_battery:1; // 5: TRUE if Car has low battery
+ uint8_t on_charger:1; // 6: TRUE if Car is on Charger
+ uint8_t _unavailable:1; // 7: Cannot be used due to BTLE character set constraint
+} anki_vehicle_adv_state_t;
+
+/**
+ * Vehicle information packed in the LOCAL_NAME string record
+ * of an advertising packet.
+ *
+ * - state: Current vehicle state.
+ * NOTE: Changes to the vehicle state will cause the LOCAL_NAME value
+ * to change.
+ * - version: Firmware version running on the vehicle
+ * - name: User-defined name in UTF-8 encoding
+ */
+typedef struct anki_vehicle_adv_info {
+ anki_vehicle_adv_state_t state;
+ uint16_t version;
+ uint8_t _reserved[5];
+ unsigned char name[13]; // UTF8: 12 bytes + NULL.
+} anki_vehicle_adv_info_t;
+
+
+/**
+ * Vehicle hardware information encodeded in the MANUFACTURER_DATA
+ * record of an advertising packet.
+ *
+ * - identifier: Unique identifer for a physical vehicle
+ * - model_id: The model type of a vehicle
+ * - product_id: Value identifying the vehicle as Anki Drive hardware
+ */
+typedef struct anki_vehicle_adv_mfg {
+ uint32_t identifier;
+ uint8_t model_id;
+ uint8_t _reserved;
+ uint16_t product_id;
+} anki_vehicle_adv_mfg_t;
+
+
+/**
+ * Vehicle information present in Bluetooth LE advertising packets.
+ *
+ * flags: EIR / AD flags
+ * tx_power: transmission power
+ * mfg_data: parsed data from the MANUFACTURER_DATA bytes
+ * local_name: parsed data from the LOCAL_NAME string bytes
+ * service_id: Anki Vehicle UUID (128-bit)
+ */
+typedef struct anki_vehicle_adv {
+ uint8_t flags;
+ uint8_t tx_power;
+ anki_vehicle_adv_mfg_t mfg_data;
+ anki_vehicle_adv_info_t local_name;
+ uuid128_t service_id;
+} anki_vehicle_adv_t;
+
+/**
+ * Test whether the scan record contains an Anki Drive vehicle service UUID.
+ *
+ * @param scan_data Bytes obtained by scanning vehicle advertising packets.
+ * @param scan_data_len Length of bytes in scan_data.
+ *
+ * @return 0 on failure, 1 on success.
+ */
+uint8_t anki_vehicle_adv_record_has_anki_uuid(const uint8_t *scan_data, const size_t scan_data_len);
+
+
+/**
+ * Parse advertising data for an Anki Drive vehicle.
+ *
+ * @param scan_data Bytes obtained by scanning vehicle advertising packets.
+ * @param scan_data_len Length of bytes in scan_data.
+ * @param anki_vehicle_adv Pointer to a structure for storing accumulated information for a vehicle.
+ * Parsed scan data will overwrite matching record data in the struct.
+ *
+ * @return 0 on success, 1 on failure,
+ * 2 if the data is not anki drive
+ * 3 if unknown EIR data is encountered
+ *
+ * @discuss Pass NULL for anki_vehicle_adv in order to determine if this is an appropriate record.
+ *
+ *
+ */
+uint8_t anki_vehicle_parse_adv_record(const uint8_t *scan_data, const size_t scan_data_len, anki_vehicle_adv_t *anki_vehicle_adv);
+
+/**
+ * Parse MANUFACTERER_DATA record from a vehicle advertising packet.
+ *
+ * @param scan_data Bytes obtained by scanning vehicle advertising packets.
+ * @param scan_data_len Length of bytes in scan_data.
+ * @param mfg_data Pointer to a structure for storing parsed data.
+ *
+ * @return 0 on success, 1 on failure.
+ */
+uint8_t anki_vehicle_parse_mfg_data(const uint8_t *bytes, uint8_t len, anki_vehicle_adv_mfg_t *mfg_data);
+
+/**
+ * Parse LOCAL_NAME record from a vehicle advertising packet.
+ *
+ * @param scan_data Bytes obtained by scanning vehicle advertising packets.
+ * @param scan_data_len Length of bytes in scan_data.
+ * @param local_name Pointer to a structure for storing parsed data.
+ *
+ * @return 0 on success, 1 on failure.
+ */
+uint8_t anki_vehicle_parse_local_name(const uint8_t *bytes, uint8_t len, anki_vehicle_adv_info_t *local_name);
+
+ANKI_END_DECL
+
+#endif
diff --git a/drive-sdk/src/anki_util.c b/drive-sdk/src/anki_util.c
new file mode 100644
index 0000000..24cb1dd
--- /dev/null
+++ b/drive-sdk/src/anki_util.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2014 Anki, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include <assert.h>
+
+void bytes_to_hex(const void *value, size_t len, char **output) {
+ const unsigned char *bytes = (const unsigned char *)value;
+ size_t bufferLen = len * 3 * sizeof(char);
+ char str[bufferLen];
+ memset(str, 0, bufferLen);
+
+ const char *hex = "0123456789ABCDEF";
+ char *s = str;
+
+ for (size_t i = 0; i < len-1; ++i ) {
+ *s++ = hex[(*bytes>>4)&0xF];
+ *s++ = hex[(*bytes++)&0xF];
+ *s++ = ' ';
+ }
+ *s++ = hex[(*bytes>>4)&0xF];
+ *s++ = hex[(*bytes++)&0xF];
+ *s++ = 0;
+
+ if (output != NULL)
+ memmove(*output, str, bufferLen);
+}
+
+void hexdump(const char *prefix, const size_t column_len, const void *value, size_t len) {
+ assert(prefix != NULL);
+ assert(column_len > 0);
+ int row_count = (len / column_len) + 1;
+ int i;
+
+ size_t slen = column_len*3;
+ char* s = malloc(slen * sizeof(char));
+ for (i = 0; i < row_count; ++i) {
+ memset(s, 0, slen);
+ bytes_to_hex(value, len, (char **)&s);
+ printf("%s %s\n", prefix, s);
+ }
+ free(s);
+}
+
+
diff --git a/drive-sdk/src/anki_util.h b/drive-sdk/src/anki_util.h
new file mode 100644
index 0000000..8c46361
--- /dev/null
+++ b/drive-sdk/src/anki_util.h
@@ -0,0 +1,29 @@
+
+/*
+ * Copyright (c) 2014 Anki, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_anki_util_h
+#define INCLUDE_anki_util_h
+
+#include "common.h"
+
+ANKI_BEGIN_DECL
+
+void hexdump(const char *prefix, const size_t column_len, const void *value, size_t len);
+
+ANKI_END_DECL
+
+#endif
diff --git a/drive-sdk/src/common.h b/drive-sdk/src/common.h
new file mode 100644
index 0000000..d946ceb
--- /dev/null
+++ b/drive-sdk/src/common.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2014 Anki, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_common_h
+#define INCLUDE_common_h
+
+#ifdef __cplusplus
+# define ANKI_BEGIN_DECL extern "C" {
+# define ANKI_END_DECL }
+#else
+ /** Start declarations in C mode */
+# define ANKI_BEGIN_DECL /* empty */
+ /** End declarations in C mode */
+# define ANKI_END_DECL /* empty */
+#endif
+
+/** Declare a public function */
+#if __GNUC__ >= 4
+# define ANKI_EXTERN(type) extern \
+ __attribute__((visibility("default"))) \
+ type
+#elif defined(_MSC_VER)
+# define ANKI_EXTERN(type) __declspec(dllexport) type
+#else
+# define ANKI_EXTERN(type) extern type
+#endif
+
+/** Declare a function as always inlined. */
+#if defined(_MSC_VER)
+# define ANKI_INLINE(type) static __inline type
+#else
+# define ANKI_INLINE(type) static inline type
+#endif
+
+#endif
diff --git a/drive-sdk/src/eir.c b/drive-sdk/src/eir.c
new file mode 100644
index 0000000..1255bb4
--- /dev/null
+++ b/drive-sdk/src/eir.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2014 Anki, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "eir.h"
+
+int ble_adv_parse_scan(const uint8_t *data, const size_t data_len, size_t *record_count, ble_adv_record_t records[])
+{
+ // no data to parse
+ if (data == NULL)
+ return 1;
+
+ uint8_t record_index = 0;
+ int i = 0;
+ while (i < data_len) {
+ uint8_t len = data[i++];
+ if (len == 0) break;
+
+ ble_adv_record_type_t type = data[i];
+ if (type == ADV_TYPE_INVALID) break;
+
+ if (records != NULL) {
+ ble_adv_record_t *record = &records[record_index];
+ record->type = type;
+ record->length = (len-1);
+ memmove(record->data, &data[i+1], len-1);
+ }
+
+ record_index++;
+ i += len;
+ }
+
+ if (record_count != NULL) {
+ *record_count = record_index;
+ }
+
+ return 0;
+}
diff --git a/drive-sdk/src/eir.h b/drive-sdk/src/eir.h
new file mode 100644
index 0000000..8f434bd
--- /dev/null
+++ b/drive-sdk/src/eir.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2014 Anki, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_eir_h
+#define INCLUDE_eir_h
+
+#include <stdint.h>
+#include <stddef.h>
+
+#include "common.h"
+
+ANKI_BEGIN_DECL
+
+#define BLE_ADV_RECORD_MAX_LEN 32
+struct ble_adv_record {
+ uint8_t type;
+ uint8_t length;
+ uint8_t data[30];
+};
+typedef struct ble_adv_record ble_adv_record_t;
+
+/*
+ * These Advertising Data types are required for parsing Anki Drive vehicle records.
+ *
+ * This is an incomplete list.
+ * See https://www.bluetooth.org/en-us/specification/assigned-numbers/generic-access-profile
+ * for all supported values
+ */
+enum ble_adv_record_type {
+ ADV_TYPE_INVALID = 0,
+ ADV_TYPE_FLAGS = 0x1,
+ ADV_TYPE_UUID_128 = 0x7,
+ ADV_TYPE_LOCAL_NAME = 0x9,
+ ADV_TYPE_TX_POWER = 0xa,
+ ADV_TYPE_MANUFACTURER_DATA = 0xff
+};
+typedef uint8_t ble_adv_record_type_t;
+
+int ble_adv_parse_scan(const uint8_t *data, const size_t data_len, size_t *record_count, ble_adv_record_t records[]);
+
+ANKI_END_DECL
+
+#endif
diff --git a/drive-sdk/src/protocol.c b/drive-sdk/src/protocol.c
new file mode 100644
index 0000000..0c00133
--- /dev/null
+++ b/drive-sdk/src/protocol.c
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2014 Anki, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#include "protocol.h"
+
+#define ANKI_VEHICLE_MSG_TYPE_SIZE 2
+
+uint8_t anki_vehicle_msg_set_sdk_mode(anki_vehicle_msg_t *message, uint8_t on, uint8_t flags)
+{
+ assert(message != NULL);
+
+ anki_vehicle_msg_sdk_mode_t *msg = (anki_vehicle_msg_sdk_mode_t *)message;
+ memset(msg, 0, sizeof(anki_vehicle_msg_sdk_mode_t));
+ msg->size = ANKI_VEHICLE_MSG_SDK_MODE_SIZE;
+ msg->msg_id = ANKI_VEHICLE_MSG_C2V_SDK_MODE;
+ msg->on = on;
+ msg->flags = flags;
+
+ return sizeof(anki_vehicle_msg_sdk_mode_t);
+}
+
+uint8_t anki_vehicle_msg_set_speed(anki_vehicle_msg_t *message, uint16_t speed_mm_per_sec, uint16_t accel_mm_per_sec2)
+{
+ assert(message != NULL);
+
+ anki_vehicle_msg_set_speed_t *msg = (anki_vehicle_msg_set_speed_t *)message;
+ memset(msg, 0, sizeof(anki_vehicle_msg_set_speed_t));
+ msg->size = ANKI_VEHICLE_MSG_C2V_SET_SPEED_SIZE;
+ msg->msg_id = ANKI_VEHICLE_MSG_C2V_SET_SPEED;
+ msg->speed_mm_per_sec = speed_mm_per_sec;
+ msg->accel_mm_per_sec2 = accel_mm_per_sec2;
+
+ return sizeof(anki_vehicle_msg_set_speed_t);
+}
+
+uint8_t anki_vehicle_msg_set_offset_from_road_center(anki_vehicle_msg_t *msg, float offset_mm)
+{
+ assert(msg != NULL);
+
+ anki_vehicle_msg_set_offset_from_road_center_t *m = (anki_vehicle_msg_set_offset_from_road_center_t *)msg;
+ memset(m, 0, sizeof(anki_vehicle_msg_set_offset_from_road_center_t));
+ m->size = sizeof(anki_vehicle_msg_set_offset_from_road_center_t) - ANKI_VEHICLE_MSG_BASE_SIZE;
+ m->msg_id = ANKI_VEHICLE_MSG_C2V_SET_OFFSET_FROM_ROAD_CENTER;
+ m->offset_mm = offset_mm;
+
+ return sizeof(anki_vehicle_msg_set_offset_from_road_center_t);
+}
+
+uint8_t anki_vehicle_msg_change_lane(anki_vehicle_msg_t *message,
+ uint16_t horizontal_speed_mm_per_sec,
+ uint16_t horizontal_accel_mm_per_sec2,
+ float offset_from_center_mm)
+{
+ assert(message != NULL);
+ memset(message, 0, sizeof(anki_vehicle_msg_t));
+
+ anki_vehicle_msg_change_lane_t *msg = (anki_vehicle_msg_change_lane_t *)message;
+ msg->size = ANKI_VEHICLE_MSG_C2V_CHANGE_LANE_SIZE;
+ msg->msg_id = ANKI_VEHICLE_MSG_C2V_CHANGE_LANE;
+ msg->horizontal_speed_mm_per_sec = horizontal_speed_mm_per_sec;
+ msg->horizontal_accel_mm_per_sec2 = horizontal_accel_mm_per_sec2;
+ msg->offset_from_road_center_mm = offset_from_center_mm;
+
+ return sizeof(anki_vehicle_msg_change_lane_t);
+}
+
+uint8_t anki_vehicle_msg_set_lights(anki_vehicle_msg_t *message, uint8_t mask)
+{
+ assert(message != NULL);
+
+ anki_vehicle_msg_set_lights_t *msg = (anki_vehicle_msg_set_lights_t *)message;
+ memset(msg, 0, sizeof(anki_vehicle_msg_set_lights_t));
+ msg->size = ANKI_VEHICLE_MSG_C2V_SET_LIGHTS_SIZE;
+ msg->msg_id = ANKI_VEHICLE_MSG_C2V_SET_LIGHTS;
+ msg->light_mask = mask;
+
+ return sizeof(anki_vehicle_msg_set_lights_t);
+}
+
+void anki_vehicle_light_config(anki_vehicle_light_config_t *config,
+ anki_vehicle_light_channel_t channel,
+ anki_vehicle_light_effect_t effect,
+ uint8_t start,
+ uint8_t end,
+ uint16_t cycles_per_min)
+{
+ assert(config != NULL);
+ config->channel = channel;
+ config->effect = effect;
+ config->start = (start > ANKI_VEHICLE_MAX_LIGHT_INTENSITY) ? ANKI_VEHICLE_MAX_LIGHT_INTENSITY : start;
+ config->end = (end > ANKI_VEHICLE_MAX_LIGHT_INTENSITY) ? ANKI_VEHICLE_MAX_LIGHT_INTENSITY : end;
+ uint16_t cpm = cycles_per_min > ANKI_VEHICLE_MAX_LIGHT_TIME ? ANKI_VEHICLE_MAX_LIGHT_TIME : cycles_per_min;
+ config->cycles_per_10_sec = (uint8_t)(cpm / 6);
+}
+
+uint8_t anki_vehicle_msg_lights_pattern_append(anki_vehicle_msg_lights_pattern_t* msg,
+ anki_vehicle_light_config_t* config)
+{
+ assert(msg != NULL);
+ assert(config != NULL);
+
+ if ( (msg->msg_id != ANKI_VEHICLE_MSG_C2V_LIGHTS_PATTERN)
+ || (msg->size != ANKI_VEHICLE_MSG_C2V_LIGHTS_PATTERN_SIZE)
+ || (msg->channel_count == 0)) {
+ memset(msg, 0, sizeof(anki_vehicle_msg_lights_pattern_t));
+ }
+
+ uint8_t next_index = msg->channel_count;
+ if (next_index >= LIGHT_CHANNEL_COUNT_MAX) {
+ return 0;
+ }
+
+ msg->size = ANKI_VEHICLE_MSG_C2V_LIGHTS_PATTERN_SIZE;
+ msg->msg_id = ANKI_VEHICLE_MSG_C2V_LIGHTS_PATTERN;
+
+ anki_vehicle_light_config_t* target = &(msg->channel_config[next_index]);
+ const size_t len = sizeof(*config);
+ memmove(target, config, len);
+ (msg->channel_count)++;
+
+ return len;
+}
+
+/*
+uint8_t anki_vehicle_msg_lights_pattern(anki_vehicle_msg_t *message,
+ anki_vehicle_light_config_t* config,
+ uint8_t count)
+*/
+uint8_t anki_vehicle_msg_lights_pattern(anki_vehicle_msg_t* message,
+ anki_vehicle_light_channel_t channel,
+ anki_vehicle_light_effect_t effect,
+ uint8_t start,
+ uint8_t end,
+ uint16_t cycles_per_min)
+{
+ assert(message != NULL);
+
+ anki_vehicle_msg_lights_pattern_t *msg = (anki_vehicle_msg_lights_pattern_t *)message;
+ memset(msg, 0, sizeof(anki_vehicle_msg_lights_pattern_t));
+ msg->size = ANKI_VEHICLE_MSG_C2V_LIGHTS_PATTERN_SIZE;
+ msg->msg_id = ANKI_VEHICLE_MSG_C2V_LIGHTS_PATTERN;
+
+ anki_vehicle_light_config_t config;
+ anki_vehicle_light_config(&config, channel, effect, start, end, cycles_per_min);
+
+ const uint8_t len = anki_vehicle_msg_lights_pattern_append(msg, &config);
+ if (len == 0) {
+ return 0;
+ }
+
+ return sizeof(*msg);
+}
+
+uint8_t anki_vehicle_msg_disconnect(anki_vehicle_msg_t *msg)
+{
+ assert(msg != NULL);
+ msg->size = ANKI_VEHICLE_MSG_BASE_SIZE;
+ msg->msg_id = ANKI_VEHICLE_MSG_C2V_DISCONNECT;
+ return ANKI_VEHICLE_MSG_TYPE_SIZE;
+}
+
+uint8_t anki_vehicle_msg_cancel_lane_change(anki_vehicle_msg_t *msg)
+{
+ assert(msg != NULL);
+ msg->size = ANKI_VEHICLE_MSG_BASE_SIZE;
+ msg->msg_id = ANKI_VEHICLE_MSG_C2V_CANCEL_LANE_CHANGE;
+ return ANKI_VEHICLE_MSG_TYPE_SIZE;
+}
+
+uint8_t anki_vehicle_msg_turn(anki_vehicle_msg_t *msg,
+ anki_vehicle_turn_type_t type,
+ anki_vehicle_turn_trigger_t trigger)
+{
+ assert(msg != NULL);
+
+ anki_vehicle_msg_turn_t *m = (anki_vehicle_msg_turn_t *)msg;
+ memset(m, 0, sizeof(anki_vehicle_msg_turn_t));
+ m->size = ANKI_VEHICLE_MSG_C2V_TURN_SIZE;
+ m->msg_id = ANKI_VEHICLE_MSG_C2V_TURN;
+ m->type = (uint8_t)type;
+ m->trigger = (uint8_t)trigger;
+
+ return sizeof(anki_vehicle_msg_turn_t);
+}
+
+uint8_t anki_vehicle_msg_turn_180(anki_vehicle_msg_t *msg)
+{
+ return anki_vehicle_msg_turn(msg, VEHICLE_TURN_UTURN, VEHICLE_TURN_TRIGGER_IMMEDIATE);
+}
+
+uint8_t anki_vehicle_msg_set_config_params(anki_vehicle_msg_t* msg,
+ uint8_t super_code_parse_mask,
+ anki_track_material_t track_material)
+{
+ assert(msg != NULL);
+
+ anki_vehicle_msg_set_config_params_t* m = (anki_vehicle_msg_set_config_params_t*)msg;
+ memset(m, 0, sizeof(anki_vehicle_msg_set_config_params_t));
+ m->size = ANKI_VEHICLE_MSG_C2V_SET_CONFIG_PARAMS_SIZE;
+ m->msg_id = ANKI_VEHICLE_MSG_C2V_SET_CONFIG_PARAMS;
+ m->super_code_parse_mask = super_code_parse_mask;
+ m->track_material = track_material;
+
+ return sizeof(anki_vehicle_msg_set_config_params_t);
+}
+
+uint8_t anki_vehicle_msg_ping(anki_vehicle_msg_t *msg)
+{
+ assert(msg != NULL);
+ msg->size = ANKI_VEHICLE_MSG_BASE_SIZE;
+ msg->msg_id = ANKI_VEHICLE_MSG_C2V_PING_REQUEST;
+ return ANKI_VEHICLE_MSG_TYPE_SIZE;
+}
+
+uint8_t anki_vehicle_msg_get_version(anki_vehicle_msg_t *msg)
+{
+ assert(msg != NULL);
+ msg->size = ANKI_VEHICLE_MSG_BASE_SIZE;
+ msg->msg_id = ANKI_VEHICLE_MSG_C2V_VERSION_REQUEST;
+ return ANKI_VEHICLE_MSG_TYPE_SIZE;
+}
+
+uint8_t anki_vehicle_msg_get_battery_level(anki_vehicle_msg_t *msg)
+{
+ assert(msg != NULL);
+ msg->size = ANKI_VEHICLE_MSG_BASE_SIZE;
+ msg->msg_id = ANKI_VEHICLE_MSG_C2V_BATTERY_LEVEL_REQUEST;
+ return ANKI_VEHICLE_MSG_TYPE_SIZE;
+}
diff --git a/drive-sdk/src/protocol.h b/drive-sdk/src/protocol.h
new file mode 100644
index 0000000..259960a
--- /dev/null
+++ b/drive-sdk/src/protocol.h
@@ -0,0 +1,559 @@
+/*
+ * Copyright (c) 2014 Anki, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_protocol_h
+#define INCLUDE_protocol_h
+
+#include <stdint.h>
+
+#include "common.h"
+
+ANKI_BEGIN_DECL
+
+#define ANKI_VEHICLE_MSG_MAX_SIZE 20
+#define ANKI_VEHICLE_MSG_PAYLOAD_MAX_SIZE 18
+#define ANKI_VEHICLE_MSG_BASE_SIZE 1
+
+/** Identifier for a vehicle message */
+enum {
+ // BLE Connections
+ ANKI_VEHICLE_MSG_C2V_DISCONNECT = 0x0d,
+
+ // Ping request / response
+ ANKI_VEHICLE_MSG_C2V_PING_REQUEST = 0x16,
+ ANKI_VEHICLE_MSG_V2C_PING_RESPONSE = 0x17,
+
+ // Messages for checking vehicle version info
+ ANKI_VEHICLE_MSG_C2V_VERSION_REQUEST = 0x18,
+ ANKI_VEHICLE_MSG_V2C_VERSION_RESPONSE = 0x19,
+
+ // Battery level
+ ANKI_VEHICLE_MSG_C2V_BATTERY_LEVEL_REQUEST = 0x1a,
+ ANKI_VEHICLE_MSG_V2C_BATTERY_LEVEL_RESPONSE = 0x1b,
+
+ // Lights
+ ANKI_VEHICLE_MSG_C2V_SET_LIGHTS = 0x1d,
+
+ // Driving Commands
+ ANKI_VEHICLE_MSG_C2V_SET_SPEED = 0x24,
+ ANKI_VEHICLE_MSG_C2V_CHANGE_LANE = 0x25,
+ ANKI_VEHICLE_MSG_C2V_CANCEL_LANE_CHANGE = 0x26,
+ ANKI_VEHICLE_MSG_C2V_TURN = 0x32,
+
+ // Vehicle position updates
+ ANKI_VEHICLE_MSG_V2C_LOCALIZATION_POSITION_UPDATE = 0x27,
+ ANKI_VEHICLE_MSG_V2C_LOCALIZATION_TRANSITION_UPDATE = 0x29,
+ ANKI_VEHICLE_MSG_V2C_LOCALIZATION_INTERSECTION_UPDATE = 0x2a,
+ ANKI_VEHICLE_MSG_V2C_VEHICLE_DELOCALIZED = 0x2b,
+ ANKI_VEHICLE_MSG_C2V_SET_OFFSET_FROM_ROAD_CENTER = 0x2c,
+ ANKI_VEHICLE_MSG_V2C_OFFSET_FROM_ROAD_CENTER_UPDATE = 0x2d,
+
+ // Light Patterns
+ ANKI_VEHICLE_MSG_C2V_LIGHTS_PATTERN = 0x33,
+
+ // Vehicle Configuration Parameters
+ ANKI_VEHICLE_MSG_C2V_SET_CONFIG_PARAMS = 0x45,
+
+ // SDK Mode
+ ANKI_VEHICLE_MSG_C2V_SDK_MODE = 0x90,
+};
+
+#define ATTRIBUTE_PACKED __attribute__((packed))
+
+/**
+ * Basic vehicle message.
+ *
+ * - size: Size in bytes of the msg_id plus payload
+ * - msg_id: Identifier for message
+ * - payload: Optional message data for parameters or response info.
+ *
+ */
+typedef struct anki_vehicle_msg {
+ uint8_t size;
+ uint8_t msg_id;
+ uint8_t payload[ANKI_VEHICLE_MSG_PAYLOAD_MAX_SIZE];
+} anki_vehicle_msg_t;
+
+typedef struct anki_vehicle_msg_version_response {
+ uint8_t size;
+ uint8_t msg_id;
+ uint16_t version;
+} ATTRIBUTE_PACKED anki_vehicle_msg_version_response_t;
+#define ANKI_VEHICLE_MSG_V2C_VERSION_RESPONSE_SIZE 3
+
+typedef struct anki_vehicle_msg_battery_level_response {
+ uint8_t size;
+ uint8_t msg_id;
+ uint16_t battery_level;
+} ATTRIBUTE_PACKED anki_vehicle_msg_battery_level_response_t;
+#define ANKI_VEHICLE_MSG_V2C_BATTERY_LEVEL_RESPONSE_SIZE 3
+
+#define ANKI_VEHICLE_SDK_OPTION_OVERRIDE_LOCALIZATION 0x1
+typedef struct anki_vehicle_msg_sdk_mode {
+ uint8_t size;
+ uint8_t msg_id;
+ uint8_t on;
+ uint8_t flags;
+} ATTRIBUTE_PACKED anki_vehicle_msg_sdk_mode_t;
+#define ANKI_VEHICLE_MSG_SDK_MODE_SIZE 3
+
+typedef struct anki_vehicle_msg_set_speed {
+ uint8_t size;
+ uint8_t msg_id;
+ int16_t speed_mm_per_sec; // mm/sec
+ int16_t accel_mm_per_sec2; // mm/sec^2
+ uint8_t respect_road_piece_speed_limit;
+} ATTRIBUTE_PACKED anki_vehicle_msg_set_speed_t;
+#define ANKI_VEHICLE_MSG_C2V_SET_SPEED_SIZE 6
+
+typedef enum {
+ VEHICLE_TURN_NONE = 0,
+ VEHICLE_TURN_LEFT = 1,
+ VEHICLE_TURN_RIGHT = 2,
+ VEHICLE_TURN_UTURN = 3,
+ VEHICLE_TURN_UTURN_JUMP = 4,
+} anki_vehicle_turn_type_t;
+
+typedef enum {
+ VEHICLE_TURN_TRIGGER_IMMEDIATE = 0, // Run immediately
+ VEHICLE_TURN_TRIGGER_INTERSECTION = 1, // Run at the next intersection
+} anki_vehicle_turn_trigger_t;
+
+typedef struct anki_vehicle_msg_turn {
+ uint8_t size;
+ uint8_t msg_id;
+ uint8_t type;
+ uint8_t trigger;
+} ATTRIBUTE_PACKED anki_vehicle_msg_turn_t;
+#define ANKI_VEHICLE_MSG_C2V_TURN_SIZE 3
+
+typedef struct anki_vehicle_msg_set_offset_from_road_center {
+ uint8_t size;
+ uint8_t msg_id;
+ float offset_mm;
+} ATTRIBUTE_PACKED anki_vehicle_msg_set_offset_from_road_center_t;
+#define ANKI_VEHICLE_MSG_C2V_SET_OFFSET_FROM_ROAD_CENTER_SIZE 5
+
+typedef struct anki_vehicle_msg_change_lane {
+ uint8_t size;
+ uint8_t msg_id;
+ uint16_t horizontal_speed_mm_per_sec;
+ uint16_t horizontal_accel_mm_per_sec2;
+ float offset_from_road_center_mm;
+ uint8_t hop_intent;
+ uint8_t tag;
+} ATTRIBUTE_PACKED anki_vehicle_msg_change_lane_t;
+#define ANKI_VEHICLE_MSG_C2V_CHANGE_LANE_SIZE 11
+
+//
+// Bitwise masks applied to parsing_flags
+//
+// determine how many bits per code were read
+#define PARSEFLAGS_MASK_NUM_BITS 0x0f
+
+// determine if the track has an inverted code scheme
+#define PARSEFLAGS_MASK_INVERTED_COLOR 0x80
+
+// determine if the the code has been reverse parsed
+#define PARSEFLAGS_MASK_REVERSE_PARSING 0x40
+
+// determine if the current driving dir is reversed
+#define PARSEFLAGS_MASK_REVERSE_DRIVING 0x20
+
+typedef struct anki_vehicle_msg_localization_position_update {
+ uint8_t size;
+ uint8_t msg_id;
+ uint8_t location_id;
+ uint8_t road_piece_id;
+ float offset_from_road_center_mm;
+ uint16_t speed_mm_per_sec;
+ uint8_t parsing_flags;
+
+ /* ACK commands received */
+ uint8_t last_recv_lane_change_cmd_id;
+ uint8_t last_exec_lane_change_cmd_id;
+ uint16_t last_desired_horizontal_speed_mm_per_sec;
+ uint16_t last_desired_speed_mm_per_sec;
+} ATTRIBUTE_PACKED anki_vehicle_msg_localization_position_update_t;
+#define ANKI_VEHICLE_MSG_V2C_LOCALIZATION_POSITION_UPDATE_SIZE 16
+
+typedef enum anki_vehicle_driving_direction {
+ FORWARD = 0,
+ REVERSE = 1,
+} anki_vehicle_driving_direction_t;
+
+typedef struct anki_vehicle_msg_localization_transition_update {
+ uint8_t size;
+ uint8_t msg_id;
+ uint8_t road_piece_idx;
+ uint8_t road_piece_idx_prev;
+ float offset_from_road_center_mm;
+
+ uint8_t driving_direction;
+
+ /* ACK commands received */
+ uint8_t last_recv_lane_change_id;
+ uint8_t last_exec_lane_change_id;
+ uint16_t last_desired_horizontal_speed_mm_per_sec;
+ uint16_t last_desired_speed_mm_per_sec;
+
+ /* track grade detection */
+ uint8_t uphill_counter;
+ uint8_t downhill_counter;
+
+ /* wheel displacement (cm) since last transition bar */
+ uint8_t left_wheel_dist_cm;
+ uint8_t right_wheel_dist_cm;
+} ATTRIBUTE_PACKED anki_vehicle_msg_localization_transition_update_t;
+#define ANKI_VEHICLE_MSG_V2C_LOCALIZATION_TRANSITION_UPDATE_SIZE 18
+
+typedef enum {
+ INTERSECTION_CODE_ENTRY_FIRST,
+ INTERSECTION_CODE_EXIT_FIRST,
+ INTERSECTION_CODE_ENTRY_SECOND,
+ INTERSECTION_CODE_EXIT_SECOND,
+} anki_intersection_code_t;
+
+typedef struct anki_vehicle_msg_localization_intersection_update {
+ uint8_t size;
+ uint8_t msg_id;
+ uint8_t road_piece_idx;
+ float offset_from_road_center_mm;
+
+ uint8_t driving_direction;
+ uint8_t intersection_code;
+ uint8_t intersection_turn;
+ uint8_t is_exiting;
+} ATTRIBUTE_PACKED anki_vehicle_msg_localization_intersection_update_t;
+#define ANKI_VEHICLE_MSG_V2C_LOCALIZATION_INTERSECTION_UPDATE_SIZE 10
+
+typedef struct anki_vehicle_msg_offset_from_road_center_update {
+ uint8_t size;
+ uint8_t msg_id;
+ float offset_from_road_center_mm;
+ uint8_t lane_change_id;
+} ATTRIBUTE_PACKED anki_vehicle_msg_offset_from_road_center_update_t;
+#define ANKI_VEHICLE_MSG_V2C_OFFSET_FROM_ROAD_CENTER_UPDATE_SIZE 6
+
+// Lights
+// The bits in the simple light message (ANKI_VEHICLE_MSG_C2V_SET_LIGHTS) corresponding to
+// each type of light.
+#define LIGHT_HEADLIGHTS 0
+#define LIGHT_BRAKELIGHTS 1
+#define LIGHT_FRONTLIGHTS 2
+#define LIGHT_ENGINE 3
+
+// Helper macros for parsing lights bits
+#define LIGHT_ANKI_VEHICLE_MSG_IS_VALID(messageBits, LIGHT_ID) (((messageBits >> LIGHT_ID) & 1) == TRUE)
+#define LIGHT_ANKI_VEHICLE_MSG_GET_VALUE(messageBits, LIGHT_ID) ((messageBits >> (4 + LIGHT_ANKI_VEHICLE_MSG_HEADLIGHTS) & 1))
+
+typedef struct anki_vehicle_msg_set_lights {
+ uint8_t size;
+ uint8_t msg_id;
+ uint8_t light_mask; // Valid and value bits for lights (see above)
+} ATTRIBUTE_PACKED anki_vehicle_msg_set_lights_t;
+#define ANKI_VEHICLE_MSG_C2V_SET_LIGHTS_SIZE 2
+
+#define ANKI_VEHICLE_MAX_LIGHT_INTENSITY 14
+#define ANKI_VEHICLE_MAX_LIGHT_TIME 11
+
+// LED channel definitions - for RGB engine, front, and tail lights
+typedef enum {
+ LIGHT_RED,
+ LIGHT_TAIL,
+ LIGHT_BLUE,
+ LIGHT_GREEN,
+ LIGHT_FRONTL,
+ LIGHT_FRONTR,
+ LIGHT_COUNT
+} anki_vehicle_light_channel_t;
+
+// Below is a description of the various effects used in SetLight(...)
+typedef enum {
+ EFFECT_STEADY, // Simply set the light intensity to 'start' value
+ EFFECT_FADE, // Fade intensity from 'start' to 'end'
+ EFFECT_THROB, // Fade intensity from 'start' to 'end' and back to 'start'
+ EFFECT_FLASH, // Turn on LED between time 'start' and time 'end' inclusive
+ EFFECT_RANDOM, // Flash the LED erratically - ignoring start/end
+ EFFECT_COUNT
+} anki_vehicle_light_effect_t;
+
+typedef struct anki_vehicle_light_config {
+ uint8_t channel;
+ uint8_t effect;
+ uint8_t start;
+ uint8_t end;
+ uint8_t cycles_per_10_sec;
+} ATTRIBUTE_PACKED anki_vehicle_light_config_t;
+
+#define LIGHT_CHANNEL_COUNT_MAX 3
+typedef struct anki_vehicle_msg_lights_pattern {
+ uint8_t size;
+ uint8_t msg_id;
+ uint8_t channel_count;
+ anki_vehicle_light_config_t channel_config[LIGHT_CHANNEL_COUNT_MAX];
+} ATTRIBUTE_PACKED anki_vehicle_msg_lights_pattern_t;
+#define ANKI_VEHICLE_MSG_C2V_LIGHTS_PATTERN_SIZE 17
+
+typedef enum anki_track_material {
+ TRACK_MATERIAL_PLASTIC,
+ TRACK_MATERIAL_VINYL,
+} anki_track_material_t;
+
+#define SUPERCODE_NONE 0
+#define SUPERCODE_BOOST_JUMP 1
+#define SUPERCODE_ALL (SUPERCODE_BOOST_JUMP)
+
+typedef struct anki_vehicle_msg_set_config_params {
+ uint8_t size;
+ uint8_t msg_id;
+ uint8_t super_code_parse_mask;
+ uint8_t track_material;
+} ATTRIBUTE_PACKED anki_vehicle_msg_set_config_params_t;
+#define ANKI_VEHICLE_MSG_C2V_SET_CONFIG_PARAMS_SIZE 3
+
+/**
+ * Create a message for setting the SDK mode.
+ *
+ * Note that in order to set the speed and change lanes in the current SDK,
+ * the ANKI_VEHICLE_SDK_OPTION_OVERRIDE_LOCALIZATION flag must be set
+ * when enabling the SDK mode.
+ *
+ * @param msg A pointer to the vehicle message struct to be written.
+ * @param on Whether to turn SDK mode on (1) or off (0).
+ * @param flags Option flags to specify vehicle behaviors while SDK mode is enabled.
+ *
+ * @return size of bytes written to msg
+ */
+uint8_t anki_vehicle_msg_set_sdk_mode(anki_vehicle_msg_t *msg, uint8_t on, uint8_t flags);
+
+/**
+ * Create a message for setting the vehicle speed.
+ *
+ * @param msg A pointer to the vehicle message struct to be written.
+ * @param speed_mm_per_sec The requested vehicle speed in mm/sec.
+ * @param accel_mm_per_sec2 The acceleration in mm/sec^2.
+ *
+ * @return size of bytes written to msg
+ */
+uint8_t anki_vehicle_msg_set_speed(anki_vehicle_msg_t *msg,
+ uint16_t speed_mm_per_sec,
+ uint16_t accel_mm_per_sec2);
+
+/**
+ * Create a message for setting vehicle's internal offset from road center.
+ *
+ * This value is stored internally in the vehicle and is used during a
+ * lane change request to determine the target location. In the current
+ * version of the SDK, this message is always sent to set the current offset
+ * to zero before a lane change message. This allows the lane change to control
+ * the relative horizontal movement of the vehicle
+ *
+ * @param msg A pointer to the vehicle message struct to be written.
+ * @param offset_mm The offset from the road center in mm.
+ *
+ * @return size of bytes written to msg
+ *
+ * @see anki_vehicle_msg_change_lane
+ */
+uint8_t anki_vehicle_msg_set_offset_from_road_center(anki_vehicle_msg_t *msg, float offset_mm);
+
+/**
+ * Create a message to change the lane of driving vehicle.
+ *
+ * The vehicle must be moving in order for this command to be
+ * executed.
+ *
+ * @param msg A pointer to the vehicle message struct to be written.
+ * @param horizontal_speed_mm_per_sec The horizontal speed at for the lane change in mm/sec.
+ * @param horizontal_accel_mm_per_sec The horizontal acceleration for the lane change in mm/sec.
+ * @param offset_from_center_mm The target offset from the road center in mm.
+ *
+ * @return size of bytes written to msg
+ */
+uint8_t anki_vehicle_msg_change_lane(anki_vehicle_msg_t *msg,
+ uint16_t horizontal_speed_mm_per_sec,
+ uint16_t horizontal_accel_mm_per_sec2,
+ float offset_from_center_mm);
+
+/**
+ * Create a message to set vehicle light directly using a mask.
+ *
+ * @param msg A pointer to the vehicle message struct to be written.
+ * @param mask Mask byte representing the desired lights.
+ *
+ * @return size of bytes written to msg
+ *
+ * @see anki_vehicle_msg_set_lights_t
+ */
+uint8_t anki_vehicle_msg_set_lights(anki_vehicle_msg_t *msg, uint8_t mask);
+
+/**
+ * Create a vehicle lights configuration.
+ *
+ * @param config A pointer to the light channel configuration.
+ * @param channel The target lights. See anki_vehicle_light_channel_t.
+ * @param effect The type of desired effect. See anki_vehicle_light_effect_t.
+ * @param start The starting intensity of the LED.
+ * @param end The end intensity of the LED.
+ * @param cycles_per_min The frequency repeated start->end transition phases (according to effect).
+ *
+ * @see anki_vehicle_light_channel_t, anki_vehicle_light_effect_t
+ */
+void anki_vehicle_light_config(anki_vehicle_light_config_t *config,
+ anki_vehicle_light_channel_t channel,
+ anki_vehicle_light_effect_t effect,
+ uint8_t start,
+ uint8_t end,
+ uint16_t cycles_per_min);
+
+/**
+ * Create a message to set a vehicle lights pattern.
+ *
+ * @param msg A pointer to the vehicle message struct to be written.
+ * @param channel The target lights. See anki_vehicle_light_channel_t.
+ * @param effect The type of desired effect. See anki_vehicle_light_effect_t.
+ * @param start The starting intensity of the LED.
+ * @param end The end intensity of the LED.
+ * @param cycles_per_min The frequency repeated start->end transition phases (according to effect).
+ *
+ * @return size of bytes written to msg
+ *
+ * @see anki_vehicle_light_channel_t, anki_vehicle_light_effect_t
+ */
+uint8_t anki_vehicle_msg_lights_pattern(anki_vehicle_msg_t *message,
+ anki_vehicle_light_channel_t channel,
+ anki_vehicle_light_effect_t effect,
+ uint8_t start,
+ uint8_t end,
+ uint16_t cycles_per_min);
+
+/**
+ * Create a message to set vehicle lights using light channel configurations.
+ *
+ * Up to 3 channel configurations can be added to a single lights_pattern message.
+ *
+ * @param message A pointer to the vehicle message struct to be written.
+ * @param config A pointer to the light channel config to append to the message.
+ *
+ * @return size of appended config object or zero if nothing was appended.
+ */
+uint8_t anki_vehicle_msg_lights_pattern_append(anki_vehicle_msg_lights_pattern_t* message,
+ anki_vehicle_light_config_t* config);
+
+/**
+ * Create a message to request that the vehicle disconnect.
+ *
+ * This is often a more reliable way to disconnect compared to closing
+ * the connection to a vehicle from the central.
+ *
+ * @param msg A pointer to the vehicle message struct to be written.
+ *
+ * @return size of bytes written to msg
+ */
+uint8_t anki_vehicle_msg_disconnect(anki_vehicle_msg_t *msg);
+
+/**
+ * Create a message to send the vehicle a ping request.
+ *
+ * This will cause the vehicle to response with a message of type
+ * ANKI_VEHICLE_MSG_V2C_PING_RESPONSE.
+ *
+ * @param msg A pointer to the vehicle message struct to be written.
+ *
+ * @return size of bytes written to msg
+ */
+uint8_t anki_vehicle_msg_ping(anki_vehicle_msg_t *msg);
+
+/**
+ * Create a message to request the vehicle firmware version.
+ *
+ * The vehicle will response with a anki_vehicle_msg_version_response_t message.
+ *
+ * @param msg A pointer to the vehicle message struct to be written.
+ *
+ * @return size of bytes written to msg
+ */
+uint8_t anki_vehicle_msg_get_version(anki_vehicle_msg_t *msg);
+
+/**
+ * Create a message to request the vehicle battery level.
+ *
+ * The vehicle will respond with a anki_vehicle_msg_battery_level_response_t
+ * message.
+ *
+ * @param msg A pointer to the vehicle message struct to be written.
+ *
+ * @return size of bytes written to msg
+ */
+uint8_t anki_vehicle_msg_get_battery_level(anki_vehicle_msg_t *msg);
+
+/**
+ * Create a message to cancel a requested lane change.
+ *
+ * @param msg A pointer to the vehicle message struct to be written.
+ *
+ * @return size of bytes written to msg
+ */
+uint8_t anki_vehicle_msg_cancel_lane_change(anki_vehicle_msg_t *msg);
+
+/**
+ * Create a message to request a turn.
+ *
+ * @param msg A pointer to the vehicle message struct to be written.
+ * @param type Enum value specifying the type of turn to execute. (see `see anki_vehicle_turn_type_t`)
+ * The default value is `VEHICLE_TURN_TYPE_NONE`, which is a no-op (no turn executed).
+ * @param trigger Enum value specifying when to execute the turn. (see `anki_vehicle_turn_trigger_t`)
+ * The only supported value is currently `VEHICLE_TURN_TRIGGER_IMMEDIATE`,
+ * which causes the turn to be executed immediately.
+ *
+ * @return size of bytes written to msg
+ */
+uint8_t anki_vehicle_msg_turn(anki_vehicle_msg_t *msg,
+ anki_vehicle_turn_type_t type,
+ anki_vehicle_turn_trigger_t trigger);
+
+/**
+ * Create a message to request a 180 degree turn.
+ *
+ * @param msg A pointer to the vehicle message struct to be written.
+ *
+ * @return size of bytes written to msg
+ */
+uint8_t anki_vehicle_msg_turn_180(anki_vehicle_msg_t *msg);
+
+
+/**
+ * Create a message to set vehicle config parameters
+ *
+ * Specify parameters that modify scan-parsing or change the way that track codes are
+ * iterpreted.
+ *
+ * This message is experimental and may change in the future.
+ *
+ * @param msg A pointer to the vehicle message struct to be written.
+ * @param super_code_parse_mask Mask byte specifying super codes that should be parsed.
+ * @param track_material Enum value specifying the material on which codes are printed.
+ *
+ * @return size of bytes written to msg
+ */
+uint8_t anki_vehicle_msg_set_config_params(anki_vehicle_msg_t* msg,
+ uint8_t super_code_parse_mask,
+ anki_track_material_t track_material);
+
+ANKI_END_DECL
+
+#endif
diff --git a/drive-sdk/src/uuid.c b/drive-sdk/src/uuid.c
new file mode 100644
index 0000000..b3ee649
--- /dev/null
+++ b/drive-sdk/src/uuid.c
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2014 Anki, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stddef.h>
+#include <string.h>
+
+#include "uuid.h"
+
+int uuid128_cmp(const uuid128_t *a, const uuid128_t *b)
+{
+ return memcmp(a, b, sizeof(uuid128_t));
+}
diff --git a/drive-sdk/src/uuid.h b/drive-sdk/src/uuid.h
new file mode 100644
index 0000000..e93ca84
--- /dev/null
+++ b/drive-sdk/src/uuid.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2014 Anki, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_uuid_h
+#define INCLUDE_uuid_h
+
+#include <stdint.h>
+
+#include "common.h"
+
+ANKI_BEGIN_DECL
+
+struct uuid128 {
+ uint8_t byte0;
+ uint8_t byte1;
+ uint8_t byte2;
+ uint8_t byte3;
+ uint8_t byte4;
+ uint8_t byte5;
+ uint8_t byte6;
+ uint8_t byte7;
+ uint8_t byte8;
+ uint8_t byte9;
+ uint8_t byte10;
+ uint8_t byte11;
+ uint8_t byte12;
+ uint8_t byte13;
+ uint8_t byte14;
+ uint8_t byte15;
+};
+typedef struct uuid128 uuid128_t;
+
+int uuid128_cmp(const uuid128_t *a, const uuid128_t *b);
+
+ANKI_END_DECL
+
+#endif
diff --git a/drive-sdk/src/vehicle_gatt_profile.h b/drive-sdk/src/vehicle_gatt_profile.h
new file mode 100644
index 0000000..6ed0f25
--- /dev/null
+++ b/drive-sdk/src/vehicle_gatt_profile.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2014 Anki, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef INCLUDE_vehicle_gatt_profile_h
+#define INCLUDE_vehicle_gatt_profile_h
+
+#define ANKI_STR_SERVICE_UUID "BE15BEEF-6186-407E-8381-0BD89C4D8DF4"
+#define ANKI_SERVICE_UUID { 0xbe, 0x15, 0xbe, 0xef, 0x61, 0x68, 0x40, 0x7e, 0x83, 0x81, 0x0b, 0xd8, 0x9c, 0x4d, 0x8d, 0xf4 }
+#define ANKI_SERVICE_UUID_LE { 0xf4, 0x8d, 0x4d, 0x9c, 0xd8, 0x0b, 0x81, 0x83, 0x7e, 0x40, 0x86, 0x61, 0xef, 0xbe, 0x15, 0xbe }
+
+#define ANKI_STR_CHR_READ_UUID "BE15BEE0-6186-407E-8381-0BD89C4D8DF4"
+#define ANKI_CHR_READ_UUID { 0xbe, 0x15, 0xbe, 0xe0, 0x61, 0x68, 0x40, 0x7e, 0x83, 0x81, 0x0b, 0xd8, 0x9c, 0x4d, 0x8d, 0xf4 }
+
+#define ANKI_STR_CHR_WRITE_UUID "BE15BEE1-6186-407E-8381-0BD89C4D8DF4"
+#define ANKI_CHR_WRITE_UUID { 0xbe, 0x15, 0xbe, 0xe1, 0x61, 0x68, 0x40, 0x7e, 0x83, 0x81, 0x0b, 0xd8, 0x9c, 0x4d, 0x8d, 0xf4 }
+
+#endif