From 3c89fd52679c8ccebceb30294a4bd815b51ede19 Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Mon, 5 Dec 2016 11:22:26 +0100 Subject: Import Drive SDK --- drive-sdk/src/CMakeLists.txt | 22 ++ drive-sdk/src/advertisement.c | 148 ++++++++++ drive-sdk/src/advertisement.h | 145 +++++++++ drive-sdk/src/anki_util.c | 62 ++++ drive-sdk/src/anki_util.h | 29 ++ drive-sdk/src/common.h | 48 +++ drive-sdk/src/eir.c | 54 ++++ drive-sdk/src/eir.h | 56 ++++ drive-sdk/src/protocol.c | 246 +++++++++++++++ drive-sdk/src/protocol.h | 559 +++++++++++++++++++++++++++++++++++ drive-sdk/src/uuid.c | 25 ++ drive-sdk/src/uuid.h | 50 ++++ drive-sdk/src/vehicle_gatt_profile.h | 30 ++ 13 files changed, 1474 insertions(+) create mode 100644 drive-sdk/src/CMakeLists.txt create mode 100644 drive-sdk/src/advertisement.c create mode 100644 drive-sdk/src/advertisement.h create mode 100644 drive-sdk/src/anki_util.c create mode 100644 drive-sdk/src/anki_util.h create mode 100644 drive-sdk/src/common.h create mode 100644 drive-sdk/src/eir.c create mode 100644 drive-sdk/src/eir.h create mode 100644 drive-sdk/src/protocol.c create mode 100644 drive-sdk/src/protocol.h create mode 100644 drive-sdk/src/uuid.c create mode 100644 drive-sdk/src/uuid.h create mode 100644 drive-sdk/src/vehicle_gatt_profile.h (limited to 'drive-sdk/src') 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 +#include +#include + +#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 +#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 +#include +#include +#include +#include +#include + +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 +#include +#include + +#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 +#include + +#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 +#include +#include + +#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 + +#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 +#include + +#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 + +#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 -- cgit v1.2.3