mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-03-26 02:37:41 +00:00
btstack_defines: add Cycling Power service events
This commit is contained in:
parent
0b8dd114aa
commit
9fe70df8e9
14
src/ble/gatt-service/cycling_power_service.gatt
Normal file
14
src/ble/gatt-service/cycling_power_service.gatt
Normal file
@ -0,0 +1,14 @@
|
||||
// Specification Type org.bluetooth.service.cycling_power
|
||||
// https://www.bluetooth.com/api/gatt/xmlfile?xmlFileName=org.bluetooth.service.cycling_power.xml
|
||||
|
||||
// Cycling Power 1818
|
||||
PRIMARY_SERVICE, ORG_BLUETOOTH_SERVICE_CYCLING_POWER
|
||||
CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_CYCLING_POWER_MEASUREMENT, DYNAMIC | NOTIFY | BROADCAST,
|
||||
CLIENT_CHARACTERISTIC_CONFIGURATION, READ | WRITE,
|
||||
SERVER_CHARACTERISTIC_CONFIGURATION, READ | WRITE,
|
||||
CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_CYCLING_POWER_FEATURE, DYNAMIC | READ,
|
||||
CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_SENSOR_LOCATION, DYNAMIC | READ,
|
||||
CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_CYCLING_POWER_VECTOR, DYNAMIC | NOTIFY,
|
||||
CLIENT_CHARACTERISTIC_CONFIGURATION, READ | WRITE,
|
||||
CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_CYCLING_POWER_CONTROL_POINT, DYNAMIC | WRITE | INDICATE,
|
||||
CLIENT_CHARACTERISTIC_CONFIGURATION, READ | WRITE,
|
1344
src/ble/gatt-service/cycling_power_service_server.c
Normal file
1344
src/ble/gatt-service/cycling_power_service_server.c
Normal file
File diff suppressed because it is too large
Load Diff
224
src/ble/gatt-service/cycling_power_service_server.h
Normal file
224
src/ble/gatt-service/cycling_power_service_server.h
Normal file
@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Copyright (C) 2018 BlueKitchen GmbH
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at
|
||||
* contact@bluekitchen-gmbh.com
|
||||
*
|
||||
*/
|
||||
#ifndef __CYCLING_POWER_SERVICE_SERVER_H
|
||||
#define __CYCLING_POWER_SERVICE_SERVER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Implementation of the GATT Cycling Power Service Server
|
||||
*/
|
||||
|
||||
// *****************************************************************************
|
||||
/* GATT_SERVICE_SERVER_START(cycling_power_service_server){Cycling PowerService}
|
||||
*
|
||||
*/
|
||||
// *****************************************************************************
|
||||
/* GATT_SERVICE_SERVER_END */
|
||||
|
||||
/* API_START */
|
||||
#define CYCLING_POWER_MANUFACTURER_SPECIFIC_DATA_MAX_SIZE 16
|
||||
|
||||
typedef enum {
|
||||
CP_PEDAL_POWER_BALANCE_REFERENCE_UNKNOWN = 0,
|
||||
CP_PEDAL_POWER_BALANCE_REFERENCE_LEFT,
|
||||
CP_PEDAL_POWER_BALANCE_REFERENCE_NOT_SUPPORTED
|
||||
} cycling_power_pedal_power_balance_reference_t;
|
||||
|
||||
typedef enum {
|
||||
CP_TORQUE_SOURCE_WHEEL = 0,
|
||||
CP_TORQUE_SOURCE_CRANK,
|
||||
CP_TORQUE_SOURCE_NOT_SUPPORTED
|
||||
} cycling_power_torque_source_t;
|
||||
|
||||
typedef enum {
|
||||
CP_SENSOR_MEASUREMENT_CONTEXT_FORCE = 0,
|
||||
CP_SENSOR_MEASUREMENT_CONTEXT_TORQUE
|
||||
} cycling_power_sensor_measurement_context_t;
|
||||
|
||||
typedef enum {
|
||||
CP_DISTRIBUTED_SYSTEM_UNSPECIFIED = 0,
|
||||
CP_DISTRIBUTED_SYSTEM_NOT_SUPPORTED,
|
||||
CP_DISTRIBUTED_SYSTEM_SUPPORTED
|
||||
} cycling_power_distributed_system_t;
|
||||
|
||||
typedef enum {
|
||||
CP_MEASUREMENT_FLAG_PEDAL_POWER_BALANCE_PRESENT = 0,
|
||||
CP_MEASUREMENT_FLAG_PEDAL_POWER_BALANCE_REFERENCE, // 0 - unknown, 1 - left
|
||||
CP_MEASUREMENT_FLAG_ACCUMULATED_TORQUE_PRESENT,
|
||||
CP_MEASUREMENT_FLAG_ACCUMULATED_TORQUE_SOURCE, // 0 - wheel based, 1 - crank based
|
||||
CP_MEASUREMENT_FLAG_WHEEL_REVOLUTION_DATA_PRESENT,
|
||||
CP_MEASUREMENT_FLAG_CRANK_REVOLUTION_DATA_PRESENT,
|
||||
CP_MEASUREMENT_FLAG_EXTREME_FORCE_MAGNITUDES_PRESENT,
|
||||
CP_MEASUREMENT_FLAG_EXTREME_TORQUE_MAGNITUDES_PRESENT,
|
||||
CP_MEASUREMENT_FLAG_EXTREME_ANGLES_PRESENT,
|
||||
CP_MEASUREMENT_FLAG_TOP_DEAD_SPOT_ANGLE_PRESENT,
|
||||
CP_MEASUREMENT_FLAG_BOTTOM_DEAD_SPOT_ANGLE_PRESENT,
|
||||
CP_MEASUREMENT_FLAG_ACCUMULATED_ENERGY_PRESENT,
|
||||
CP_MEASUREMENT_FLAG_OFFSET_COMPENSATION_INDICATOR,
|
||||
CP_MEASUREMENT_FLAG_RESERVED
|
||||
} cycling_power_measurement_flag_t;
|
||||
|
||||
typedef enum {
|
||||
CP_INSTANTANEOUS_MEASUREMENT_DIRECTION_UNKNOWN = 0,
|
||||
CP_INSTANTANEOUS_MEASUREMENT_DIRECTION_TANGENTIAL_COMPONENT,
|
||||
CP_INSTANTANEOUS_MEASUREMENT_DIRECTION_RADIAL_COMPONENT,
|
||||
CP_INSTANTANEOUS_MEASUREMENT_DIRECTION_LATERAL_COMPONENT
|
||||
} cycling_power_instantaneous_measurement_direction_t;
|
||||
|
||||
typedef enum {
|
||||
CP_VECTOR_FLAG_CRANK_REVOLUTION_DATA_PRESENT = 0,
|
||||
CP_VECTOR_FLAG_FIRST_CRANK_MEASUREMENT_ANGLE_PRESENT,
|
||||
CP_VECTOR_FLAG_INSTANTANEOUS_FORCE_MAGNITUDE_ARRAY_PRESENT,
|
||||
CP_VECTOR_FLAG_INSTANTANEOUS_TORQUE_MAGNITUDE_ARRAY_PRESENT,
|
||||
CP_VECTOR_FLAG_INSTANTANEOUS_MEASUREMENT_DIRECTION = 4, // 2 bit
|
||||
CP_VECTOR_FLAG_RESERVED = 6
|
||||
} cycling_power_vector_flag_t;
|
||||
|
||||
typedef enum {
|
||||
CP_SENSOR_LOCATION_OTHER,
|
||||
CP_SENSOR_LOCATION_TOP_OF_SHOE,
|
||||
CP_SENSOR_LOCATION_IN_SHOE,
|
||||
CP_SENSOR_LOCATION_HIP,
|
||||
CP_SENSOR_LOCATION_FRONT_WHEEL,
|
||||
CP_SENSOR_LOCATION_LEFT_CRANK,
|
||||
CP_SENSOR_LOCATION_RIGHT_CRANK,
|
||||
CP_SENSOR_LOCATION_LEFT_PEDAL,
|
||||
CP_SENSOR_LOCATION_RIGHT_PEDAL,
|
||||
CP_SENSOR_LOCATION_FRONT_HUB,
|
||||
CP_SENSOR_LOCATION_REAR_DROPOUT,
|
||||
CP_SENSOR_LOCATION_CHAINSTAY,
|
||||
CP_SENSOR_LOCATION_REAR_WHEEL,
|
||||
CP_SENSOR_LOCATION_REAR_HUB,
|
||||
CP_SENSOR_LOCATION_CHEST,
|
||||
CP_SENSOR_LOCATION_SPIDER,
|
||||
CP_SENSOR_LOCATION_CHAIN_RING,
|
||||
CP_SENSOR_LOCATION_RESERVED
|
||||
} cycling_power_sensor_location_t;
|
||||
|
||||
typedef enum {
|
||||
CP_FEATURE_FLAG_PEDAL_POWER_BALANCE_SUPPORTED = 0,
|
||||
CP_FEATURE_FLAG_ACCUMULATED_TORQUE_SUPPORTED,
|
||||
CP_FEATURE_FLAG_WHEEL_REVOLUTION_DATA_SUPPORTED,
|
||||
CP_FEATURE_FLAG_CRANK_REVOLUTION_DATA_SUPPORTED,
|
||||
CP_FEATURE_FLAG_EXTREME_MAGNITUDES_SUPPORTED,
|
||||
CP_FEATURE_FLAG_EXTREME_ANGLES_SUPPORTED,
|
||||
CP_FEATURE_FLAG_TOP_AND_BOTTOM_DEAD_SPOT_ANGLE_SUPPORTED,
|
||||
CP_FEATURE_FLAG_ACCUMULATED_ENERGY_SUPPORTED,
|
||||
CP_FEATURE_FLAG_OFFSET_COMPENSATION_INDICATOR_SUPPORTED,
|
||||
CP_FEATURE_FLAG_OFFSET_COMPENSATION_SUPPORTED,
|
||||
CP_FEATURE_FLAG_CYCLING_POWER_MEASUREMENT_CHARACTERISTIC_CONTENT_MASKING_SUPPORTED,
|
||||
CP_FEATURE_FLAG_MULTIPLE_SENSOR_LOCATIONS_SUPPORTED,
|
||||
CP_FEATURE_FLAG_CRANK_LENGTH_ADJUSTMENT_SUPPORTED,
|
||||
CP_FEATURE_FLAG_CHAIN_LENGTH_ADJUSTMENT_SUPPORTED,
|
||||
CP_FEATURE_FLAG_CHAIN_WEIGHT_ADJUSTMENT_SUPPORTED,
|
||||
CP_FEATURE_FLAG_SPAN_LENGTH_ADJUSTMENT_SUPPORTED,
|
||||
CP_FEATURE_FLAG_SENSOR_MEASUREMENT_CONTEXT, // 0-force based, 1-torque based
|
||||
CP_FEATURE_FLAG_INSTANTANEOUS_MEASUREMENT_DIRECTION_SUPPORTED,
|
||||
CP_FEATURE_FLAG_FACTORY_CALIBRATION_DATE_SUPPORTED,
|
||||
CP_FEATURE_FLAG_ENHANCED_OFFSET_COMPENSATION_SUPPORTED,
|
||||
CP_FEATURE_FLAG_DISTRIBUTED_SYSTEM_SUPPORT = 20, // 0-unspecified, 1-not for use in distr. system, 2-used in distr. system, 3-reserved
|
||||
CP_FEATURE_FLAG_RESERVED = 22
|
||||
} cycling_power_feature_flag_t;
|
||||
|
||||
typedef enum {
|
||||
CP_CALIBRATION_STATUS_INCORRECT_CALIBRATION_POSITION = 0x01,
|
||||
CP_CALIBRATION_STATUS_MANUFACTURER_SPECIFIC_ERROR_FOLLOWS = 0xFF
|
||||
} cycling_power_calibration_status_t;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Init Server with ATT DB
|
||||
*/
|
||||
void cycling_power_service_server_init(uint32_t feature_flags,
|
||||
cycling_power_pedal_power_balance_reference_t reference, cycling_power_torque_source_t torque_source,
|
||||
cycling_power_sensor_location_t * supported_sensor_locations, uint16_t num_supported_sensor_locations,
|
||||
cycling_power_sensor_location_t current_sensor_location);
|
||||
/**
|
||||
* @brief Push update
|
||||
* @note triggers notifications if subscribed
|
||||
*/
|
||||
void cycling_power_service_server_update_values(void);
|
||||
|
||||
void cycling_power_server_enhanced_calibration_done(cycling_power_sensor_measurement_context_t measurement_type,
|
||||
uint16_t calibrated_value, uint16_t manufacturer_company_id,
|
||||
uint8_t num_manufacturer_specific_data, uint8_t * manufacturer_specific_data);
|
||||
|
||||
int cycling_power_get_measurement_adv(uint16_t adv_interval, uint8_t * value, uint16_t max_value_size);
|
||||
/**
|
||||
* @brief Register callback for the calibration.
|
||||
* @param callback
|
||||
*/
|
||||
void cycling_power_service_server_packet_handler(btstack_packet_handler_t callback);
|
||||
|
||||
void cycling_power_server_calibration_done(cycling_power_sensor_measurement_context_t measurement_type, uint16_t calibrated_value);
|
||||
|
||||
int cycling_power_service_server_set_factory_calibration_date(gatt_date_time_t date);
|
||||
void cycling_power_service_server_set_sampling_rate(uint8_t sampling_rate_hz);
|
||||
|
||||
void cycling_power_service_server_add_torque(int16_t torque_m);
|
||||
void cycling_power_service_server_add_wheel_revolution(int32_t wheel_revolution, uint16_t wheel_event_time_s);
|
||||
void cycling_power_service_server_add_crank_revolution(uint16_t crank_revolution, uint16_t crank_event_time_s);
|
||||
void cycling_power_service_add_energy(uint16_t energy_kJ);
|
||||
|
||||
void cycling_power_service_server_set_instantaneous_power(int16_t instantaneous_power_watt);
|
||||
void cycling_power_service_server_set_pedal_power_balance(uint8_t pedal_power_balance_percentage);
|
||||
void cycling_power_service_server_set_force_magnitude(int16_t min_force_magnitude_newton, int16_t max_force_magnitude_newton);
|
||||
void cycling_power_service_server_set_torque_magnitude(int16_t min_torque_magnitude_newton, int16_t max_torque_magnitude_newton);
|
||||
void cycling_power_service_server_set_angle(uint16_t min_angle_deg, uint16_t max_angle_deg);
|
||||
void cycling_power_service_server_set_top_dead_spot_angle(uint16_t top_dead_spot_angle_deg);
|
||||
void cycling_power_service_server_set_bottom_dead_spot_angle(uint16_t bottom_dead_spot_angle_deg);
|
||||
void cycling_power_service_server_set_force_magnitude_values(int force_magnitude_count, int16_t * force_magnitude_newton_array);
|
||||
void cycling_power_service_server_set_torque_magnitude_values(int torque_magnitude_count, int16_t * torque_magnitude_newton_array);
|
||||
void cycling_power_service_server_set_instantaneous_measurement_direction(cycling_power_instantaneous_measurement_direction_t direction);
|
||||
// send only in first packet, ignore during continuation
|
||||
void cycling_power_service_server_set_first_crank_measurement_angle(uint16_t first_crank_measurement_angle_deg);
|
||||
|
||||
uint16_t cycling_power_service_measurement_flags(void);
|
||||
uint8_t cycling_power_service_vector_flags(void);
|
||||
/* API_END */
|
||||
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
11
src/ble/gatt-service/cycling_speed_and_cadence_service.gatt
Normal file
11
src/ble/gatt-service/cycling_speed_and_cadence_service.gatt
Normal file
@ -0,0 +1,11 @@
|
||||
// Specification Type org.bluetooth.service.cycling_speed_and_cadence
|
||||
// https://www.bluetooth.com/api/gatt/xmlfile?xmlFileName=org.bluetooth.service.cycling_speed_and_cadence.xml
|
||||
|
||||
// Cycling Speed and Cadence 1816
|
||||
PRIMARY_SERVICE, ORG_BLUETOOTH_SERVICE_CYCLING_SPEED_AND_CADENCE
|
||||
CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_CSC_MEASUREMENT, DYNAMIC | NOTIFY,
|
||||
CLIENT_CHARACTERISTIC_CONFIGURATION, READ | WRITE,
|
||||
CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_CSC_FEATURE, DYNAMIC | READ,
|
||||
CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_SENSOR_LOCATION, DYNAMIC | READ,
|
||||
CHARACTERISTIC, ORG_BLUETOOTH_CHARACTERISTIC_SC_CONTROL_POINT, DYNAMIC | WRITE | INDICATE,
|
||||
CLIENT_CHARACTERISTIC_CONFIGURATION, READ | WRITE,
|
390
src/ble/gatt-service/cycling_speed_and_cadence_service_server.c
Normal file
390
src/ble/gatt-service/cycling_speed_and_cadence_service_server.c
Normal file
@ -0,0 +1,390 @@
|
||||
/*
|
||||
* Copyright (C) 2014 BlueKitchen GmbH
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at
|
||||
* contact@bluekitchen-gmbh.com
|
||||
*
|
||||
*/
|
||||
|
||||
#define __BTSTACK_FILE__ "cycling_speed_and_cadence_service_server.c"
|
||||
|
||||
|
||||
#include "bluetooth.h"
|
||||
#include "btstack_defines.h"
|
||||
#include "ble/att_db.h"
|
||||
#include "ble/att_server.h"
|
||||
#include "btstack_util.h"
|
||||
#include "bluetooth_gatt.h"
|
||||
#include "btstack_debug.h"
|
||||
#include "l2cap.h"
|
||||
|
||||
#include "ble/gatt-service/cycling_speed_and_cadence_service_server.h"
|
||||
|
||||
// error codes from cscs spec
|
||||
#define CSC_ERROR_CODE_PROCEDURE_ALREADY_IN_PROGRESS 0x80
|
||||
#define CSC_ERROR_CODE_CCC_DESCRIPTOR_IMPROPERLY_CONFIGURED 0x81
|
||||
|
||||
typedef enum {
|
||||
CSC_RESPONSE_VALUE_SUCCESS = 1,
|
||||
CSC_RESPONSE_VALUE_OP_CODE_NOT_SUPPORTED,
|
||||
CSC_RESPONSE_VALUE_INVALID_PARAMETER,
|
||||
CSC_RESPONSE_VALUE_OPERATION_FAILED
|
||||
} csc_response_value_t;
|
||||
|
||||
typedef struct {
|
||||
hci_con_handle_t con_handle;
|
||||
|
||||
uint8_t wheel_revolution_data_supported;
|
||||
uint8_t crank_revolution_data_supported;
|
||||
uint8_t multiple_sensor_locations_supported;
|
||||
|
||||
// characteristic: CSC Mesurement
|
||||
uint16_t measurement_value_handle;
|
||||
uint32_t cumulative_wheel_revolutions;
|
||||
uint16_t last_wheel_event_time; // Unit has a resolution of 1/1024s
|
||||
uint16_t cumulative_crank_revolutions;
|
||||
uint16_t last_crank_event_time; // Unit has a resolution of 1/1024s
|
||||
|
||||
// characteristic descriptor: Client Characteristic Configuration
|
||||
uint16_t measurement_client_configuration_descriptor_handle;
|
||||
uint16_t measurement_client_configuration_descriptor_notify;
|
||||
btstack_context_callback_registration_t measurement_callback;
|
||||
|
||||
// sensor locations bitmap
|
||||
uint16_t feature_handle;
|
||||
|
||||
// sensor locations
|
||||
uint16_t sensor_location_value_handle;
|
||||
cycling_speed_and_cadence_sensor_location_t sensor_location;
|
||||
uint32_t supported_sensor_locations;
|
||||
|
||||
// characteristic: Heart Rate Control Point
|
||||
uint16_t control_point_value_handle;
|
||||
uint16_t control_point_client_configuration_descriptor_handle;
|
||||
uint16_t control_point_client_configuration_descriptor_indicate;
|
||||
btstack_context_callback_registration_t control_point_callback;
|
||||
|
||||
csc_opcode_t request_opcode;
|
||||
csc_response_value_t response_value;
|
||||
} cycling_speed_and_cadence_t;
|
||||
|
||||
static att_service_handler_t cycling_speed_and_cadence_service;
|
||||
static cycling_speed_and_cadence_t cycling_speed_and_cadence;
|
||||
|
||||
static uint16_t cycling_speed_and_cadence_service_read_callback(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){
|
||||
UNUSED(con_handle);
|
||||
UNUSED(attribute_handle);
|
||||
UNUSED(offset);
|
||||
cycling_speed_and_cadence_t * instance = &cycling_speed_and_cadence;
|
||||
|
||||
if (attribute_handle == instance->measurement_client_configuration_descriptor_handle){
|
||||
if (buffer && buffer_size >= 2){
|
||||
little_endian_store_16(buffer, 0, instance->measurement_client_configuration_descriptor_notify);
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (attribute_handle == instance->control_point_client_configuration_descriptor_handle){
|
||||
if (buffer && buffer_size >= 2){
|
||||
little_endian_store_16(buffer, 0, instance->control_point_client_configuration_descriptor_indicate);
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (attribute_handle == instance->feature_handle){
|
||||
if (buffer && buffer_size >= 2){
|
||||
uint16_t feature = (instance->wheel_revolution_data_supported << CSC_FLAG_WHEEL_REVOLUTION_DATA_SUPPORTED);
|
||||
feature |= (instance->crank_revolution_data_supported << CSC_FLAG_CRANK_REVOLUTION_DATA_SUPPORTED);
|
||||
feature |= (instance->multiple_sensor_locations_supported << CSC_FLAG_MULTIPLE_SENSOR_LOCATIONS_SUPPORTED);
|
||||
little_endian_store_16(buffer, 0, feature);
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (attribute_handle == instance->sensor_location_value_handle){
|
||||
if (buffer && buffer_size >= 1){
|
||||
buffer[0] = instance->sensor_location;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cycling_speed_and_cadence_service_csc_measurement_can_send_now(void * context){
|
||||
cycling_speed_and_cadence_t * instance = (cycling_speed_and_cadence_t *) context;
|
||||
if (!instance){
|
||||
printf("instance is null (cycling_speed_and_cadence_service_csc_measurement_can_send_now)\n");
|
||||
return;
|
||||
}
|
||||
uint8_t flags = (instance->wheel_revolution_data_supported << CSC_FLAG_WHEEL_REVOLUTION_DATA_SUPPORTED);
|
||||
flags |= (instance->crank_revolution_data_supported << CSC_FLAG_CRANK_REVOLUTION_DATA_SUPPORTED);
|
||||
|
||||
uint8_t value[11];
|
||||
int pos = 0;
|
||||
|
||||
value[pos++] = flags;
|
||||
if (instance->wheel_revolution_data_supported){
|
||||
little_endian_store_32(value, pos, instance->cumulative_wheel_revolutions);
|
||||
pos += 4;
|
||||
little_endian_store_16(value, pos, instance->last_wheel_event_time);
|
||||
pos += 2;
|
||||
printf("send cumulative 0x%04x\n", instance->cumulative_wheel_revolutions);
|
||||
}
|
||||
|
||||
if (instance->crank_revolution_data_supported){
|
||||
little_endian_store_16(value, pos, instance->cumulative_crank_revolutions);
|
||||
pos += 2;
|
||||
little_endian_store_16(value, pos, instance->last_crank_event_time);
|
||||
pos += 2;
|
||||
}
|
||||
|
||||
att_server_notify(instance->con_handle, instance->measurement_value_handle, &value[0], pos);
|
||||
}
|
||||
|
||||
static void cycling_speed_and_cadence_service_response_can_send_now(void * context){
|
||||
cycling_speed_and_cadence_t * instance = (cycling_speed_and_cadence_t *) context;
|
||||
if (!instance){
|
||||
printf("instance is null (cycling_speed_and_cadence_service_response_can_send_now)\n");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t value[3 + sizeof(cycling_speed_and_cadence_sensor_location_t)];
|
||||
int pos = 0;
|
||||
value[pos++] = CSC_OPCODE_RESPONSE_CODE;
|
||||
value[pos++] = instance->request_opcode;
|
||||
value[pos++] = instance->response_value;
|
||||
switch (instance->request_opcode){
|
||||
case CSC_OPCODE_REQUEST_SUPPORTED_SENSOR_LOCATIONS:{
|
||||
int loc;
|
||||
for (loc = CSC_SERVICE_SENSOR_LOCATION_OTHER; loc < CSC_SERVICE_SENSOR_LOCATION_RESERVED-1; loc++){
|
||||
if (instance->supported_sensor_locations & (1 << loc)){
|
||||
value[pos++] = loc;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
csc_opcode_t temp_request_opcode = instance->request_opcode;
|
||||
instance->request_opcode = CSC_OPCODE_IDLE;
|
||||
|
||||
printf_hexdump(value, pos);
|
||||
uint8_t status = att_server_indicate(instance->con_handle, instance->control_point_value_handle, &value[0], pos);
|
||||
printf("att_server_indicate status 0x%02x\n", status);
|
||||
switch (temp_request_opcode){
|
||||
case CSC_OPCODE_SET_CUMULATIVE_VALUE:
|
||||
if (instance->response_value != CSC_RESPONSE_VALUE_SUCCESS) break;
|
||||
if (instance->measurement_client_configuration_descriptor_notify){
|
||||
instance->measurement_callback.callback = &cycling_speed_and_cadence_service_csc_measurement_can_send_now;
|
||||
instance->measurement_callback.context = (void*) instance;
|
||||
att_server_register_can_send_now_callback(&instance->measurement_callback, instance->con_handle);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int cycling_speed_and_cadence_service_write_callback(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size){
|
||||
UNUSED(con_handle);
|
||||
UNUSED(transaction_mode);
|
||||
UNUSED(offset);
|
||||
UNUSED(buffer_size);
|
||||
cycling_speed_and_cadence_t * instance = &cycling_speed_and_cadence;
|
||||
|
||||
// printf("cycling_speed_and_cadence_service_write_callback: attr handle 0x%02x\n", attribute_handle);
|
||||
if (attribute_handle == instance->measurement_client_configuration_descriptor_handle){
|
||||
if (buffer_size < 2){
|
||||
return ATT_ERROR_INVALID_OFFSET;
|
||||
}
|
||||
instance->measurement_client_configuration_descriptor_notify = little_endian_read_16(buffer, 0);
|
||||
instance->con_handle = con_handle;
|
||||
if (instance->measurement_client_configuration_descriptor_notify){
|
||||
printf("enable notification\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (attribute_handle == instance->control_point_client_configuration_descriptor_handle){
|
||||
if (buffer_size < 2){
|
||||
return ATT_ERROR_INVALID_OFFSET;
|
||||
}
|
||||
instance->control_point_client_configuration_descriptor_indicate = little_endian_read_16(buffer, 0);
|
||||
instance->con_handle = con_handle;
|
||||
if (instance->control_point_client_configuration_descriptor_indicate){
|
||||
printf("enable indication\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (attribute_handle == instance->control_point_value_handle){
|
||||
if (instance->control_point_client_configuration_descriptor_indicate == 0) return CSC_ERROR_CODE_CCC_DESCRIPTOR_IMPROPERLY_CONFIGURED;
|
||||
if (instance->request_opcode != CSC_OPCODE_IDLE) return CSC_ERROR_CODE_PROCEDURE_ALREADY_IN_PROGRESS;
|
||||
|
||||
instance->request_opcode = buffer[0];
|
||||
instance->response_value = CSC_RESPONSE_VALUE_SUCCESS;
|
||||
|
||||
switch (instance->request_opcode){
|
||||
case CSC_OPCODE_SET_CUMULATIVE_VALUE:
|
||||
if (instance->wheel_revolution_data_supported){
|
||||
instance->cumulative_wheel_revolutions = little_endian_read_32(buffer, 1);
|
||||
break;
|
||||
}
|
||||
instance->response_value = CSC_RESPONSE_VALUE_OPERATION_FAILED;
|
||||
break;
|
||||
case CSC_OPCODE_START_SENSOR_CALIBRATION:
|
||||
break;
|
||||
case CSC_OPCODE_UPDATE_SENSOR_LOCATION:
|
||||
if (instance->multiple_sensor_locations_supported){
|
||||
cycling_speed_and_cadence_sensor_location_t sensor_location = buffer[1];
|
||||
if (sensor_location >= CSC_SERVICE_SENSOR_LOCATION_RESERVED){
|
||||
instance->response_value = CSC_RESPONSE_VALUE_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
instance->sensor_location = sensor_location;
|
||||
break;
|
||||
}
|
||||
instance->response_value = CSC_RESPONSE_VALUE_OPERATION_FAILED;
|
||||
break;
|
||||
case CSC_OPCODE_REQUEST_SUPPORTED_SENSOR_LOCATIONS:
|
||||
break;
|
||||
default:
|
||||
instance->response_value = CSC_RESPONSE_VALUE_OP_CODE_NOT_SUPPORTED;
|
||||
break;
|
||||
}
|
||||
printf("control point, opcode %02x, response %02x\n", instance->request_opcode, instance->response_value);
|
||||
|
||||
if (instance->control_point_client_configuration_descriptor_indicate){
|
||||
instance->control_point_callback.callback = &cycling_speed_and_cadence_service_response_can_send_now;
|
||||
instance->control_point_callback.context = (void*) instance;
|
||||
att_server_register_can_send_now_callback(&instance->control_point_callback, instance->con_handle);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("heart_rate_service_read_callback, not handeled read on handle 0x%02x\n", attribute_handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cycling_speed_and_cadence_service_server_init(uint32_t supported_sensor_locations,
|
||||
uint8_t multiple_sensor_locations_supported, uint8_t wheel_revolution_data_supported, uint8_t crank_revolution_data_supported){
|
||||
cycling_speed_and_cadence_t * instance = &cycling_speed_and_cadence;
|
||||
|
||||
instance->wheel_revolution_data_supported = wheel_revolution_data_supported;
|
||||
instance->crank_revolution_data_supported = crank_revolution_data_supported;
|
||||
instance->multiple_sensor_locations_supported = multiple_sensor_locations_supported;
|
||||
instance->supported_sensor_locations = supported_sensor_locations;
|
||||
|
||||
instance->sensor_location = CSC_SERVICE_SENSOR_LOCATION_OTHER;
|
||||
|
||||
// get service handle range
|
||||
uint16_t start_handle = 0;
|
||||
uint16_t end_handle = 0xffff;
|
||||
int service_found = gatt_server_get_get_handle_range_for_service_with_uuid16(ORG_BLUETOOTH_SERVICE_CYCLING_SPEED_AND_CADENCE, &start_handle, &end_handle);
|
||||
if (!service_found){
|
||||
printf("no service found\n");
|
||||
return;
|
||||
}
|
||||
// // get CSC Mesurement characteristic value handle and client configuration handle
|
||||
instance->measurement_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_CSC_MEASUREMENT);
|
||||
instance->measurement_client_configuration_descriptor_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_CSC_MEASUREMENT);
|
||||
|
||||
// get CSC Feature characteristic value handle and client configuration handle
|
||||
instance->feature_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_CSC_FEATURE);
|
||||
|
||||
// get Body Sensor Location characteristic value handle and client configuration handle
|
||||
instance->sensor_location_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_SENSOR_LOCATION);
|
||||
|
||||
// get SC Control Point characteristic value handle and client configuration handle
|
||||
instance->control_point_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_SC_CONTROL_POINT);
|
||||
instance->control_point_client_configuration_descriptor_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid16(start_handle, end_handle, ORG_BLUETOOTH_CHARACTERISTIC_SC_CONTROL_POINT);
|
||||
printf("Measurement value handle 0x%02x\n", instance->measurement_value_handle);
|
||||
printf("Measurement Cfg value handle 0x%02x\n", instance->measurement_client_configuration_descriptor_handle);
|
||||
printf("Feature value handle 0x%02x\n", instance->feature_handle);
|
||||
printf("Sensor location value handle 0x%02x\n", instance->sensor_location_value_handle);
|
||||
printf("Control Point value handle 0x%02x\n", instance->control_point_value_handle);
|
||||
printf("Control P. Cfg. value handle 0x%02x\n", instance->control_point_client_configuration_descriptor_handle);
|
||||
|
||||
cycling_speed_and_cadence_service.start_handle = start_handle;
|
||||
cycling_speed_and_cadence_service.end_handle = end_handle;
|
||||
cycling_speed_and_cadence_service.read_callback = &cycling_speed_and_cadence_service_read_callback;
|
||||
cycling_speed_and_cadence_service.write_callback = &cycling_speed_and_cadence_service_write_callback;
|
||||
|
||||
att_server_register_service_handler(&cycling_speed_and_cadence_service);
|
||||
}
|
||||
|
||||
static void cycling_speed_and_cadence_service_calculate_cumulative_wheel_revolutions(int32_t revolutions_change){
|
||||
cycling_speed_and_cadence_t * instance = &cycling_speed_and_cadence;
|
||||
if (revolutions_change < 0){
|
||||
if (instance->cumulative_wheel_revolutions > -revolutions_change){
|
||||
instance->cumulative_wheel_revolutions += revolutions_change;
|
||||
} else {
|
||||
instance->cumulative_wheel_revolutions = 0;
|
||||
}
|
||||
} else {
|
||||
if (instance->cumulative_wheel_revolutions < 0xffffffff - revolutions_change){
|
||||
instance->cumulative_wheel_revolutions += revolutions_change;
|
||||
} else {
|
||||
instance->cumulative_wheel_revolutions = 0xffffffff;
|
||||
}
|
||||
}
|
||||
printf("cumulative 0x%04x, wheel revolution change %d\n", instance->cumulative_wheel_revolutions, revolutions_change);
|
||||
}
|
||||
|
||||
static void cycling_speed_and_cadence_service_calculate_cumulative_crank_revolutions(uint16_t revolutions_change){
|
||||
cycling_speed_and_cadence_t * instance = &cycling_speed_and_cadence;
|
||||
|
||||
if (instance->cumulative_crank_revolutions <= 0xffff - revolutions_change){
|
||||
instance->cumulative_crank_revolutions += revolutions_change;
|
||||
} else {
|
||||
instance->cumulative_crank_revolutions = 0xffff;
|
||||
}
|
||||
}
|
||||
|
||||
// The Cumulative Wheel Revolutions value may decrement (e.g. If the bicycle is rolled in reverse), but shall not decrease below 0void cycling_speed_and_cadence_service_add_wheel_revolutions(int32_t wheel_revolutions, uint16_t last_wheel_event_time){
|
||||
void cycling_speed_and_cadence_service_server_update_values(int32_t wheel_revolutions, uint16_t last_wheel_event_time, uint16_t crank_revolutions, uint16_t last_crank_event_time){
|
||||
cycling_speed_and_cadence_t * instance = &cycling_speed_and_cadence;
|
||||
|
||||
cycling_speed_and_cadence_service_calculate_cumulative_wheel_revolutions(wheel_revolutions);
|
||||
instance->last_wheel_event_time = last_wheel_event_time;
|
||||
|
||||
cycling_speed_and_cadence_service_calculate_cumulative_crank_revolutions(crank_revolutions);
|
||||
instance->last_wheel_event_time = last_crank_event_time;
|
||||
|
||||
if (instance->measurement_client_configuration_descriptor_notify){
|
||||
instance->measurement_callback.callback = &cycling_speed_and_cadence_service_csc_measurement_can_send_now;
|
||||
instance->measurement_callback.context = (void*) instance;
|
||||
// printf("cycling_speed_and_cadence_service_server_update_values instance %p, context %p\n", instance, instance->measurement_callback.context);
|
||||
att_server_register_can_send_now_callback(&instance->measurement_callback, instance->con_handle);
|
||||
}
|
||||
}
|
114
src/ble/gatt-service/cycling_speed_and_cadence_service_server.h
Normal file
114
src/ble/gatt-service/cycling_speed_and_cadence_service_server.h
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (C) 2018 BlueKitchen GmbH
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at
|
||||
* contact@bluekitchen-gmbh.com
|
||||
*
|
||||
*/
|
||||
#ifndef __CYCLING_SPEED_AND_CADENCE_SERVICE_SERVER_H
|
||||
#define __CYCLING_SPEED_AND_CADENCE_SERVICE_SERVER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Implementation of the GATT Cycling Speed and Cadence Service Server
|
||||
*/
|
||||
|
||||
// *****************************************************************************
|
||||
/* GATT_SERVICE_SERVER_START(cycling_speed_and_cadence_service_server){Cycling Speed and Cadence Service}
|
||||
*
|
||||
*/
|
||||
// *****************************************************************************
|
||||
/* GATT_SERVICE_SERVER_END */
|
||||
|
||||
/* API_START */
|
||||
|
||||
typedef enum {
|
||||
CSC_SERVICE_SENSOR_LOCATION_OTHER = 0,
|
||||
CSC_SERVICE_SENSOR_LOCATION_TOP_OF_SHOE,
|
||||
CSC_SERVICE_SENSOR_LOCATION_IN_SHOE,
|
||||
CSC_SERVICE_SENSOR_LOCATION_HIP,
|
||||
CSC_SERVICE_SENSOR_LOCATION_FRONT_WHEEL,
|
||||
CSC_SERVICE_SENSOR_LOCATION_LEFT_CRANK,
|
||||
CSC_SERVICE_SENSOR_LOCATION_RIGHT_CRANK,
|
||||
CSC_SERVICE_SENSOR_LOCATION_LEFT_PEDAL,
|
||||
CSC_SERVICE_SENSOR_LOCATION_RIGHT_PEDAL,
|
||||
CSC_SERVICE_SENSOR_LOCATION_FRONT_HUB,
|
||||
CSC_SERVICE_SENSOR_LOCATION_REAR_DROPOUT,
|
||||
CSC_SERVICE_SENSOR_LOCATION_CHAINSTAY,
|
||||
CSC_SERVICE_SENSOR_LOCATION_REAR_WHEEL,
|
||||
CSC_SERVICE_SENSOR_LOCATION_REAR_HUB,
|
||||
CSC_SERVICE_SENSOR_LOCATION_CHEST,
|
||||
CSC_SERVICE_SENSOR_LOCATION_SPIDER,
|
||||
CSC_SERVICE_SENSOR_LOCATION_CHAIN_RING,
|
||||
CSC_SERVICE_SENSOR_LOCATION_RESERVED
|
||||
} cycling_speed_and_cadence_sensor_location_t;
|
||||
|
||||
typedef enum {
|
||||
CSC_FLAG_WHEEL_REVOLUTION_DATA_SUPPORTED = 0,
|
||||
CSC_FLAG_CRANK_REVOLUTION_DATA_SUPPORTED,
|
||||
CSC_FLAG_MULTIPLE_SENSOR_LOCATIONS_SUPPORTED
|
||||
} csc_feature_flag_bit_t;
|
||||
|
||||
typedef enum {
|
||||
CSC_OPCODE_IDLE = 0,
|
||||
CSC_OPCODE_SET_CUMULATIVE_VALUE = 1,
|
||||
CSC_OPCODE_START_SENSOR_CALIBRATION,
|
||||
CSC_OPCODE_UPDATE_SENSOR_LOCATION,
|
||||
CSC_OPCODE_REQUEST_SUPPORTED_SENSOR_LOCATIONS,
|
||||
CSC_OPCODE_RESPONSE_CODE = 16
|
||||
} csc_opcode_t;
|
||||
|
||||
/**
|
||||
* @brief Init Server with ATT DB
|
||||
*/
|
||||
void cycling_speed_and_cadence_service_server_init(uint32_t supported_sensor_locations,
|
||||
uint8_t multiple_sensor_locations_supported, uint8_t wheel_revolution_data_supported, uint8_t crank_revolution_data_supported);
|
||||
|
||||
/**
|
||||
* @brief Update heart rate (unit: beats per minute)
|
||||
* @note triggers notifications if subscribed
|
||||
*/
|
||||
void cycling_speed_and_cadence_service_server_update_values(int32_t wheel_revolutions, uint16_t last_wheel_event_time, uint16_t crank_revolutions, uint16_t last_crank_event_time);
|
||||
|
||||
/* API_END */
|
||||
|
||||
#if defined __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -1019,6 +1019,7 @@ typedef uint8_t sm_key_t[16];
|
||||
#define HCI_EVENT_HID_META 0xEF
|
||||
#define HCI_EVENT_A2DP_META 0xF0
|
||||
#define HCI_EVENT_HIDS_META 0xF1
|
||||
#define HCI_EVENT_GATT_SERVICE_META 0xF2
|
||||
|
||||
// Potential other meta groups
|
||||
// #define HCI_EVENT_BNEP_META 0xxx
|
||||
@ -2190,4 +2191,27 @@ typedef uint8_t sm_key_t[16];
|
||||
* @param con_handle
|
||||
*/
|
||||
#define HIDS_SUBEVENT_EXIT_SUSPEND 0x09
|
||||
|
||||
/**
|
||||
* @format 1211
|
||||
* @param subevent_code
|
||||
* @param con_handle
|
||||
* @param measurement_type // 0 - force magnitude, 1 - torque magnitude, see cycling_power_sensor_measurement_context_t
|
||||
* @param is_enhanced
|
||||
*/
|
||||
#define GATT_SERVICE_SUBEVENT_CYCLING_POWER_START_CALIBRATION 0x01
|
||||
|
||||
/**
|
||||
* @format 12
|
||||
* @param subevent_code
|
||||
* @param con_handle
|
||||
*/
|
||||
#define GATT_SERVICE_SUBEVENT_CYCLING_POWER_BROADCAST_START 0x02
|
||||
|
||||
/**
|
||||
* @format 12
|
||||
* @param subevent_code
|
||||
* @param con_handle
|
||||
*/
|
||||
#define GATT_SERVICE_SUBEVENT_CYCLING_POWER_BROADCAST_STOP 0x03
|
||||
#endif
|
||||
|
@ -6838,6 +6838,60 @@ static inline uint16_t hids_subevent_exit_suspend_get_con_handle(const uint8_t *
|
||||
return little_endian_read_16(event, 3);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_BLE
|
||||
/**
|
||||
* @brief Get field con_handle from event GATT_SERVICE_SUBEVENT_CYCLING_POWER_START_CALIBRATION
|
||||
* @param event packet
|
||||
* @return con_handle
|
||||
* @note: btstack_type 2
|
||||
*/
|
||||
static inline uint16_t gatt_service_subevent_cycling_power_start_calibration_get_con_handle(const uint8_t * event){
|
||||
return little_endian_read_16(event, 3);
|
||||
}
|
||||
/**
|
||||
* @brief Get field measurement_type from event GATT_SERVICE_SUBEVENT_CYCLING_POWER_START_CALIBRATION
|
||||
* @param event packet
|
||||
* @return measurement_type
|
||||
* @note: btstack_type 1
|
||||
*/
|
||||
static inline uint8_t gatt_service_subevent_cycling_power_start_calibration_get_measurement_type(const uint8_t * event){
|
||||
return event[5];
|
||||
}
|
||||
/**
|
||||
* @brief Get field is_enhanced from event GATT_SERVICE_SUBEVENT_CYCLING_POWER_START_CALIBRATION
|
||||
* @param event packet
|
||||
* @return is_enhanced
|
||||
* @note: btstack_type 1
|
||||
*/
|
||||
static inline uint8_t gatt_service_subevent_cycling_power_start_calibration_get_is_enhanced(const uint8_t * event){
|
||||
return event[6];
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_BLE
|
||||
/**
|
||||
* @brief Get field con_handle from event GATT_SERVICE_SUBEVENT_CYCLING_POWER_BROADCAST_START
|
||||
* @param event packet
|
||||
* @return con_handle
|
||||
* @note: btstack_type 2
|
||||
*/
|
||||
static inline uint16_t gatt_service_subevent_cycling_power_broadcast_start_get_con_handle(const uint8_t * event){
|
||||
return little_endian_read_16(event, 3);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_BLE
|
||||
/**
|
||||
* @brief Get field con_handle from event GATT_SERVICE_SUBEVENT_CYCLING_POWER_BROADCAST_STOP
|
||||
* @param event packet
|
||||
* @return con_handle
|
||||
* @note: btstack_type 2
|
||||
*/
|
||||
static inline uint16_t gatt_service_subevent_cycling_power_broadcast_stop_get_con_handle(const uint8_t * event){
|
||||
return little_endian_read_16(event, 3);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* API_END */
|
||||
|
858
test/pts/csc_client_test.c
Normal file
858
test/pts/csc_client_test.c
Normal file
@ -0,0 +1,858 @@
|
||||
/*
|
||||
* Copyright (C) 2018 BlueKitchen GmbH
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at
|
||||
* contact@bluekitchen-gmbh.com
|
||||
*
|
||||
*/
|
||||
|
||||
#define __BTSTACK_FILE__ "csc_client_test.c"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "btstack.h"
|
||||
|
||||
// prototypes
|
||||
static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
|
||||
|
||||
typedef enum {
|
||||
TC_OFF,
|
||||
TC_IDLE,
|
||||
TC_W4_SCAN_RESULT,
|
||||
TC_W4_CONNECT,
|
||||
TC_W4_SERVICE_RESULT,
|
||||
TC_W4_CHARACTERISTIC_RESULT,
|
||||
TC_W4_CHARACTERISTIC_DESCRIPTOR_RESULT,
|
||||
TC_W4_HEART_RATE_MEASUREMENT_CHARACTERISTIC,
|
||||
TC_W4_ENABLE_NOTIFICATIONS_COMPLETE,
|
||||
TC_W4_ENABLE_INDICATIONS_COMPLETE,
|
||||
TC_W4_SENSOR_LOCATION_CHARACTERISTIC,
|
||||
TC_W4_SENSOR_LOCATION,
|
||||
TC_W4_WRITE_CHARACTERISTIC,
|
||||
TC_CONNECTED,
|
||||
TC_W4_DISCONNECT
|
||||
} gc_state_t;
|
||||
|
||||
static const char * sensor_location_string[] = {
|
||||
"other",
|
||||
"top of shoe",
|
||||
"in shoe",
|
||||
"hip",
|
||||
"front wheel",
|
||||
"left crank",
|
||||
"right crank",
|
||||
"left pedal",
|
||||
"right pedal",
|
||||
"front hub",
|
||||
"rear dropout",
|
||||
"chainstay",
|
||||
"rear wheel",
|
||||
"rear hub",
|
||||
"chest",
|
||||
"spider",
|
||||
"chain ring",
|
||||
"reserved"
|
||||
};
|
||||
|
||||
static const char * sensor_loc2str(cycling_speed_and_cadence_sensor_location_t index){
|
||||
if (index < CSC_SERVICE_SENSOR_LOCATION_RESERVED){
|
||||
return sensor_location_string[index];
|
||||
}
|
||||
return sensor_location_string[CSC_SERVICE_SENSOR_LOCATION_RESERVED];
|
||||
}
|
||||
|
||||
#define MAX_NUM_MEASUREMENTS 20
|
||||
#ifdef HAVE_BTSTACK_STDIN
|
||||
// pts:
|
||||
static const char * device_addr_string = "00:1B:DC:07:32:EF";
|
||||
#endif
|
||||
static bd_addr_t cmdline_addr = { };
|
||||
static int cmdline_addr_found = 0;
|
||||
|
||||
// addr and type of device with correct name
|
||||
static bd_addr_t csc_server_addr;
|
||||
static bd_addr_type_t csc_server_addr_type;
|
||||
|
||||
static hci_con_handle_t connection_handle;
|
||||
|
||||
static gatt_client_service_t service;
|
||||
static gatt_client_characteristic_t characteristic;
|
||||
static gatt_client_characteristic_t measurement_characteristic;
|
||||
static gatt_client_characteristic_t control_point_characteristic;
|
||||
|
||||
static gatt_client_characteristic_descriptor_t descriptor;
|
||||
|
||||
static gatt_client_notification_t notification_listener;
|
||||
static int notification_listener_registered;
|
||||
|
||||
static gatt_client_notification_t indication_listener;
|
||||
static int indication_listener_registered;
|
||||
|
||||
static gc_state_t state = TC_OFF;
|
||||
static btstack_packet_callback_registration_t hci_event_callback_registration;
|
||||
static btstack_packet_callback_registration_t sm_event_callback_registration;
|
||||
|
||||
static uint32_t cumulative_wheel_revolutions[MAX_NUM_MEASUREMENTS];
|
||||
static uint16_t last_wheel_event_time[MAX_NUM_MEASUREMENTS]; // Unit has a resolution of 1/1024s
|
||||
static uint16_t cumulative_crank_revolutions[MAX_NUM_MEASUREMENTS];
|
||||
static uint16_t last_crank_event_time[MAX_NUM_MEASUREMENTS]; // Unit has a resolution of 1/1024s
|
||||
static int wm_index = 0;
|
||||
static int cm_index = 0;
|
||||
|
||||
static btstack_timer_source_t indication_timer;
|
||||
|
||||
static int wheel_circumference_cm = 210;
|
||||
// returns 1 if name is found in advertisement
|
||||
static int advertisement_report_contains_uuid16(uint16_t uuid16, uint8_t * advertisement_report){
|
||||
// get advertisement from report event
|
||||
const uint8_t * adv_data = gap_event_advertising_report_get_data(advertisement_report);
|
||||
uint16_t adv_len = gap_event_advertising_report_get_data_length(advertisement_report);
|
||||
|
||||
// iterate over advertisement data
|
||||
ad_context_t context;
|
||||
int found = 0;
|
||||
for (ad_iterator_init(&context, adv_len, adv_data) ; ad_iterator_has_more(&context) ; ad_iterator_next(&context)){
|
||||
uint8_t data_type = ad_iterator_get_data_type(&context);
|
||||
uint8_t data_size = ad_iterator_get_data_len(&context);
|
||||
const uint8_t * data = ad_iterator_get_data(&context);
|
||||
int i;
|
||||
switch (data_type){
|
||||
case BLUETOOTH_DATA_TYPE_INCOMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS:
|
||||
case BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS:
|
||||
// compare common prefix
|
||||
for (i=0; i<data_size;i+=2){
|
||||
if (little_endian_read_16(data, i) == uuid16) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
#define INDICATION_TIMEOUT_MS 30000
|
||||
|
||||
static void csc_client_indication_timeout_handler(btstack_timer_source_t * timer){
|
||||
UNUSED(timer);
|
||||
// avdtp_connection_t * connection = (avdtp_connection_t *) btstack_run_loop_get_timer_context(timer);
|
||||
printf("Indication not received\n");
|
||||
}
|
||||
|
||||
static void csc_client_indication_timer_start(void){
|
||||
btstack_run_loop_remove_timer(&indication_timer);
|
||||
btstack_run_loop_set_timer_handler(&indication_timer, csc_client_indication_timeout_handler);
|
||||
// btstack_run_loop_set_timer_context(&indication_timer, connection);
|
||||
btstack_run_loop_set_timer(&indication_timer, INDICATION_TIMEOUT_MS);
|
||||
btstack_run_loop_add_timer(&indication_timer);
|
||||
}
|
||||
|
||||
static void csc_client_indication_timer_stop(void){
|
||||
btstack_run_loop_remove_timer(&indication_timer);
|
||||
}
|
||||
|
||||
|
||||
static float csc_client_calculate_instantaneous_speed_km_per_h(uint32_t * wheel_rotation, uint16_t * time){
|
||||
if (time[1] == time[0]) return 0;
|
||||
int16_t delta_time = time[1] - time[0];
|
||||
int32_t delta_wheel_rotation = wheel_rotation[1] - wheel_rotation[0];
|
||||
return delta_wheel_rotation * wheel_circumference_cm / (float)delta_time * 1024 * 3600 / 100000;
|
||||
}
|
||||
|
||||
static float csc_client_calculate_instantaneous_cadence_rpm(uint16_t * crank_revolution, uint16_t * time){
|
||||
if (time[1] == time[0]) return 0;
|
||||
int16_t delta_time = time[1] - time[0];
|
||||
int16_t delta_crank_revolution = crank_revolution[1] - crank_revolution[0];
|
||||
return delta_crank_revolution / (float)delta_time * 60 * 1024;
|
||||
}
|
||||
|
||||
static void handle_gatt_client_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
||||
UNUSED(packet_type);
|
||||
UNUSED(channel);
|
||||
UNUSED(size);
|
||||
|
||||
|
||||
switch(state){
|
||||
case TC_W4_SERVICE_RESULT:
|
||||
switch(hci_event_packet_get_type(packet)){
|
||||
case GATT_EVENT_SERVICE_QUERY_RESULT:
|
||||
// store service (we expect only one)
|
||||
gatt_event_service_query_result_get_service(packet, &service);
|
||||
printf("GATT Service:\n start handle 0x%02x, end handle 0x%02x\n", service.start_group_handle, service.end_group_handle);
|
||||
break;
|
||||
case GATT_EVENT_QUERY_COMPLETE:
|
||||
if (packet[4] != 0){
|
||||
printf("SERVICE_QUERY_RESULT - Error status %x.\n", packet[4]);
|
||||
gap_disconnect(connection_handle);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case TC_W4_CHARACTERISTIC_RESULT:
|
||||
switch(hci_event_packet_get_type(packet)){
|
||||
case GATT_EVENT_CHARACTERISTIC_QUERY_RESULT:
|
||||
gatt_event_characteristic_query_result_get_characteristic(packet, &characteristic);
|
||||
printf("GATT characteristic:\n attribute handle 0x%02x, properties 0x%02x, handle 0x%02x, uuid 0x%02x\n",
|
||||
characteristic.start_handle, characteristic.properties, characteristic.value_handle, characteristic.uuid16);
|
||||
switch(characteristic.uuid16){
|
||||
case ORG_BLUETOOTH_CHARACTERISTIC_CSC_MEASUREMENT:
|
||||
measurement_characteristic = characteristic;
|
||||
break;
|
||||
case ORG_BLUETOOTH_CHARACTERISTIC_SC_CONTROL_POINT:
|
||||
control_point_characteristic = characteristic;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case GATT_EVENT_QUERY_COMPLETE:
|
||||
if (packet[4] != 0){
|
||||
printf("CHARACTERISTIC_QUERY_RESULT - Error status %x.\n", packet[4]);
|
||||
gap_disconnect(connection_handle);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case TC_W4_CHARACTERISTIC_DESCRIPTOR_RESULT:
|
||||
switch(hci_event_packet_get_type(packet)){
|
||||
case GATT_EVENT_ALL_CHARACTERISTIC_DESCRIPTORS_QUERY_RESULT:
|
||||
gatt_event_all_characteristic_descriptors_query_result_get_characteristic_descriptor(packet, &descriptor);
|
||||
printf("GATT descriptors:\n handle 0x%02x, uuid 0x%02x\n",
|
||||
descriptor.handle, descriptor.uuid16);
|
||||
break;
|
||||
case GATT_EVENT_QUERY_COMPLETE:
|
||||
if (packet[4] != 0){
|
||||
printf("CHARACTERISTIC_QUERY_RESULT - Error status %x.\n", packet[4]);
|
||||
gap_disconnect(connection_handle);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case TC_W4_SENSOR_LOCATION:
|
||||
switch(hci_event_packet_get_type(packet)){
|
||||
case GATT_EVENT_CHARACTERISTIC_VALUE_QUERY_RESULT:{
|
||||
int value_len = gatt_event_characteristic_value_query_result_get_value_length(packet);
|
||||
const uint8_t * value = gatt_event_characteristic_value_query_result_get_value(packet);
|
||||
printf_hexdump(value, value_len);
|
||||
|
||||
switch (characteristic.uuid16){
|
||||
case ORG_BLUETOOTH_CHARACTERISTIC_HEART_RATE_MEASUREMENT:{
|
||||
break;
|
||||
}
|
||||
case ORG_BLUETOOTH_CHARACTERISTIC_SENSOR_LOCATION:
|
||||
// https://www.bluetooth.com/specifications/gatt/services
|
||||
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.body_sensor_location.xml
|
||||
printf("Body sensor location 0x%02x, name %s\n", value[0], sensor_loc2str(value[0]));
|
||||
break;
|
||||
case ORG_BLUETOOTH_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST:
|
||||
break;
|
||||
case ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING:
|
||||
break;
|
||||
case ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
state = TC_CONNECTED;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case TC_W4_ENABLE_NOTIFICATIONS_COMPLETE:
|
||||
switch(hci_event_packet_get_type(packet)){
|
||||
case GATT_EVENT_QUERY_COMPLETE:
|
||||
printf("Notifications enabled, status %02x\n", gatt_event_query_complete_get_status(packet));
|
||||
state = TC_CONNECTED;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case TC_W4_ENABLE_INDICATIONS_COMPLETE:
|
||||
switch(hci_event_packet_get_type(packet)){
|
||||
case GATT_EVENT_QUERY_COMPLETE:
|
||||
printf("Indications enabled, status %02x\n", gatt_event_query_complete_get_status(packet));
|
||||
state = TC_CONNECTED;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case TC_W4_WRITE_CHARACTERISTIC:
|
||||
switch(hci_event_packet_get_type(packet)){
|
||||
case GATT_EVENT_QUERY_COMPLETE:
|
||||
printf("Command done, status %02x\n", gatt_event_query_complete_get_status(packet));
|
||||
if (gatt_event_query_complete_get_status(packet) == ERROR_CODE_SUCCESS){
|
||||
csc_client_indication_timer_start();
|
||||
}
|
||||
state = TC_CONNECTED;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case TC_CONNECTED:
|
||||
switch(hci_event_packet_get_type(packet)){
|
||||
case GATT_EVENT_INDICATION:{
|
||||
uint16_t value_length = gatt_event_notification_get_value_length(packet);
|
||||
const uint8_t * value = gatt_event_notification_get_value(packet);
|
||||
printf("Indication value len %d, value: ", value_length);
|
||||
printf_hexdump(value, value_length);
|
||||
csc_client_indication_timer_stop();
|
||||
break;
|
||||
}
|
||||
case GATT_EVENT_NOTIFICATION:{
|
||||
// uint16_t value_handle = gatt_event_notification_get_value_handle(packet);
|
||||
// uint16_t value_length = gatt_event_notification_get_value_length(packet);
|
||||
const uint8_t * value = gatt_event_notification_get_value(packet);
|
||||
// printf("Notification handle 0x%04x, value len %d, value: ", value_handle, value_length);
|
||||
// printf_hexdump(value, value_length);
|
||||
int pos = 0;
|
||||
uint8_t wheel_revolution_data_supported = 0;
|
||||
uint8_t crank_revolution_data_supported = 0;
|
||||
|
||||
switch (characteristic.uuid16){
|
||||
case ORG_BLUETOOTH_CHARACTERISTIC_CSC_MEASUREMENT:{
|
||||
uint8_t flags = value[pos++];
|
||||
printf("flags 0x%02x\n", flags);
|
||||
|
||||
if (flags & (1 << CSC_FLAG_WHEEL_REVOLUTION_DATA_SUPPORTED)){
|
||||
wheel_revolution_data_supported = 1;
|
||||
}
|
||||
if (flags & (1 << CSC_FLAG_CRANK_REVOLUTION_DATA_SUPPORTED)){
|
||||
crank_revolution_data_supported = 1;
|
||||
}
|
||||
|
||||
if (wheel_revolution_data_supported){
|
||||
cumulative_wheel_revolutions[wm_index] = little_endian_read_32(value, pos);
|
||||
pos += 4;
|
||||
last_wheel_event_time[wm_index] = little_endian_read_16(value, pos);
|
||||
pos += 2;
|
||||
printf("wheel_revolution_data (0x%04x) = %d, time (0x%02x) = %f s\n", cumulative_wheel_revolutions[wm_index], cumulative_wheel_revolutions[wm_index],
|
||||
last_wheel_event_time[wm_index], last_wheel_event_time[wm_index]/1024.0);
|
||||
if (wm_index > 0){
|
||||
// Speed = (Difference in two successive Cumulative Wheel Revolution values * Wheel Circumference) / (Difference in two successive Last Wheel Event Time values)
|
||||
printf("instantaneous speed %f km/h\n", csc_client_calculate_instantaneous_speed_km_per_h(&cumulative_wheel_revolutions[wm_index-1], &last_wheel_event_time[wm_index-1]));
|
||||
}
|
||||
wm_index++;
|
||||
}
|
||||
|
||||
if (crank_revolution_data_supported){
|
||||
cumulative_crank_revolutions[cm_index] = little_endian_read_16(value, pos);
|
||||
pos += 2;
|
||||
last_crank_event_time[cm_index] = little_endian_read_16(value, pos);
|
||||
pos += 2;
|
||||
printf("crank_revolution_data (0x%04x) = %d, time (0x%02x) = %f s\n", cumulative_crank_revolutions[cm_index], cumulative_crank_revolutions[cm_index],
|
||||
last_crank_event_time[cm_index], last_crank_event_time[cm_index]/1024.0);
|
||||
|
||||
if (cm_index > 0){
|
||||
printf("instantaneous cadence %f rpm\n",
|
||||
csc_client_calculate_instantaneous_cadence_rpm(&cumulative_crank_revolutions[cm_index-1], &last_crank_event_time[cm_index-1]));
|
||||
}
|
||||
cm_index++;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ORG_BLUETOOTH_CHARACTERISTIC_CSC_FEATURE:
|
||||
printf("ORG_BLUETOOTH_CHARACTERISTIC_CSC_FEATURE\n");
|
||||
break;
|
||||
case ORG_BLUETOOTH_CHARACTERISTIC_SENSOR_LOCATION:{
|
||||
cycling_speed_and_cadence_sensor_location_t sensor_location = value[pos++];
|
||||
printf("Sensor location 0x%2x, name %s\n", sensor_location, sensor_loc2str(sensor_location));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
printf("uuid 0x%2x\n", characteristic.uuid16);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GATT_EVENT_QUERY_COMPLETE:
|
||||
break;
|
||||
case GATT_EVENT_CAN_WRITE_WITHOUT_RESPONSE:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void hci_event_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
||||
UNUSED(channel);
|
||||
UNUSED(size);
|
||||
|
||||
if (packet_type != HCI_EVENT_PACKET) return;
|
||||
bd_addr_t addr;
|
||||
uint16_t conn_interval;
|
||||
uint8_t event = hci_event_packet_get_type(packet);
|
||||
switch (event) {
|
||||
case SM_EVENT_JUST_WORKS_REQUEST:
|
||||
printf("Just Works requested\n");
|
||||
sm_just_works_confirm(sm_event_just_works_request_get_handle(packet));
|
||||
break;
|
||||
case SM_EVENT_NUMERIC_COMPARISON_REQUEST:
|
||||
printf("Confirming numeric comparison: %"PRIu32"\n", sm_event_numeric_comparison_request_get_passkey(packet));
|
||||
sm_numeric_comparison_confirm(sm_event_passkey_display_number_get_handle(packet));
|
||||
break;
|
||||
case SM_EVENT_PASSKEY_DISPLAY_NUMBER:
|
||||
printf("Display Passkey: %"PRIu32"\n", sm_event_passkey_display_number_get_passkey(packet));
|
||||
break;
|
||||
case SM_EVENT_IDENTITY_CREATED:
|
||||
sm_event_identity_created_get_identity_address(packet, addr);
|
||||
printf("Identity created: type %u address %s\n", sm_event_identity_created_get_identity_addr_type(packet), bd_addr_to_str(addr));
|
||||
break;
|
||||
case SM_EVENT_IDENTITY_RESOLVING_SUCCEEDED:
|
||||
sm_event_identity_resolving_succeeded_get_identity_address(packet, addr);
|
||||
printf("Identity resolved: type %u address %s\n", sm_event_identity_resolving_succeeded_get_identity_addr_type(packet), bd_addr_to_str(addr));
|
||||
break;
|
||||
case SM_EVENT_IDENTITY_RESOLVING_FAILED:
|
||||
sm_event_identity_created_get_address(packet, addr);
|
||||
printf("Identity resolving failed\n");
|
||||
break;
|
||||
case SM_EVENT_PAIRING_COMPLETE:
|
||||
switch (sm_event_pairing_complete_get_status(packet)){
|
||||
case ERROR_CODE_SUCCESS:
|
||||
printf("Pairing complete, success\n");
|
||||
break;
|
||||
case ERROR_CODE_CONNECTION_TIMEOUT:
|
||||
printf("Pairing failed, timeout\n");
|
||||
break;
|
||||
case ERROR_CODE_REMOTE_USER_TERMINATED_CONNECTION:
|
||||
printf("Pairing faileed, disconnected\n");
|
||||
break;
|
||||
case ERROR_CODE_AUTHENTICATION_FAILURE:
|
||||
printf("Pairing failed, reason = %u\n", sm_event_pairing_complete_get_reason(packet));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case BTSTACK_EVENT_STATE:
|
||||
// BTstack activated, get started
|
||||
if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING) {
|
||||
printf("btstack is powered on\n");;
|
||||
} else {
|
||||
state = TC_OFF;
|
||||
}
|
||||
break;
|
||||
case GAP_EVENT_ADVERTISING_REPORT:
|
||||
if (state != TC_W4_SCAN_RESULT) return;
|
||||
// check name in advertisement
|
||||
if (!advertisement_report_contains_uuid16(ORG_BLUETOOTH_SERVICE_HEART_RATE, packet)) return;
|
||||
// store address and type
|
||||
gap_event_advertising_report_get_address(packet, csc_server_addr);
|
||||
csc_server_addr_type = gap_event_advertising_report_get_address_type(packet);
|
||||
// stop scanning, and connect to the device
|
||||
state = TC_W4_CONNECT;
|
||||
gap_stop_scan();
|
||||
printf("Stop scan. Connect to device with addr %s.\n", bd_addr_to_str(csc_server_addr));
|
||||
gap_connect(csc_server_addr,csc_server_addr_type);
|
||||
break;
|
||||
case HCI_EVENT_LE_META:
|
||||
// wait for connection complete
|
||||
if (hci_event_le_meta_get_subevent_code(packet) != HCI_SUBEVENT_LE_CONNECTION_COMPLETE) break;
|
||||
if (state != TC_W4_CONNECT) return;
|
||||
connection_handle = hci_subevent_le_connection_complete_get_connection_handle(packet);
|
||||
// print connection parameters (without using float operations)
|
||||
conn_interval = hci_subevent_le_connection_complete_get_conn_interval(packet);
|
||||
printf("Connection Interval: %u.%02u ms\n", conn_interval * 125 / 100, 25 * (conn_interval & 3));
|
||||
printf("Connection Latency: %u\n", hci_subevent_le_connection_complete_get_conn_latency(packet));
|
||||
state = TC_CONNECTED;
|
||||
// TODO: find better way to re-register listeners. Re-register notification listener if Characteristic is known
|
||||
notification_listener_registered = 0;
|
||||
if (measurement_characteristic.uuid16 != 0){
|
||||
gatt_client_listen_for_characteristic_value_updates(¬ification_listener, handle_gatt_client_event, connection_handle, &measurement_characteristic);
|
||||
notification_listener_registered = 1;
|
||||
}
|
||||
|
||||
indication_listener_registered = 0;
|
||||
if (control_point_characteristic.uuid16 != 0){
|
||||
gatt_client_listen_for_characteristic_value_updates(&indication_listener, handle_gatt_client_event, connection_handle, &control_point_characteristic);
|
||||
indication_listener_registered = 1;
|
||||
}
|
||||
break;
|
||||
case HCI_EVENT_DISCONNECTION_COMPLETE:
|
||||
// unregister listener
|
||||
connection_handle = HCI_CON_HANDLE_INVALID;
|
||||
if (notification_listener_registered){
|
||||
notification_listener_registered = 0;
|
||||
gatt_client_stop_listening_for_characteristic_value_updates(¬ification_listener);
|
||||
}
|
||||
if (indication_listener_registered){
|
||||
indication_listener_registered = 0;
|
||||
gatt_client_stop_listening_for_characteristic_value_updates(&indication_listener);
|
||||
}
|
||||
printf("Disconnected %s\n", bd_addr_to_str(csc_server_addr));
|
||||
wm_index = 0;
|
||||
cm_index = 0;
|
||||
if (state == TC_OFF) break;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_BTSTACK_STDIN
|
||||
static void usage(const char *name){
|
||||
fprintf(stderr, "Usage: %s [-a|--address aa:bb:cc:dd:ee:ff]\n", name);
|
||||
fprintf(stderr, "If no argument is provided, GATT Heart Rate Client will start scanning and connect to the first device named 'LE Streamer'.\n");
|
||||
fprintf(stderr, "To connect to a specific device use argument [-a].\n\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_BTSTACK_STDIN
|
||||
|
||||
static void show_usage(void){
|
||||
bd_addr_t iut_address;
|
||||
gap_local_bd_addr(iut_address);
|
||||
printf("\n--- GATT Heart Rate Client Test Console %s ---\n", bd_addr_to_str(iut_address));
|
||||
printf("e - create connection to addr %s\n", device_addr_string);
|
||||
printf("E - disconnect\n");
|
||||
printf("b - start pairing\n");
|
||||
printf("a - start scanning\n");
|
||||
printf("\n");
|
||||
printf("P - search all primary services\n");
|
||||
printf("C - search all characteristics of last found primary service\n");
|
||||
printf("D - search all characteristic descriptors of last found characteristic\n");
|
||||
printf("\n");
|
||||
printf("H - search CSC service\n");
|
||||
printf("1 - search CSC_MEASUREMENT characteristic\n");
|
||||
printf("2 - search CSC FEATURE characteristic\n");
|
||||
printf("3 - search SENSOR_LOCATION characteristic\n");
|
||||
printf("4 - search CONTROL_POINT characteristic\n");
|
||||
printf("\n");
|
||||
printf("I - search GATT Device Information service\n");
|
||||
printf("x - search for 0x2a2a device info characteristic\n");
|
||||
printf("y - search for 0x2a25 device info characteristic\n");
|
||||
printf("z - search for 0x2a29 device info characteristic\n");
|
||||
printf(" \n");
|
||||
printf("F - search CSC feature\n");
|
||||
printf("f - search for 0x0075 handle\n");
|
||||
printf(" \n");
|
||||
|
||||
printf("r - read request for last found characteristic\n");
|
||||
printf("n - register notification for last found characteristic\n");
|
||||
printf("i - register indication for last found characteristic\n");
|
||||
printf(" \n");
|
||||
|
||||
printf("j - set cumulative wheel revolutions to 0 \n");
|
||||
printf("k - set cumulative wheel revolutions to 0xFFFF \n");
|
||||
printf("o - request supported sensor locations\n");
|
||||
printf("p - update sensor location to right pedal\n");
|
||||
printf(" \n");
|
||||
|
||||
printf("J - write unsupported opcode \n");
|
||||
printf("K - write invalid parameter \n");
|
||||
|
||||
printf(" \n");
|
||||
printf(" \n");
|
||||
printf("Ctrl-c - exit\n");
|
||||
printf("---\n");
|
||||
}
|
||||
|
||||
|
||||
static uint8_t csc_client_set_cumulative_wheel_revolutions(uint32_t wheel_revolutions){
|
||||
uint8_t event[20];
|
||||
int pos = 0;
|
||||
event[pos++] = CSC_OPCODE_SET_CUMULATIVE_VALUE; // opcode;
|
||||
little_endian_store_32(event, pos, wheel_revolutions);
|
||||
pos += 4;
|
||||
return gatt_client_write_value_of_characteristic(handle_gatt_client_event, connection_handle, control_point_characteristic.value_handle, pos, event);
|
||||
}
|
||||
|
||||
static uint8_t csc_client_update_sensor_location(cycling_speed_and_cadence_sensor_location_t sensor_location){
|
||||
uint8_t event[20];
|
||||
int pos = 0;
|
||||
event[pos++] = CSC_OPCODE_UPDATE_SENSOR_LOCATION; // opcode;
|
||||
event[pos++] = sensor_location;
|
||||
return gatt_client_write_value_of_characteristic(handle_gatt_client_event, connection_handle, control_point_characteristic.value_handle, pos, event);
|
||||
}
|
||||
|
||||
static uint8_t csc_client_request_supported_sensor_locations(){
|
||||
uint8_t event[20];
|
||||
int pos = 0;
|
||||
event[pos++] = CSC_OPCODE_REQUEST_SUPPORTED_SENSOR_LOCATIONS; // opcode;
|
||||
return gatt_client_write_value_of_characteristic(handle_gatt_client_event, connection_handle, control_point_characteristic.value_handle, pos, event);
|
||||
}
|
||||
|
||||
|
||||
static void stdin_process(char cmd){
|
||||
uint8_t status = ERROR_CODE_SUCCESS;
|
||||
switch (cmd){
|
||||
case 'e':
|
||||
printf("Connect to %s\n", device_addr_string);
|
||||
state = TC_W4_CONNECT;
|
||||
status = gap_connect(csc_server_addr, 0);
|
||||
break;
|
||||
case 'E':
|
||||
printf("Disconnect from %s\n", device_addr_string);
|
||||
state = TC_W4_DISCONNECT;
|
||||
status = gap_disconnect(connection_handle);
|
||||
break;
|
||||
case 'b':
|
||||
printf("Start pairing\n");
|
||||
sm_request_pairing(connection_handle);
|
||||
break;
|
||||
case 'a':
|
||||
printf("Start scanning!\n");
|
||||
state = TC_W4_SCAN_RESULT;
|
||||
gap_set_scan_parameters(0,0x0030, 0x0030);
|
||||
gap_start_scan();
|
||||
break;
|
||||
case 'P':
|
||||
printf("Search all primary services\n");
|
||||
state = TC_W4_SERVICE_RESULT;
|
||||
status = gatt_client_discover_primary_services(handle_gatt_client_event, connection_handle);
|
||||
break;
|
||||
case 'C':
|
||||
printf("Search all characteristics\n");
|
||||
state = TC_W4_CHARACTERISTIC_RESULT;
|
||||
status = gatt_client_discover_characteristics_for_service(handle_gatt_client_event, connection_handle, &service);
|
||||
break;
|
||||
case 'D':
|
||||
printf("Search all characteristic descriptors\n");
|
||||
state = TC_W4_CHARACTERISTIC_DESCRIPTOR_RESULT;
|
||||
status = gatt_client_discover_characteristic_descriptors(handle_gatt_client_event, connection_handle, &characteristic);
|
||||
break;
|
||||
case 'H':
|
||||
printf("Search CSC service\n");
|
||||
state = TC_W4_SERVICE_RESULT;
|
||||
status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, connection_handle, ORG_BLUETOOTH_SERVICE_CYCLING_SPEED_AND_CADENCE);
|
||||
break;
|
||||
|
||||
case '1':
|
||||
printf("Search for CSC_MEASUREMENT characteristic.\n");
|
||||
state = TC_W4_CHARACTERISTIC_RESULT;
|
||||
status = gatt_client_discover_characteristics_for_service_by_uuid16(handle_gatt_client_event, connection_handle, &service, ORG_BLUETOOTH_CHARACTERISTIC_CSC_MEASUREMENT);
|
||||
break;
|
||||
case '2':
|
||||
printf("Search for CSC_FEATURE characteristic.\n");
|
||||
state = TC_W4_CHARACTERISTIC_RESULT;
|
||||
status = gatt_client_discover_characteristics_for_service_by_uuid16(handle_gatt_client_event, connection_handle, &service, ORG_BLUETOOTH_CHARACTERISTIC_CSC_FEATURE);
|
||||
break;
|
||||
case '3':
|
||||
printf("Search for SENSOR_LOCATION characteristic.\n");
|
||||
state = TC_W4_CHARACTERISTIC_RESULT;
|
||||
status = gatt_client_discover_characteristics_for_service_by_uuid16(handle_gatt_client_event, connection_handle, &service, ORG_BLUETOOTH_CHARACTERISTIC_SENSOR_LOCATION);
|
||||
break;
|
||||
case '4':
|
||||
printf("Search for CONTROL_POINT characteristic.\n");
|
||||
state = TC_W4_CHARACTERISTIC_RESULT;
|
||||
status = gatt_client_discover_characteristics_for_service_by_uuid16(handle_gatt_client_event, connection_handle, &service, ORG_BLUETOOTH_CHARACTERISTIC_SC_CONTROL_POINT);
|
||||
break;
|
||||
|
||||
case 'I':
|
||||
printf("Search GATT Device Information service\n");
|
||||
state = TC_W4_SERVICE_RESULT;
|
||||
status = gatt_client_discover_primary_services_by_uuid16(handle_gatt_client_event, connection_handle, ORG_BLUETOOTH_SERVICE_DEVICE_INFORMATION);
|
||||
break;
|
||||
case 'x':
|
||||
printf("Search for 0x2a2a device info characteristic.\n");
|
||||
state = TC_W4_CHARACTERISTIC_RESULT;
|
||||
status = gatt_client_discover_characteristics_for_service_by_uuid16(handle_gatt_client_event, connection_handle, &service, ORG_BLUETOOTH_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST);
|
||||
break;
|
||||
case 'y':
|
||||
printf("Search for 0x2a25 device info characteristic.\n");
|
||||
state = TC_W4_CHARACTERISTIC_RESULT;
|
||||
status = gatt_client_discover_characteristics_for_service_by_uuid16(handle_gatt_client_event, connection_handle, &service, ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING);
|
||||
break;
|
||||
case 'z':
|
||||
printf("Search for 0x2a29 device info characteristic.\n");
|
||||
state = TC_W4_CHARACTERISTIC_RESULT;
|
||||
status = gatt_client_discover_characteristics_for_service_by_uuid16(handle_gatt_client_event, connection_handle, &service, ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING);
|
||||
break;
|
||||
|
||||
// case 'x':
|
||||
// printf("Search for 0x2a2a device info characteristic.\n");
|
||||
// state = TC_W4_CHARACTERISTIC_RESULT;
|
||||
// status = gatt_client_discover_characteristics_for_service_by_uuid16(handle_gatt_client_event, connection_handle, &service, ORG_BLUETOOTH_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST);
|
||||
// break;
|
||||
|
||||
case 'r':
|
||||
printf("Read request for characteristic 0x%4x, value handle 0x%4x.\n", characteristic.uuid16, characteristic.value_handle);
|
||||
state = TC_W4_SENSOR_LOCATION;
|
||||
status = gatt_client_read_value_of_characteristic(handle_gatt_client_event, connection_handle, &characteristic);
|
||||
break;
|
||||
case 'n':
|
||||
printf("Register notification handler for characteristic 0x%4x.\n", characteristic.uuid16);
|
||||
gatt_client_listen_for_characteristic_value_updates(¬ification_listener, handle_gatt_client_event, connection_handle, &characteristic);
|
||||
printf("Request Notify on characteristic 0x%4x.\n", characteristic.uuid16);
|
||||
state = TC_W4_ENABLE_NOTIFICATIONS_COMPLETE;
|
||||
status = gatt_client_write_client_characteristic_configuration(handle_gatt_client_event, connection_handle,
|
||||
&characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION);
|
||||
if (status == ERROR_CODE_SUCCESS){
|
||||
notification_listener_registered = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
printf("Register indication handler for characteristic 0x%4x.\n", characteristic.uuid16);
|
||||
gatt_client_listen_for_characteristic_value_updates(&indication_listener, handle_gatt_client_event, connection_handle, &characteristic);
|
||||
printf("Request Indicate on characteristic 0x%4x.\n", characteristic.uuid16);
|
||||
state = TC_W4_ENABLE_INDICATIONS_COMPLETE;
|
||||
status = gatt_client_write_client_characteristic_configuration(handle_gatt_client_event, connection_handle,
|
||||
&characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_INDICATION);
|
||||
if (status == ERROR_CODE_SUCCESS){
|
||||
indication_listener_registered = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'j':
|
||||
printf("set cumulative value to 0\n");
|
||||
state = TC_W4_WRITE_CHARACTERISTIC;
|
||||
status = csc_client_set_cumulative_wheel_revolutions(0);
|
||||
break;
|
||||
case 'k':
|
||||
printf("set cumulative value to 0xFFFF\n");
|
||||
state = TC_W4_WRITE_CHARACTERISTIC;
|
||||
status = csc_client_set_cumulative_wheel_revolutions(0xFFFF);
|
||||
break;
|
||||
case 'o':
|
||||
printf("request supported sensor locations\n");
|
||||
state = TC_W4_WRITE_CHARACTERISTIC;
|
||||
status = csc_client_request_supported_sensor_locations();
|
||||
break;
|
||||
case 'p':
|
||||
printf("update sensor location to right pedal\n");
|
||||
state = TC_W4_WRITE_CHARACTERISTIC;
|
||||
status = csc_client_update_sensor_location(CSC_SERVICE_SENSOR_LOCATION_RIGHT_PEDAL);
|
||||
break;
|
||||
|
||||
case 'J':{
|
||||
uint8_t value[] = {10};
|
||||
printf("write unsupported opcode\n");
|
||||
state = TC_W4_WRITE_CHARACTERISTIC;
|
||||
status = gatt_client_write_value_of_characteristic(handle_gatt_client_event, connection_handle, control_point_characteristic.value_handle, 1, value);
|
||||
break;
|
||||
}
|
||||
case 'K':
|
||||
printf("write invalid param\n");
|
||||
state = TC_W4_WRITE_CHARACTERISTIC;
|
||||
status = csc_client_update_sensor_location(CSC_SERVICE_SENSOR_LOCATION_RESERVED);
|
||||
break;
|
||||
|
||||
case '\n':
|
||||
case '\r':
|
||||
break;
|
||||
default:
|
||||
show_usage();
|
||||
break;
|
||||
}
|
||||
if (status != ERROR_CODE_SUCCESS){
|
||||
printf("GATT cmd \'%c\' failed, status 0x%02x\n", cmd, status);
|
||||
state = TC_CONNECTED;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int btstack_main(int argc, const char * argv[]);
|
||||
int btstack_main(int argc, const char * argv[]){
|
||||
|
||||
#ifdef HAVE_BTSTACK_STDIN
|
||||
int arg = 1;
|
||||
cmdline_addr_found = 0;
|
||||
|
||||
while (arg < argc) {
|
||||
if(!strcmp(argv[arg], "-a") || !strcmp(argv[arg], "--address")){
|
||||
arg++;
|
||||
cmdline_addr_found = sscanf_bd_addr(argv[arg], cmdline_addr);
|
||||
arg++;
|
||||
if (!cmdline_addr_found) exit(1);
|
||||
continue;
|
||||
}
|
||||
usage(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
#endif
|
||||
|
||||
hci_event_callback_registration.callback = &hci_event_handler;
|
||||
hci_add_event_handler(&hci_event_callback_registration);
|
||||
|
||||
l2cap_init();
|
||||
|
||||
gatt_client_init();
|
||||
|
||||
le_device_db_init();
|
||||
|
||||
sm_init();
|
||||
sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT);
|
||||
sm_set_authentication_requirements(SM_AUTHREQ_BONDING);
|
||||
sm_event_callback_registration.callback = &hci_event_handler;
|
||||
sm_add_event_handler(&sm_event_callback_registration);
|
||||
|
||||
#ifdef HAVE_BTSTACK_STDIN
|
||||
// parse human readable Bluetooth address
|
||||
sscanf_bd_addr(device_addr_string, csc_server_addr);
|
||||
btstack_stdin_setup(stdin_process);
|
||||
#endif
|
||||
|
||||
// turn on!
|
||||
hci_power_control(HCI_POWER_ON);
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* EXAMPLE_END */
|
153
test/pts/csc_server_test.c
Normal file
153
test/pts/csc_server_test.c
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (C) 2014 BlueKitchen GmbH
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at
|
||||
* contact@bluekitchen-gmbh.com
|
||||
*
|
||||
*/
|
||||
|
||||
#define __BTSTACK_FILE__ "csc_server_test.c"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "csc_server_test.h"
|
||||
#include "btstack.h"
|
||||
|
||||
#define WHEEL_REVOLUTION_DATA_SUPPORTED 1
|
||||
#define CRANK_REVOLUTION_DATA_SUPPORTED 1
|
||||
#define MULTIPLE_SENSOR_LOCATIONS_SUPPORTED 1
|
||||
|
||||
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml
|
||||
// cycling / speed and cadence sensor
|
||||
static const uint16_t appearance = (18 << 6) | 5;
|
||||
|
||||
const uint8_t adv_data[] = {
|
||||
// Flags general discoverable, BR/EDR not supported
|
||||
0x02, 0x01, 0x06,
|
||||
// Name
|
||||
0x0B, 0x09, 'C', 'S', 'C', ' ', 'S', 'e', 'r', 'v', 'e','r',
|
||||
// 16-bit Service UUIDs
|
||||
0x03, BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, ORG_BLUETOOTH_SERVICE_CYCLING_SPEED_AND_CADENCE & 0xff, ORG_BLUETOOTH_SERVICE_CYCLING_SPEED_AND_CADENCE >> 8,
|
||||
// Appearance
|
||||
3, BLUETOOTH_DATA_TYPE_APPEARANCE, appearance & 0xff, appearance >> 8,
|
||||
};
|
||||
|
||||
const uint8_t adv_data_len = sizeof(adv_data);
|
||||
|
||||
static int32_t wheel_revolutions = 0;
|
||||
static uint16_t last_wheel_event_time = 0;
|
||||
static uint16_t crank_revolutions = 0;
|
||||
static uint16_t last_crank_event_time = 0;
|
||||
|
||||
#ifdef HAVE_BTSTACK_STDIN
|
||||
|
||||
static void show_usage(void){
|
||||
bd_addr_t iut_address;
|
||||
gap_local_bd_addr(iut_address);
|
||||
printf("\n--- Bluetooth CSCS Server Test Console %s ---\n", bd_addr_to_str(iut_address));
|
||||
printf("f - update forward wheel revolution\n");
|
||||
printf("r - update reverse wheel revolution\n");
|
||||
printf("c - update crank revolution\n");
|
||||
printf("Ctrl-c - exit\n");
|
||||
printf("---\n");
|
||||
}
|
||||
|
||||
|
||||
static void stdin_process(char cmd){
|
||||
last_wheel_event_time += 10;
|
||||
last_crank_event_time += 10;
|
||||
|
||||
switch (cmd){
|
||||
case '\n':
|
||||
case '\r':
|
||||
break;
|
||||
case 'f':
|
||||
wheel_revolutions = 10;
|
||||
crank_revolutions = 10;
|
||||
cycling_speed_and_cadence_service_server_update_values(wheel_revolutions, last_wheel_event_time, crank_revolutions, last_crank_event_time);
|
||||
break;
|
||||
case 'r':
|
||||
wheel_revolutions = -10;
|
||||
crank_revolutions = 10;
|
||||
cycling_speed_and_cadence_service_server_update_values(wheel_revolutions, last_wheel_event_time, crank_revolutions, last_crank_event_time);
|
||||
break;
|
||||
case 'c':
|
||||
wheel_revolutions = 0;
|
||||
crank_revolutions = 0;
|
||||
cycling_speed_and_cadence_service_server_update_values(wheel_revolutions, last_wheel_event_time, crank_revolutions, last_crank_event_time);
|
||||
break;
|
||||
default:
|
||||
show_usage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int btstack_main(void);
|
||||
int btstack_main(void){
|
||||
l2cap_init();
|
||||
|
||||
// setup le device db
|
||||
le_device_db_init();
|
||||
|
||||
// setup SM: Display only
|
||||
sm_init();
|
||||
|
||||
// setup ATT server
|
||||
att_server_init(profile_data, NULL, NULL);
|
||||
|
||||
// setup heart rate service
|
||||
cycling_speed_and_cadence_service_server_init(CSC_SERVICE_SENSOR_LOCATION_TOP_OF_SHOE,
|
||||
MULTIPLE_SENSOR_LOCATIONS_SUPPORTED, WHEEL_REVOLUTION_DATA_SUPPORTED, CRANK_REVOLUTION_DATA_SUPPORTED);
|
||||
|
||||
// setup advertisements
|
||||
uint16_t adv_int_min = 0x0030;
|
||||
uint16_t adv_int_max = 0x0030;
|
||||
uint8_t adv_type = 0;
|
||||
bd_addr_t null_addr;
|
||||
memset(null_addr, 0, 6);
|
||||
gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00);
|
||||
gap_advertisements_set_data(adv_data_len, (uint8_t*) adv_data);
|
||||
gap_advertisements_enable(1);
|
||||
|
||||
#ifdef HAVE_BTSTACK_STDIN
|
||||
btstack_stdin_setup(stdin_process);
|
||||
#endif
|
||||
// turn on!
|
||||
hci_power_control(HCI_POWER_ON);
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* EXAMPLE_END */
|
5
test/pts/csc_server_test.gatt
Normal file
5
test/pts/csc_server_test.gatt
Normal file
@ -0,0 +1,5 @@
|
||||
PRIMARY_SERVICE, GAP_SERVICE
|
||||
CHARACTERISTIC, GAP_DEVICE_NAME, READ, "CSC Server"
|
||||
|
||||
// add Cycling Speed and Cadence Service
|
||||
#import <cycling_speed_and_cadence_service.gatt>
|
501
test/pts/cycling_power_server_test.c
Normal file
501
test/pts/cycling_power_server_test.c
Normal file
@ -0,0 +1,501 @@
|
||||
/*
|
||||
* Copyright (C) 2014 BlueKitchen GmbH
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* 4. Any redistribution, use, or modification is done solely for
|
||||
* personal benefit and not for any commercial purpose or for
|
||||
* monetary gain.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
||||
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Please inquire about commercial licensing options at
|
||||
* contact@bluekitchen-gmbh.com
|
||||
*
|
||||
*/
|
||||
|
||||
#define __BTSTACK_FILE__ "cycling_power_server_test.c"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cycling_power_server_test.h"
|
||||
#include "btstack.h"
|
||||
|
||||
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml
|
||||
// cycling / cycling power sensor
|
||||
static const uint16_t appearance = (18 << 6) | 4;
|
||||
static uint16_t con_handle;
|
||||
|
||||
static uint16_t event_time_s = 0;
|
||||
static uint16_t force_magnitude_newton = 0;
|
||||
static uint16_t torque_magnitude_newton_m = 0;
|
||||
static uint16_t angle_deg = 0;
|
||||
|
||||
static uint8_t broadcast_adv[31];
|
||||
static uint16_t adv_int_min = 0x0030;
|
||||
static uint16_t adv_int_max = 0x0030;
|
||||
static btstack_packet_callback_registration_t hci_event_callback_registration;
|
||||
|
||||
|
||||
const uint8_t adv_data[] = {
|
||||
// Flags general discoverable, BR/EDR not supported
|
||||
0x02, BLUETOOTH_DATA_TYPE_FLAGS, 0x06,
|
||||
// Name
|
||||
0x0E, BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME, 'C', 'y', 'c', 'l', 'i', 'n', 'g', '_', 'P', 'o', 'w', 'e', 'r',
|
||||
// 16-bit Service UUIDs
|
||||
0x03, BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, ORG_BLUETOOTH_SERVICE_CYCLING_POWER & 0xff, ORG_BLUETOOTH_SERVICE_CYCLING_POWER >> 8,
|
||||
// Appearance
|
||||
3, BLUETOOTH_DATA_TYPE_APPEARANCE, appearance & 0xff, appearance >> 8
|
||||
};
|
||||
|
||||
const uint8_t adv_data_len = sizeof(adv_data);
|
||||
|
||||
static cycling_power_sensor_location_t supported_sensor_locations[] = {
|
||||
CP_SENSOR_LOCATION_TOP_OF_SHOE,
|
||||
CP_SENSOR_LOCATION_IN_SHOE,
|
||||
CP_SENSOR_LOCATION_HIP,
|
||||
CP_SENSOR_LOCATION_FRONT_WHEEL,
|
||||
CP_SENSOR_LOCATION_LEFT_CRANK,
|
||||
CP_SENSOR_LOCATION_RIGHT_CRANK
|
||||
};
|
||||
static uint16_t num_supported_sensor_locations = 6;
|
||||
static gatt_date_time_t calibration_date = {2018, 1, 1, 13, 40, 50};
|
||||
static cycling_power_sensor_measurement_context_t measurement_type;
|
||||
static int enhanced_calibration = 0;
|
||||
static uint16_t manufacturer_company_id = 0xf0f0; // SIG assigned numbers
|
||||
static uint8_t numb_manufacturer_specific_data = CYCLING_POWER_MANUFACTURER_SPECIFIC_DATA_MAX_SIZE;
|
||||
static uint8_t manufacturer_specific_data[] = {
|
||||
0x11, 0x11, 0x11, 0x11,
|
||||
0x22, 0x22, 0x22, 0x22,
|
||||
0x33, 0x33, 0x33, 0x33,
|
||||
0x44, 0x44, 0x44, 0x44
|
||||
};
|
||||
|
||||
#ifdef HAVE_BTSTACK_STDIN
|
||||
// static char * measurement_flag_str[] = {
|
||||
// "1 Pedal Power Balance",
|
||||
// "2 Pedal Power Balance Reference", // Unknown/Left
|
||||
// "2 Accumulated Torque", // Wheel Based/Crank
|
||||
// "2 Accumulated Torque Source", // Wheel Based/Crank
|
||||
// "4 2 Wheel Revolution Data",
|
||||
// "2 2 Crank Revolution Data",
|
||||
// "2 2 Extreme Force Magnitudes",
|
||||
// "2 2 Extreme Torque Magnitudes",
|
||||
// "3 Extreme Angles",
|
||||
// "2 Top Dead Spot Angle",
|
||||
// "2 Bottom Dead Spot Angle",
|
||||
// "2 Accumulated Energy",
|
||||
// "Offset Compensation Indicator"
|
||||
// };
|
||||
|
||||
// static char buffer[80];
|
||||
|
||||
// static char * measurement_flag2str(cycling_power_measurement_flag_t flag, uint8_t value){
|
||||
// if (flag >= CP_MEASUREMENT_FLAG_RESERVED) return "Reserved";
|
||||
|
||||
// strcpy(buffer, measurement_flag_str[flag]);
|
||||
// int pos = strlen(measurement_flag_str[flag]);
|
||||
// // printf(" copy %d\n", pos);
|
||||
// switch (flag){
|
||||
// case CP_MEASUREMENT_FLAG_PEDAL_POWER_BALANCE_REFERENCE:
|
||||
// if (value == 0){
|
||||
// strcpy(buffer + pos, ": Unknown");
|
||||
// } else {
|
||||
// strcpy(buffer + pos, ": Left");
|
||||
// }
|
||||
// break;
|
||||
// case CP_MEASUREMENT_FLAG_ACCUMULATED_TORQUE_PRESENT:
|
||||
// if (value == 0){
|
||||
// strcpy(buffer + pos, ": Wheel Based");
|
||||
// } else {
|
||||
// strcpy(buffer + pos, ": Crank Based");
|
||||
// }
|
||||
// break;
|
||||
// case CP_MEASUREMENT_FLAG_ACCUMULATED_TORQUE_SOURCE:
|
||||
// if (value == 0){
|
||||
// strcpy(buffer + pos, ": Wheel Based");
|
||||
// } else {
|
||||
// strcpy(buffer + pos, ": Crank Based");
|
||||
// }
|
||||
// break;
|
||||
// default:
|
||||
// if (value == 0){
|
||||
// strcpy(buffer + pos, ": NOT SUPPORTED");
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
// return &buffer[0];
|
||||
// }
|
||||
|
||||
static void dump_feature_flags(uint32_t feature_flags){
|
||||
int i;
|
||||
printf("feature flags: \n");
|
||||
for (i = 0; i < CP_FEATURE_FLAG_RESERVED; i++){
|
||||
printf("%02d ", i);
|
||||
}
|
||||
printf("\n");
|
||||
for (i = 0; i < CP_FEATURE_FLAG_RESERVED; i++){
|
||||
uint8_t value = (feature_flags & (1 << i)) != 0;
|
||||
printf("%2d ", value);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void dump_measurement_flags(uint16_t measurement_flags){
|
||||
int i;
|
||||
printf("measurement flags: \n");
|
||||
// for (i = 0; i < CP_MEASUREMENT_FLAG_RESERVED; i++){
|
||||
// printf("%02d ", i);
|
||||
// }
|
||||
// printf("\n");
|
||||
for (i = 0; i < CP_MEASUREMENT_FLAG_RESERVED; i++){
|
||||
uint8_t value = (measurement_flags & (1 << i)) != 0;
|
||||
printf("%2d ", value);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void show_usage(void){
|
||||
bd_addr_t iut_address;
|
||||
gap_local_bd_addr(iut_address);
|
||||
printf("\n--- Bluetooth Cycling Power Server Test Console %s ---\n", bd_addr_to_str(iut_address));
|
||||
printf("u - push update\n");
|
||||
printf("\n");
|
||||
printf("t - add positive torque\n");
|
||||
printf("T - add negative torque\n");
|
||||
printf("w - add wheel revolution\n");
|
||||
printf("c - add crank revolution\n");
|
||||
printf("e - add energy\n");
|
||||
printf("\n");
|
||||
printf("p - set instantaneous power\n");
|
||||
printf("b - set power balance percentage\n");
|
||||
printf("m - set force magnitude\n");
|
||||
printf("M - set torque magnitude\n");
|
||||
printf("a - set angle\n");
|
||||
printf("x - set top dead spot angle\n");
|
||||
printf("y - set bottom dead spot angle\n");
|
||||
printf("R - reset values\n");
|
||||
printf("z - stop calibration\n");
|
||||
printf("Z - incorrect calibration position\n");
|
||||
printf("Y - Invalid param\n");
|
||||
printf("X - start calibration\n");
|
||||
|
||||
printf("\n");
|
||||
printf("Ctrl-c - exit\n");
|
||||
printf("---\n");
|
||||
}
|
||||
|
||||
static void cps_reset_values(void){
|
||||
event_time_s = 0;
|
||||
force_magnitude_newton = 0;
|
||||
torque_magnitude_newton_m = 0;
|
||||
angle_deg = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void stdin_process(char cmd){
|
||||
switch (cmd){
|
||||
case 'C':
|
||||
printf("reset all values\n");
|
||||
cps_reset_values();
|
||||
break;
|
||||
case 'u':{
|
||||
printf("push update\n");
|
||||
cycling_power_service_server_update_values();
|
||||
event_time_s++;
|
||||
break;
|
||||
}
|
||||
case 'r':
|
||||
printf("add positive torque\n");
|
||||
cycling_power_service_server_add_torque(100);
|
||||
break;
|
||||
case 'R':
|
||||
printf("add negative torque\n");
|
||||
cycling_power_service_server_add_torque(-100);
|
||||
break;
|
||||
case 'w':
|
||||
printf("add wheel revolution\n");
|
||||
cycling_power_service_server_add_wheel_revolution(10, event_time_s);
|
||||
break;
|
||||
case 'W':
|
||||
printf("reverse wheel revolution\n");
|
||||
cycling_power_service_server_add_wheel_revolution(-10, event_time_s);
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
printf("add crank revolution\n");
|
||||
cycling_power_service_server_add_crank_revolution(10, event_time_s);
|
||||
break;
|
||||
case 'e':
|
||||
printf("add energy\n");
|
||||
cycling_power_service_add_energy(100);
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
printf("set instantaneous power\n");
|
||||
cycling_power_service_server_set_instantaneous_power(100);
|
||||
break;
|
||||
case 'b':
|
||||
printf("set power balance percentage\n");
|
||||
cycling_power_service_server_set_pedal_power_balance(50);
|
||||
break;
|
||||
case 'm':
|
||||
force_magnitude_newton += 10;
|
||||
printf("set force magnitude\n");
|
||||
cycling_power_service_server_set_force_magnitude(force_magnitude_newton-5, force_magnitude_newton+5);
|
||||
break;
|
||||
case 'M':
|
||||
torque_magnitude_newton_m += 10;
|
||||
printf("set torque magnitude\n");
|
||||
cycling_power_service_server_set_torque_magnitude(torque_magnitude_newton_m-5, torque_magnitude_newton_m+5);
|
||||
break;
|
||||
case 'a':
|
||||
angle_deg += 10;
|
||||
printf("set angle\n");
|
||||
cycling_power_service_server_set_angle(angle_deg-5, angle_deg+5);
|
||||
break;
|
||||
case 'x':
|
||||
printf("set top dead spot angle\n");
|
||||
cycling_power_service_server_set_top_dead_spot_angle(180);
|
||||
break;
|
||||
case 'y':
|
||||
printf("set bottom dead spot angle\n");
|
||||
cycling_power_service_server_set_bottom_dead_spot_angle(20);
|
||||
break;
|
||||
case 'Y':
|
||||
printf("Invalid parameter\n");
|
||||
uint16_t calibrated_value = 0;
|
||||
if (enhanced_calibration){
|
||||
cycling_power_server_enhanced_calibration_done(3, calibrated_value,
|
||||
manufacturer_company_id, numb_manufacturer_specific_data, manufacturer_specific_data);
|
||||
} else {
|
||||
cycling_power_server_calibration_done(2, calibrated_value);
|
||||
}
|
||||
break;
|
||||
case 'z':{
|
||||
printf("stop calibration\n");
|
||||
switch (measurement_type){
|
||||
case CP_SENSOR_MEASUREMENT_CONTEXT_FORCE:
|
||||
calibrated_value = force_magnitude_newton;
|
||||
break;
|
||||
case CP_SENSOR_MEASUREMENT_CONTEXT_TORQUE:
|
||||
calibrated_value = torque_magnitude_newton_m;
|
||||
break;
|
||||
default:
|
||||
printf("wrong measurement type\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (enhanced_calibration){
|
||||
printf(" enhanced calibration on\n");
|
||||
cycling_power_server_enhanced_calibration_done(measurement_type, calibrated_value,
|
||||
manufacturer_company_id, numb_manufacturer_specific_data, manufacturer_specific_data);
|
||||
} else {
|
||||
// printf("cycling_power_server_calibration_done, data %d \n", calibrated_value);
|
||||
cycling_power_server_calibration_done(measurement_type, calibrated_value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'Z':
|
||||
printf("stop calibration, incorrect calibration position\n");
|
||||
if (enhanced_calibration){
|
||||
printf(" enhanced calibration on\n");
|
||||
cycling_power_server_enhanced_calibration_done(measurement_type, CP_CALIBRATION_STATUS_INCORRECT_CALIBRATION_POSITION,
|
||||
manufacturer_company_id, numb_manufacturer_specific_data, manufacturer_specific_data);
|
||||
} else {
|
||||
cycling_power_server_calibration_done(measurement_type, CP_CALIBRATION_STATUS_INCORRECT_CALIBRATION_POSITION);
|
||||
}
|
||||
|
||||
break;
|
||||
case 't':
|
||||
printf("disconnect \n");
|
||||
// gap_advertisements_enable(0);
|
||||
gap_disconnect(con_handle);
|
||||
break;
|
||||
case '\n':
|
||||
case '\r':
|
||||
break;
|
||||
default:
|
||||
show_usage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
||||
UNUSED(channel);
|
||||
UNUSED(size);
|
||||
int pos;
|
||||
bd_addr_t null_addr;
|
||||
|
||||
if (packet_type != HCI_EVENT_PACKET) return;
|
||||
|
||||
switch (hci_event_packet_get_type(packet)){
|
||||
case HCI_EVENT_LE_META:
|
||||
switch (hci_event_le_meta_get_subevent_code(packet)){
|
||||
case HCI_SUBEVENT_LE_CONNECTION_COMPLETE:
|
||||
con_handle = hci_subevent_le_connection_complete_get_connection_handle(packet);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case HCI_EVENT_GATT_SERVICE_META:
|
||||
switch (packet[2]){
|
||||
case GATT_SERVICE_SUBEVENT_CYCLING_POWER_START_CALIBRATION:
|
||||
measurement_type = gatt_service_subevent_cycling_power_start_calibration_get_measurement_type(packet);
|
||||
enhanced_calibration = gatt_service_subevent_cycling_power_start_calibration_get_is_enhanced(packet);
|
||||
break;
|
||||
|
||||
case GATT_SERVICE_SUBEVENT_CYCLING_POWER_BROADCAST_START:
|
||||
printf("start broadcast\n");
|
||||
// set ADV_NONCONN_IND
|
||||
pos = cycling_power_get_measurement_adv(adv_int_max, &broadcast_adv[0], sizeof(broadcast_adv));
|
||||
memset(null_addr, 0, 6);
|
||||
gap_advertisements_set_params(adv_int_min, adv_int_max, 0x03, 0, null_addr, 0x07, 0x00);
|
||||
gap_advertisements_set_data(pos, (uint8_t*) broadcast_adv);
|
||||
gap_advertisements_enable(1);
|
||||
break;
|
||||
case GATT_SERVICE_SUBEVENT_CYCLING_POWER_BROADCAST_STOP:
|
||||
printf("stop broadcast\n");
|
||||
gap_advertisements_enable(0);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// uint8_t event = hci_event_packet_get_type(packet);
|
||||
}
|
||||
|
||||
int btstack_main(void);
|
||||
int btstack_main(void){
|
||||
hci_event_callback_registration.callback = &packet_handler;
|
||||
hci_add_event_handler(&hci_event_callback_registration);
|
||||
|
||||
l2cap_init();
|
||||
|
||||
// setup le device db
|
||||
le_device_db_init();
|
||||
|
||||
// setup SM: Display only
|
||||
sm_init();
|
||||
|
||||
// setup ATT server
|
||||
att_server_init(profile_data, NULL, NULL);
|
||||
|
||||
// setup heart rate service
|
||||
// cycling_power_service_server_init(0x2FFFFF, 0x1F, CP_PEDAL_POWER_BALANCE_REFERENCE_LEFT);
|
||||
|
||||
uint32_t feature_flags = 0;
|
||||
feature_flags |= (1 << CP_FEATURE_FLAG_PEDAL_POWER_BALANCE_SUPPORTED);
|
||||
feature_flags |= (1 << CP_FEATURE_FLAG_ACCUMULATED_TORQUE_SUPPORTED);
|
||||
// feature_flags |= (1 << CP_FEATURE_FLAG_WHEEL_REVOLUTION_DATA_SUPPORTED);
|
||||
// feature_flags |= (1 << CP_FEATURE_FLAG_CRANK_REVOLUTION_DATA_SUPPORTED);
|
||||
feature_flags |= (1 << CP_FEATURE_FLAG_EXTREME_ANGLES_SUPPORTED);
|
||||
// feature_flags |= (1 << CP_FEATURE_FLAG_TOP_AND_BOTTOM_DEAD_SPOT_ANGLE_SUPPORTED);
|
||||
feature_flags |= (1 << CP_FEATURE_FLAG_ACCUMULATED_ENERGY_SUPPORTED);
|
||||
// feature_flags |= (1 << CP_FEATURE_FLAG_OFFSET_COMPENSATION_INDICATOR_SUPPORTED);
|
||||
// feature_flags |= (1 << CP_FEATURE_FLAG_OFFSET_COMPENSATION_SUPPORTED);
|
||||
|
||||
feature_flags |= (1 << CP_FEATURE_FLAG_EXTREME_MAGNITUDES_SUPPORTED);
|
||||
feature_flags |= (CP_SENSOR_MEASUREMENT_CONTEXT_FORCE << CP_FEATURE_FLAG_SENSOR_MEASUREMENT_CONTEXT);
|
||||
// feature_flags |= (CP_SENSOR_MEASUREMENT_CONTEXT_TORQUE << CP_FEATURE_FLAG_SENSOR_MEASUREMENT_CONTEXT);
|
||||
|
||||
// feature_flags |= (1 << CP_FEATURE_FLAG_INSTANTANEOUS_MEASUREMENT_DIRECTION_SUPPORTED);
|
||||
feature_flags |= (1 << CP_FEATURE_FLAG_MULTIPLE_SENSOR_LOCATIONS_SUPPORTED);
|
||||
feature_flags |= (1 << CP_FEATURE_FLAG_CRANK_LENGTH_ADJUSTMENT_SUPPORTED);
|
||||
feature_flags |= (1 << CP_FEATURE_FLAG_CHAIN_LENGTH_ADJUSTMENT_SUPPORTED);
|
||||
feature_flags |= (1 << CP_FEATURE_FLAG_CHAIN_WEIGHT_ADJUSTMENT_SUPPORTED);
|
||||
feature_flags |= (1 << CP_FEATURE_FLAG_SPAN_LENGTH_ADJUSTMENT_SUPPORTED);
|
||||
feature_flags |= (1 << CP_FEATURE_FLAG_FACTORY_CALIBRATION_DATE_SUPPORTED);
|
||||
feature_flags |= (1 << CP_FEATURE_FLAG_OFFSET_COMPENSATION_SUPPORTED);
|
||||
feature_flags |= (1 << CP_FEATURE_FLAG_ENHANCED_OFFSET_COMPENSATION_SUPPORTED);
|
||||
feature_flags |= (1 << CP_FEATURE_FLAG_CYCLING_POWER_MEASUREMENT_CHARACTERISTIC_CONTENT_MASKING_SUPPORTED);
|
||||
|
||||
|
||||
printf(" num_supported_sensor_locations %lu\n", sizeof(num_supported_sensor_locations));
|
||||
|
||||
cycling_power_service_server_init(feature_flags, CP_PEDAL_POWER_BALANCE_REFERENCE_LEFT, CP_TORQUE_SOURCE_WHEEL,
|
||||
&supported_sensor_locations[0], num_supported_sensor_locations, supported_sensor_locations[0]);
|
||||
cycling_power_service_server_packet_handler(packet_handler);
|
||||
|
||||
cycling_power_service_server_set_factory_calibration_date(calibration_date);
|
||||
|
||||
uint16_t measurement_flags = cycling_power_service_measurement_flags();
|
||||
dump_feature_flags(feature_flags);
|
||||
dump_measurement_flags(measurement_flags);
|
||||
// dump_measurement_flags_as_str(measurement_flags);
|
||||
|
||||
cycling_power_service_server_add_torque(100);
|
||||
cycling_power_service_server_add_torque(-100);
|
||||
cycling_power_service_server_add_wheel_revolution(10, event_time_s);
|
||||
cycling_power_service_server_add_crank_revolution(10, event_time_s);
|
||||
cycling_power_service_add_energy(100);
|
||||
|
||||
cycling_power_service_server_set_instantaneous_power(100);
|
||||
cycling_power_service_server_set_pedal_power_balance(50);
|
||||
force_magnitude_newton += 10;
|
||||
cycling_power_service_server_set_force_magnitude(force_magnitude_newton-5, force_magnitude_newton+5);
|
||||
torque_magnitude_newton_m += 10;
|
||||
cycling_power_service_server_set_torque_magnitude(torque_magnitude_newton_m-5, torque_magnitude_newton_m+5);
|
||||
angle_deg += 10;
|
||||
cycling_power_service_server_set_angle(angle_deg-5, angle_deg+5);
|
||||
cycling_power_service_server_set_top_dead_spot_angle(180);
|
||||
cycling_power_service_server_set_bottom_dead_spot_angle(20);
|
||||
cycling_power_service_add_energy(100);
|
||||
int16_t values[] = {12, -50, 100};
|
||||
cycling_power_service_server_set_torque_magnitude_values(3, values);
|
||||
cycling_power_service_server_set_force_magnitude_values(3, values);
|
||||
cycling_power_service_server_set_instantaneous_measurement_direction(CP_INSTANTANEOUS_MEASUREMENT_DIRECTION_TANGENTIAL_COMPONENT);
|
||||
// cycling_power_service_server_set_first_crank_measurement_angle(first_crank_measurement_angle_deg);
|
||||
|
||||
// setup advertisements
|
||||
|
||||
uint8_t adv_type = 0; // AFV_IND
|
||||
bd_addr_t null_addr;
|
||||
memset(null_addr, 0, 6);
|
||||
gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00);
|
||||
gap_advertisements_set_data(adv_data_len, (uint8_t*) adv_data);
|
||||
gap_advertisements_enable(1);
|
||||
|
||||
#ifdef HAVE_BTSTACK_STDIN
|
||||
btstack_stdin_setup(stdin_process);
|
||||
#endif
|
||||
// turn on!
|
||||
hci_power_control(HCI_POWER_ON);
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* EXAMPLE_END */
|
5
test/pts/cycling_power_server_test.gatt
Normal file
5
test/pts/cycling_power_server_test.gatt
Normal file
@ -0,0 +1,5 @@
|
||||
PRIMARY_SERVICE, GAP_SERVICE
|
||||
CHARACTERISTIC, GAP_DEVICE_NAME, READ, "Cycling Power Server"
|
||||
|
||||
// add Cycling Power Service
|
||||
#import <cycling_power_service.gatt>
|
@ -1,235 +0,0 @@
|
||||
|
||||
// hog_demo_test.h generated from hog_demo_test.gatt for BTstack
|
||||
// att db format version 1
|
||||
|
||||
// binary attribute representation:
|
||||
// - size in bytes (16), flags(16), handle (16), uuid (16/128), value(...)
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
const uint8_t profile_data[] =
|
||||
{
|
||||
// ATT DB Version
|
||||
1,
|
||||
|
||||
// 0x0001 PRIMARY_SERVICE-GAP_SERVICE
|
||||
0x0a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x28, 0x00, 0x18,
|
||||
// 0x0002 CHARACTERISTIC-GAP_DEVICE_NAME-READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x02, 0x00, 0x03, 0x28, 0x02, 0x03, 0x00, 0x00, 0x2a,
|
||||
// 0x0003 VALUE-GAP_DEVICE_NAME-READ-'HID Keyboard'
|
||||
// READ_ANYBODY
|
||||
0x14, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x2a, 0x48, 0x49, 0x44, 0x20, 0x4b, 0x65, 0x79, 0x62, 0x6f, 0x61, 0x72, 0x64,
|
||||
// add Battery Service
|
||||
// #import <battery_service.gatt> -- BEGIN
|
||||
// Specification Type org.bluetooth.service.battery_service
|
||||
// https://www.bluetooth.com/api/gatt/xmlfile?xmlFileName=org.bluetooth.service.battery_service.xml
|
||||
// Battery Service 180F
|
||||
|
||||
// 0x0004 PRIMARY_SERVICE-ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE
|
||||
0x0a, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x28, 0x0f, 0x18,
|
||||
// 0x0005 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL-DYNAMIC | READ | NOTIFY
|
||||
0x0d, 0x00, 0x02, 0x00, 0x05, 0x00, 0x03, 0x28, 0x12, 0x06, 0x00, 0x19, 0x2a,
|
||||
// 0x0006 VALUE-ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL-DYNAMIC | READ | NOTIFY-''
|
||||
// READ_ANYBODY
|
||||
0x08, 0x00, 0x02, 0x01, 0x06, 0x00, 0x19, 0x2a,
|
||||
// 0x0007 CLIENT_CHARACTERISTIC_CONFIGURATION
|
||||
// READ_ANYBODY, WRITE_ANYBODY
|
||||
0x0a, 0x00, 0x0e, 0x01, 0x07, 0x00, 0x02, 0x29, 0x00, 0x00,
|
||||
// #import <battery_service.gatt> -- END
|
||||
// add Device ID Service
|
||||
// #import <device_information_service.gatt> -- BEGIN
|
||||
// Specification Type org.bluetooth.service.device_information
|
||||
// https://www.bluetooth.com/api/gatt/xmlfile?xmlFileName=org.bluetooth.service.device_information.xml
|
||||
// Device Information 180A
|
||||
|
||||
// 0x0008 PRIMARY_SERVICE-ORG_BLUETOOTH_SERVICE_DEVICE_INFORMATION
|
||||
0x0a, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0x28, 0x0a, 0x18,
|
||||
// 0x0009 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING-DYNAMIC | READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x09, 0x00, 0x03, 0x28, 0x02, 0x0a, 0x00, 0x29, 0x2a,
|
||||
// 0x000a VALUE-ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING-DYNAMIC | READ-''
|
||||
// READ_ANYBODY
|
||||
0x08, 0x00, 0x02, 0x01, 0x0a, 0x00, 0x29, 0x2a,
|
||||
// 0x000b CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING-DYNAMIC | READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x0b, 0x00, 0x03, 0x28, 0x02, 0x0c, 0x00, 0x24, 0x2a,
|
||||
// 0x000c VALUE-ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING-DYNAMIC | READ-''
|
||||
// READ_ANYBODY
|
||||
0x08, 0x00, 0x02, 0x01, 0x0c, 0x00, 0x24, 0x2a,
|
||||
// 0x000d CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING-DYNAMIC | READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x0d, 0x00, 0x03, 0x28, 0x02, 0x0e, 0x00, 0x25, 0x2a,
|
||||
// 0x000e VALUE-ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING-DYNAMIC | READ-''
|
||||
// READ_ANYBODY
|
||||
0x08, 0x00, 0x02, 0x01, 0x0e, 0x00, 0x25, 0x2a,
|
||||
// 0x000f CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_HARDWARE_REVISION_STRING-DYNAMIC | READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x0f, 0x00, 0x03, 0x28, 0x02, 0x10, 0x00, 0x27, 0x2a,
|
||||
// 0x0010 VALUE-ORG_BLUETOOTH_CHARACTERISTIC_HARDWARE_REVISION_STRING-DYNAMIC | READ-''
|
||||
// READ_ANYBODY
|
||||
0x08, 0x00, 0x02, 0x01, 0x10, 0x00, 0x27, 0x2a,
|
||||
// 0x0011 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_FIRMWARE_REVISION_STRING-DYNAMIC | READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x11, 0x00, 0x03, 0x28, 0x02, 0x12, 0x00, 0x26, 0x2a,
|
||||
// 0x0012 VALUE-ORG_BLUETOOTH_CHARACTERISTIC_FIRMWARE_REVISION_STRING-DYNAMIC | READ-''
|
||||
// READ_ANYBODY
|
||||
0x08, 0x00, 0x02, 0x01, 0x12, 0x00, 0x26, 0x2a,
|
||||
// 0x0013 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SOFTWARE_REVISION_STRING-DYNAMIC | READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x13, 0x00, 0x03, 0x28, 0x02, 0x14, 0x00, 0x28, 0x2a,
|
||||
// 0x0014 VALUE-ORG_BLUETOOTH_CHARACTERISTIC_SOFTWARE_REVISION_STRING-DYNAMIC | READ-''
|
||||
// READ_ANYBODY
|
||||
0x08, 0x00, 0x02, 0x01, 0x14, 0x00, 0x28, 0x2a,
|
||||
// 0x0015 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_SYSTEM_ID-DYNAMIC | READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x15, 0x00, 0x03, 0x28, 0x02, 0x16, 0x00, 0x23, 0x2a,
|
||||
// 0x0016 VALUE-ORG_BLUETOOTH_CHARACTERISTIC_SYSTEM_ID-DYNAMIC | READ-''
|
||||
// READ_ANYBODY
|
||||
0x08, 0x00, 0x02, 0x01, 0x16, 0x00, 0x23, 0x2a,
|
||||
// 0x0017 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST-DYNAMIC | READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x17, 0x00, 0x03, 0x28, 0x02, 0x18, 0x00, 0x2a, 0x2a,
|
||||
// 0x0018 VALUE-ORG_BLUETOOTH_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST-DYNAMIC | READ-''
|
||||
// READ_ANYBODY
|
||||
0x08, 0x00, 0x02, 0x01, 0x18, 0x00, 0x2a, 0x2a,
|
||||
// 0x0019 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_PNP_ID-DYNAMIC | READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x19, 0x00, 0x03, 0x28, 0x02, 0x1a, 0x00, 0x50, 0x2a,
|
||||
// 0x001a VALUE-ORG_BLUETOOTH_CHARACTERISTIC_PNP_ID-DYNAMIC | READ-''
|
||||
// READ_ANYBODY
|
||||
0x08, 0x00, 0x02, 0x01, 0x1a, 0x00, 0x50, 0x2a,
|
||||
// #import <device_information_service.gatt> -- END
|
||||
// add HID Service
|
||||
// #import <hids.gatt> -- BEGIN
|
||||
// Specification Type org.bluetooth.service.human_interface_device
|
||||
// https://www.bluetooth.com/api/gatt/xmlfile?xmlFileName=org.bluetooth.service.human_interface_device.xml
|
||||
// Human Interface Device 1812
|
||||
|
||||
// 0x001b PRIMARY_SERVICE-ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE
|
||||
0x0a, 0x00, 0x02, 0x00, 0x1b, 0x00, 0x00, 0x28, 0x12, 0x18,
|
||||
// 0x001c CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE-DYNAMIC | READ | WRITE_WITHOUT_RESPONSE
|
||||
0x0d, 0x00, 0x02, 0x00, 0x1c, 0x00, 0x03, 0x28, 0x06, 0x1d, 0x00, 0x4e, 0x2a,
|
||||
// 0x001d VALUE-ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE-DYNAMIC | READ | WRITE_WITHOUT_RESPONSE-''
|
||||
// READ_ANYBODY, WRITE_ANYBODY
|
||||
0x08, 0x00, 0x06, 0x01, 0x1d, 0x00, 0x4e, 0x2a,
|
||||
// 0x001e CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_REPORT-DYNAMIC | READ | WRITE | NOTIFY | ENCRYPTION_KEY_SIZE_16
|
||||
0x0d, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x03, 0x28, 0x1a, 0x1f, 0x00, 0x4d, 0x2a,
|
||||
// 0x001f VALUE-ORG_BLUETOOTH_CHARACTERISTIC_REPORT-DYNAMIC | READ | WRITE | NOTIFY | ENCRYPTION_KEY_SIZE_16-''
|
||||
// READ_ENCRYPTED, WRITE_ENCRYPTED, ENCRYPTION_KEY_SIZE=16
|
||||
0x08, 0x00, 0x0b, 0xf5, 0x1f, 0x00, 0x4d, 0x2a,
|
||||
// 0x0020 CLIENT_CHARACTERISTIC_CONFIGURATION
|
||||
// READ_ANYBODY, WRITE_ENCRYPTED, ENCRYPTION_KEY_SIZE=16
|
||||
0x0a, 0x00, 0x0f, 0xf1, 0x20, 0x00, 0x02, 0x29, 0x00, 0x00,
|
||||
// fixed report id = 1, type = Input (1)
|
||||
// 0x0021 REPORT_REFERENCE-READ-1-1
|
||||
0x0a, 0x00, 0x02, 0x00, 0x21, 0x00, 0x08, 0x29, 0x1, 0x1,
|
||||
// 0x0022 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_REPORT-DYNAMIC | READ | WRITE | NOTIFY | ENCRYPTION_KEY_SIZE_16
|
||||
0x0d, 0x00, 0x02, 0x00, 0x22, 0x00, 0x03, 0x28, 0x1a, 0x23, 0x00, 0x4d, 0x2a,
|
||||
// 0x0023 VALUE-ORG_BLUETOOTH_CHARACTERISTIC_REPORT-DYNAMIC | READ | WRITE | NOTIFY | ENCRYPTION_KEY_SIZE_16-''
|
||||
// READ_ENCRYPTED, WRITE_ENCRYPTED, ENCRYPTION_KEY_SIZE=16
|
||||
0x08, 0x00, 0x0b, 0xf5, 0x23, 0x00, 0x4d, 0x2a,
|
||||
// 0x0024 CLIENT_CHARACTERISTIC_CONFIGURATION
|
||||
// READ_ANYBODY, WRITE_ENCRYPTED, ENCRYPTION_KEY_SIZE=16
|
||||
0x0a, 0x00, 0x0f, 0xf1, 0x24, 0x00, 0x02, 0x29, 0x00, 0x00,
|
||||
// fixed report id = 2, type = Output (2)
|
||||
// 0x0025 REPORT_REFERENCE-READ-2-2
|
||||
0x0a, 0x00, 0x02, 0x00, 0x25, 0x00, 0x08, 0x29, 0x2, 0x2,
|
||||
// 0x0026 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_REPORT-DYNAMIC | READ | WRITE | NOTIFY | ENCRYPTION_KEY_SIZE_16
|
||||
0x0d, 0x00, 0x02, 0x00, 0x26, 0x00, 0x03, 0x28, 0x1a, 0x27, 0x00, 0x4d, 0x2a,
|
||||
// 0x0027 VALUE-ORG_BLUETOOTH_CHARACTERISTIC_REPORT-DYNAMIC | READ | WRITE | NOTIFY | ENCRYPTION_KEY_SIZE_16-''
|
||||
// READ_ENCRYPTED, WRITE_ENCRYPTED, ENCRYPTION_KEY_SIZE=16
|
||||
0x08, 0x00, 0x0b, 0xf5, 0x27, 0x00, 0x4d, 0x2a,
|
||||
// 0x0028 CLIENT_CHARACTERISTIC_CONFIGURATION
|
||||
// READ_ANYBODY, WRITE_ENCRYPTED, ENCRYPTION_KEY_SIZE=16
|
||||
0x0a, 0x00, 0x0f, 0xf1, 0x28, 0x00, 0x02, 0x29, 0x00, 0x00,
|
||||
// fixed report id = 3, type = Feature (3)
|
||||
// 0x0029 REPORT_REFERENCE-READ-3-3
|
||||
0x0a, 0x00, 0x02, 0x00, 0x29, 0x00, 0x08, 0x29, 0x3, 0x3,
|
||||
// 0x002a CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP-DYNAMIC | READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x2a, 0x00, 0x03, 0x28, 0x02, 0x2b, 0x00, 0x4b, 0x2a,
|
||||
// 0x002b VALUE-ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP-DYNAMIC | READ-''
|
||||
// READ_ANYBODY
|
||||
0x08, 0x00, 0x02, 0x01, 0x2b, 0x00, 0x4b, 0x2a,
|
||||
// 0x002c CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT-DYNAMIC | READ | WRITE | NOTIFY
|
||||
0x0d, 0x00, 0x02, 0x00, 0x2c, 0x00, 0x03, 0x28, 0x1a, 0x2d, 0x00, 0x22, 0x2a,
|
||||
// 0x002d VALUE-ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT-DYNAMIC | READ | WRITE | NOTIFY-''
|
||||
// READ_ANYBODY, WRITE_ANYBODY
|
||||
0x08, 0x00, 0x0a, 0x01, 0x2d, 0x00, 0x22, 0x2a,
|
||||
// 0x002e CLIENT_CHARACTERISTIC_CONFIGURATION
|
||||
// READ_ANYBODY, WRITE_ANYBODY
|
||||
0x0a, 0x00, 0x0e, 0x01, 0x2e, 0x00, 0x02, 0x29, 0x00, 0x00,
|
||||
// 0x002f CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT-DYNAMIC | READ | WRITE | WRITE_WITHOUT_RESPONSE
|
||||
0x0d, 0x00, 0x02, 0x00, 0x2f, 0x00, 0x03, 0x28, 0x0e, 0x30, 0x00, 0x32, 0x2a,
|
||||
// 0x0030 VALUE-ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT-DYNAMIC | READ | WRITE | WRITE_WITHOUT_RESPONSE-''
|
||||
// READ_ANYBODY, WRITE_ANYBODY
|
||||
0x08, 0x00, 0x0e, 0x01, 0x30, 0x00, 0x32, 0x2a,
|
||||
// 0x0031 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT-DYNAMIC | READ | WRITE | NOTIFY
|
||||
0x0d, 0x00, 0x02, 0x00, 0x31, 0x00, 0x03, 0x28, 0x1a, 0x32, 0x00, 0x33, 0x2a,
|
||||
// 0x0032 VALUE-ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT-DYNAMIC | READ | WRITE | NOTIFY-''
|
||||
// READ_ANYBODY, WRITE_ANYBODY
|
||||
0x08, 0x00, 0x0a, 0x01, 0x32, 0x00, 0x33, 0x2a,
|
||||
// 0x0033 CLIENT_CHARACTERISTIC_CONFIGURATION
|
||||
// READ_ANYBODY, WRITE_ANYBODY
|
||||
0x0a, 0x00, 0x0e, 0x01, 0x33, 0x00, 0x02, 0x29, 0x00, 0x00,
|
||||
// bcdHID = 0x101 (v1.0.1), bCountryCode 0, remote wakeable = 0 | normally connectable 2
|
||||
// 0x0034 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION-READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x34, 0x00, 0x03, 0x28, 0x02, 0x35, 0x00, 0x4a, 0x2a,
|
||||
// 0x0035 VALUE-ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION-READ-'01 01 00 02'
|
||||
// READ_ANYBODY
|
||||
0x0c, 0x00, 0x02, 0x00, 0x35, 0x00, 0x4a, 0x2a, 0x01, 0x01, 0x00, 0x02,
|
||||
// 0x0036 CHARACTERISTIC-ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT-DYNAMIC | WRITE_WITHOUT_RESPONSE
|
||||
0x0d, 0x00, 0x02, 0x00, 0x36, 0x00, 0x03, 0x28, 0x04, 0x37, 0x00, 0x4c, 0x2a,
|
||||
// 0x0037 VALUE-ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT-DYNAMIC | WRITE_WITHOUT_RESPONSE-''
|
||||
// WRITE_ANYBODY
|
||||
0x08, 0x00, 0x04, 0x01, 0x37, 0x00, 0x4c, 0x2a,
|
||||
// #import <hids.gatt> -- END
|
||||
|
||||
// 0x0038 PRIMARY_SERVICE-GATT_SERVICE
|
||||
0x0a, 0x00, 0x02, 0x00, 0x38, 0x00, 0x00, 0x28, 0x01, 0x18,
|
||||
// 0x0039 CHARACTERISTIC-GATT_SERVICE_CHANGED-READ
|
||||
0x0d, 0x00, 0x02, 0x00, 0x39, 0x00, 0x03, 0x28, 0x02, 0x3a, 0x00, 0x05, 0x2a,
|
||||
// 0x003a VALUE-GATT_SERVICE_CHANGED-READ-''
|
||||
// READ_ANYBODY
|
||||
0x08, 0x00, 0x02, 0x00, 0x3a, 0x00, 0x05, 0x2a,
|
||||
|
||||
// END
|
||||
0x00, 0x00,
|
||||
}; // total size 338 bytes
|
||||
|
||||
|
||||
//
|
||||
// list service handle ranges
|
||||
//
|
||||
#define ATT_SERVICE_GAP_SERVICE_START_HANDLE 0x0001
|
||||
#define ATT_SERVICE_GAP_SERVICE_END_HANDLE 0x0003
|
||||
#define ATT_SERVICE_ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE_START_HANDLE 0x0004
|
||||
#define ATT_SERVICE_ORG_BLUETOOTH_SERVICE_BATTERY_SERVICE_END_HANDLE 0x0007
|
||||
#define ATT_SERVICE_ORG_BLUETOOTH_SERVICE_DEVICE_INFORMATION_START_HANDLE 0x0008
|
||||
#define ATT_SERVICE_ORG_BLUETOOTH_SERVICE_DEVICE_INFORMATION_END_HANDLE 0x001a
|
||||
#define ATT_SERVICE_ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE_START_HANDLE 0x001b
|
||||
#define ATT_SERVICE_ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE_END_HANDLE 0x0037
|
||||
#define ATT_SERVICE_GATT_SERVICE_START_HANDLE 0x0038
|
||||
#define ATT_SERVICE_GATT_SERVICE_END_HANDLE 0x003a
|
||||
|
||||
//
|
||||
// list mapping between characteristics and handles
|
||||
//
|
||||
#define ATT_CHARACTERISTIC_GAP_DEVICE_NAME_01_VALUE_HANDLE 0x0003
|
||||
#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL_01_VALUE_HANDLE 0x0006
|
||||
#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_BATTERY_LEVEL_01_CLIENT_CONFIGURATION_HANDLE 0x0007
|
||||
#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_MANUFACTURER_NAME_STRING_01_VALUE_HANDLE 0x000a
|
||||
#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_MODEL_NUMBER_STRING_01_VALUE_HANDLE 0x000c
|
||||
#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_SERIAL_NUMBER_STRING_01_VALUE_HANDLE 0x000e
|
||||
#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_HARDWARE_REVISION_STRING_01_VALUE_HANDLE 0x0010
|
||||
#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_FIRMWARE_REVISION_STRING_01_VALUE_HANDLE 0x0012
|
||||
#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_SOFTWARE_REVISION_STRING_01_VALUE_HANDLE 0x0014
|
||||
#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_SYSTEM_ID_01_VALUE_HANDLE 0x0016
|
||||
#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_IEEE_11073_20601_REGULATORY_CERTIFICATION_DATA_LIST_01_VALUE_HANDLE 0x0018
|
||||
#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_PNP_ID_01_VALUE_HANDLE 0x001a
|
||||
#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_PROTOCOL_MODE_01_VALUE_HANDLE 0x001d
|
||||
#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_REPORT_01_VALUE_HANDLE 0x001f
|
||||
#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_REPORT_01_CLIENT_CONFIGURATION_HANDLE 0x0020
|
||||
#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_REPORT_02_VALUE_HANDLE 0x0023
|
||||
#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_REPORT_02_CLIENT_CONFIGURATION_HANDLE 0x0024
|
||||
#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_REPORT_03_VALUE_HANDLE 0x0027
|
||||
#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_REPORT_03_CLIENT_CONFIGURATION_HANDLE 0x0028
|
||||
#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_REPORT_MAP_01_VALUE_HANDLE 0x002b
|
||||
#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT_01_VALUE_HANDLE 0x002d
|
||||
#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_INPUT_REPORT_01_CLIENT_CONFIGURATION_HANDLE 0x002e
|
||||
#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_BOOT_KEYBOARD_OUTPUT_REPORT_01_VALUE_HANDLE 0x0030
|
||||
#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT_01_VALUE_HANDLE 0x0032
|
||||
#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_BOOT_MOUSE_INPUT_REPORT_01_CLIENT_CONFIGURATION_HANDLE 0x0033
|
||||
#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_HID_INFORMATION_01_VALUE_HANDLE 0x0035
|
||||
#define ATT_CHARACTERISTIC_ORG_BLUETOOTH_CHARACTERISTIC_HID_CONTROL_POINT_01_VALUE_HANDLE 0x0037
|
||||
#define ATT_CHARACTERISTIC_GATT_SERVICE_CHANGED_01_VALUE_HANDLE 0x003a
|
Loading…
x
Reference in New Issue
Block a user