BLE Accelerometer - bluetooth-lowenergy

I want to send accelerometer values over BLE to an iOS app using a nordic nRF52. The app works perfectly with standard BLE services (HeartRate Measurement, Thermometer etc), but not when I try and define a custom BLE Accelerometer Service. Is there anything I need to do specifically when defining UUID's and things? Any help would be much appreciated, thanks.
Below is my custom Accelerometer class, and the main.cpp uploaded to the nRF52 below that.
#ifndef __BLE_ACCELEROMETER_SERVICE__
#define __BLE_ACCELEROMETER_SERVICE__
#include "ble/BLE.h"
#define UUID_ACCELEROMETER_SERVICE "00000000-0000-1000-7450-BE2E44B06B00"
#define UUID_X_CHARACTERISTIC "00000000-0000-1000-7450-BE2E44B06B01"
#define UUID_Y_CHARACTERISTIC "00000000-0000-1000-7450-BE2E44B06B02"
#define UUID_Z_CHARACTERISTIC "00000000-0000-1000-7450-BE2E44B06B03"
/**
* #class AccelerometerService
* #brief BLE Custom Accelerometer Service. This provides the x, y and z values of the SEEED 101020051 Grove accelerometer connected to the Nordic nRF52 DK.
*/
class AccelerometerService
{
public:
/**
* #brief Add the Accelerometer Service to an existing BLE object, initialize with values for x, y and z readings, represented as doubles.
* #param _ble Reference to the BLE device
* #param _x Initial value for the x axis
* #param _y Initial value for the y axis
* #param _z Initial value for the z axis
*/
AccelerometerService(BLE &_ble, double _x = 0, double _y = 0, double _z = 0) :
ble(_ble),
x(_x),
y(_y),
z(_z),
xAngleCharacteristic(UUID_X_CHARACTERISTIC, &x, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY),
yAngleCharacteristic(UUID_Y_CHARACTERISTIC, &y, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY),
zAngleCharacteristic(UUID_Z_CHARACTERISTIC, &z, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY) {
GattCharacteristic *readings[] = {&xAngleCharacteristic, &yAngleCharacteristic, &zAngleCharacteristic, };
GattService accelerometerService(UUID_ACCELEROMETER_SERVICE, readings, sizeof(readings) / sizeof(GattCharacteristic *));
ble.addService(accelerometerService);
}
/**
* #brief Update the x axis rotation with a new value.
* #param _x - New x value from accelerometer
*/
void update_x(uint8_t _x) {
x = _x;
ble.gattServer().write(xAngleCharacteristic.getValueHandle(), &x, 1);
}
/**
* #brief Update the y axis rotation with a new value.
* #param _z - New y value from accelerometer
*/
void update_y(uint8_t _y) {
y = _y;
ble.gattServer().write(yAngleCharacteristic.getValueHandle(), &y, 1);
}
/**
* #brief Update the z axis rotation with a new value.
* #param _z - New z value from accelerometer
*/
void update_z(uint8_t _z) {
z = _z;
ble.gattServer().write(zAngleCharacteristic.getValueHandle(), &z, 1);
}
protected:
/**
* A reference to the underlying BLE instance that this object is attached to.
* The services and characteristics will be registered in this BLE instance.
*/
BLE &ble;
/**
* The current x axis rotation, represented as a double
*/
uint8_t x;
/**
* The current y axis rotation, represented as a double
*/
uint8_t y;
/**
* The current z axis rotation, represented as a double
*/
uint8_t z;
/**
* A ReadOnlyGattCharacteristic that allows access to the peer device to the
* x axis rotation value through BLE.
*/
ReadOnlyGattCharacteristic<uint8_t> xAngleCharacteristic;
/**
* A ReadOnlyGattCharacteristic that allows access to the peer device to the
* y axis rotation value through BLE.
*/
ReadOnlyGattCharacteristic<uint8_t> yAngleCharacteristic;
/**
* A ReadOnlyGattCharacteristic that allows access to the peer device to the
* z axis rotation value through BLE.
*/
ReadOnlyGattCharacteristic<uint8_t> zAngleCharacteristic;
};
#endif /* __BLE_ACCELEROMETER_SERVICE__ */
Below is the main.cpp file I am using via mbed.org.
#include "mbed.h"
#include "ble/BLE.h"
#include "AccelerometerService.h"
DigitalOut led1(LED1);
DigitalOut led2(LED2);
static AccelerometerService *accelerometerServicePtr;
// Function declarations
void bleInitComplete(BLE::InitializationCompleteCallbackContext *);
void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *);
// Set device name and inital setup options
static const char DEVICE_NAME[] = "nRF52";
static const uint16_t uuid16_list[] = {0xFFFF};
static volatile bool triggerSensorPolling = false;
static float x = 10.0; // Dummy values for accelerometer for now
static float y = 15.0;
static float z = 18.0;
/*
* Initialization callback
*/
void bleInitComplete(BLE::InitializationCompleteCallbackContext *params)
{
BLE &ble = params->ble;
ble_error_t error = params->error;
if (error != BLE_ERROR_NONE){
printf("*** Error occured ***\n");
return;
}
/* Ensure that it is the default instance of BLE */
if(ble.getInstanceID() != BLE::DEFAULT_INSTANCE) {
return;
}
ble.gap().onDisconnection(disconnectionCallback);
// Setup primary service
accelerometerServicePtr = new AccelerometerService(ble, x, y, z);
// Setup advertising
ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
// Advertising payload has a maximum of 31 bytes
// BLE only, no classic BT
ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED |
GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
// Add name
ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
// UUIDs broadcast in advertising packet
ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list));
// Set advertising interval
ble.gap().setAdvertisingInterval(100); //100ms
// Start advertising
ble.gap().startAdvertising();
}
/**
* Restart advertising on disconnection
*/
void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *)
{
BLE::Instance(BLE::DEFAULT_INSTANCE).gap().startAdvertising();
}
/**
* This function is called when the ble initialization process has failed
*/
void onBleInitError(BLE &ble, ble_error_t error)
{
/* Avoid compiler warnings */
(void) ble;
(void) error;
/* Initialization error handling should go here */
}
int main()
{
// Initialize program
printf("\n\r *** Starting Main Loop *** \r\n");
BLE &ble = BLE::Instance(BLE::DEFAULT_INSTANCE);
ble.init(bleInitComplete);
while (ble.hasInitialized() == false)
{
while (true)
{
if (triggerSensorPolling && ble.gap().getState().connected) {
triggerSensorPolling = false;
accelerometerServicePtr->update_x(x);
accelerometerServicePtr->update_y(y);
accelerometerServicePtr->update_z(z);
}
else {
ble.waitForEvent(); // Infinite loop waiting for BLE interrupt events
}
}
}
}

This is wrong, you are sending a bad advertising packet. (0xFFFF == Insert 16 bit service here)
...
uuid16_list[] = {0xFFFF};
...
...
COMPLETE_LIST_128BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list)
There are for Bluetooth reserved 16 bit identifiers, which use the reserved UUID space.
Check This page: What range of Bluetooth UUIDs can be used for vendor defined profiles?
What you need to do is specify the full UUID in the 128 bit list.
I cant compile this but try something like this
char 128bitlist[] = {,0x00,0x00,0x00,0x00 ,0x00,0x00 ,0x10,0x00 ,0x74,0x50 ,0xBE,0x2E,0x44,0xB0,0x6B,0x00};
...
...
ble.gap().accumulateAdvertisingPayload (GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, (uint8_t *) 128bitlist, 1);
An outstanding tool for checking your advertising data is Lightblue. It is free and really informative. You should check the advertising on both Android and IOS with this tool.
Another thing to check is that you dont overfill the advertising packet. If your device name is too long, plus the 128 bit UUID, you can overfill and corrupt the packet. Try removing the name or making it really short.

There are two things need to be consider.
First,
To communicate a BLE device which has a custom service and its characteristics then you need a matching Application at Central side i.e. at mobile side for example. Because the standard application will always look for intended standard profile only.
Second,
To implement custom profile into your device firmware, Nordic has provided an example for their UART (NUS) profile. You can use that example as a reference and do the changes wherever required for example the UUID for Service and Characteristics.

Related

Trying to understand BLE UUIDs and handles (with example)

As far as I understand, in BLE, UUIDs are universal IDs that serve to uniquely identify a BLE attribute. They can be 2, 4 or 128 bytes. When they are 2 or 4, the remaining bits until 128 are filled with a standard BLE base UUID: 0000-1000-8000-00805f9b34fb. Is this correct?
Having UUIDs, why are handles needed? From what I have seen in my example, they are displayed as part of the attribute 'identifier', e.g.
# bluetoothctl capture
Primary Service (Handle 0x0920)
/org/bluez/hci0/dev_0C_B8_15_F6_61_3E/service0028
000000ff-0000-1000-8000-00805f9b34fb
Unknown
According to the logs I'm printing (which I will show below), the handle is 28. Is this correct? If so, what is the meaning of that 'Handle 0x0920'?
I have implemented a GATT server example in an ESP32 board. For now, I'm printing some logs to verify that the numbers I see (UUIDs, handles...) match those I see when connecting with bluetoothctl. Here are the logs:
I (1009) ESP32-DHT11: Service (inst. ID 0, uuid ff) created
I (1009) ESP32-DHT11: Service (inst. = 0, uuid = ff) started --> handle = 28
I (1009) ESP32-DHT11: Charac. (uuid ff01) added to service ff
I (1019) ESP32-DHT11: Charac. ff01 --> handle = 2a
I (1019) ESP32-DHT11: Descriptor (uuid 2902) added to service ff
I (1029) ESP32-DHT11: Charac. descr. 2902 --> handle = 2b
I (1029) ESP32-DHT11: Adv. parmeters set successfully
And here is the complete bluetoothctl capture:
Primary Service (Handle 0x0009)
/org/bluez/hci0/dev_0C_B8_15_F6_61_3E/service0001
00001801-0000-1000-8000-00805f9b34fb
Generic Attribute Profile
Characteristic (Handle 0xae24)
/org/bluez/hci0/dev_0C_B8_15_F6_61_3E/service0001/char0002
00002a05-0000-1000-8000-00805f9b34fb
Service Changed
Descriptor (Handle 0x0015)
/org/bluez/hci0/dev_0C_B8_15_F6_61_3E/service0001/char0002/desc0004
00002902-0000-1000-8000-00805f9b34fb
Client Characteristic Configuration
Primary Service (Handle 0x0920)
/org/bluez/hci0/dev_0C_B8_15_F6_61_3E/service0028
000000ff-0000-1000-8000-00805f9b34fb
Unknown
Characteristic (Handle 0x6c84)
/org/bluez/hci0/dev_0C_B8_15_F6_61_3E/service0028/char0029
0000ff01-0000-1000-8000-00805f9b34fb
Unknown
Descriptor (Handle 0x0015)
/org/bluez/hci0/dev_0C_B8_15_F6_61_3E/service0028/char0029/desc002b
00002902-0000-1000-8000-00805f9b34fb
Client Characteristic Configuration
I believe the first three attributes belong to the GAP layer and therefore can be ignored. Let's focus in the 3 next. As you can see, the UUIDs in my logs match those in the bluetoothctl capture. If we consider handles the last part of the attributes identifiers, all of them match save one (/org/bluez/hci0/dev_0C_B8_15_F6_61_3E/service0028/char0029 --> 0x29, which should be 0x2a according to my logs) Why does this happen?
What are exactly the path-like 'identifiers'? (e.g. /org/bluez/hci0/dev_0C_B8_15_F6_61_3E/service0028/char0029)
Code
The source code for this is quite long, so I attach it here at the end:
main.c
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "esp_system.h"
#include "esp_log.h"
#include "esp_bt.h"
#include "esp_bt_defs.h"
#include "esp_bt_main.h"
#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_gatt_common_api.h"
#include "sdkconfig.h"
#include "app.h"
#define SENSOR_NAME "ESP32-DHT11"
#define TAG SENSOR_NAME
#define ARG_UNUSED(arg) ((void)arg)
static int16_t temp, hum;
static esp_attr_value_t sensor_data = {
.attr_max_len = (uint16_t)sizeof(temp),
.attr_len = (uint16_t)sizeof(temp),
.attr_value = (uint8_t*)(&temp),
};
static void gap_handler(esp_gap_ble_cb_event_t event,
esp_ble_gap_cb_param_t* param)
{
switch (event) {
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
esp_ble_gap_start_advertising(&adv_params);
break;
default:
break;
}
}
// TODO service_def is global and opaque.
static uint32_t short_uuid(esp_bt_uuid_t uuid)
{
switch (uuid.len) {
case 2:
return uuid.uuid.uuid16;
case 4:
return uuid.uuid.uuid32;
default:
return (uint32_t)-1;
}
}
/**
* #brief Handle for the event REG. Triggered when an app. (a.k.a. profile) is
* registered.
*
*/
static esp_err_t gatts_register_evt_handler(esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t* param)
{
esp_err_t rc = esp_ble_gatts_create_service(gatts_if,
&service_def.service_id,
GATT_HANDLE_COUNT);
if (rc != ESP_OK) {
return rc;
}
ESP_LOGI(TAG,
"Service (inst. ID %x, uuid %x) created",
service_def.service_id.id.inst_id,
short_uuid(service_def.service_id.id.uuid));
return rc;
}
/**
* #brief Handle for the event CREATE (triggered after creating a GATTS
* service). Stores the handle of the previously created service, starts it and
* adds a characteristic to it.
*
*/
static esp_err_t gatts_create_evt_handler(esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t* param)
{
ARG_UNUSED(event);
ARG_UNUSED(gatts_if);
service_def.service_handle = param->create.service_handle;
esp_err_t rc = esp_ble_gatts_start_service(service_def.service_handle);
if (rc != ESP_OK) {
return rc;
}
ESP_LOGI(TAG,
"Service (inst. = %x, uuid = %x) started --> handle = %x",
service_def.service_id.id.inst_id,
short_uuid(service_def.service_id.id.uuid),
service_def.service_handle);
rc = esp_ble_gatts_add_char(service_def.service_handle,
&service_def.char_uuid,
ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
ESP_GATT_CHAR_PROP_BIT_READ
| ESP_GATT_CHAR_PROP_BIT_NOTIFY,
&sensor_data,
NULL);
if (rc != ESP_OK) {
return rc;
}
ESP_LOGI(TAG,
"Charac. (uuid %x) added to service %x",
short_uuid(service_def.char_uuid),
short_uuid(service_def.service_id.id.uuid));
return rc;
}
/**
* #brief Handles the event ADD_CHAR, which is triggered after a characteristic
* has been added successfully. When a charac. is added, its handle is generated
* (at runtime). This function gets it and stores it. Also, it adds a charac.
* descriptor to the service.
*
*/
static esp_err_t gatts_add_char_evt_handler(esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t* param)
{
ARG_UNUSED(event);
ARG_UNUSED(gatts_if);
service_def.char_handle = param->add_char.attr_handle;
ESP_LOGI(TAG,
"Charac. %x --> handle = %x",
short_uuid(service_def.char_uuid),
service_def.char_handle);
esp_err_t rc = esp_ble_gatts_add_char_descr(service_def.service_handle,
&service_def.descr_uuid,
ESP_GATT_PERM_READ
| ESP_GATT_PERM_WRITE,
NULL,
NULL);
if (rc != ESP_OK) {
return rc;
}
ESP_LOGI(TAG,
"Descriptor (uuid %x) added to service %x",
short_uuid(service_def.descr_uuid),
short_uuid(service_def.service_id.id.uuid));
return rc;
}
/**
* #brief Handles the event ADD_CHAR, which is triggered after a descriptor
* has been added successfully. It gets and stores such descriptor handle and
* overrides the default advertising data.
*
*/
static esp_err_t gatts_add_char_descr_evt_handler(
esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t* param)
{
ARG_UNUSED(event);
ARG_UNUSED(gatts_if);
service_def.descr_handle = param->add_char_descr.attr_handle;
ESP_LOGI(TAG,
"Charac. descr. %x --> handle = %x",
short_uuid(service_def.descr_uuid),
service_def.descr_handle);
esp_err_t rc = esp_ble_gap_config_adv_data(&adv_data);
if (rc != ESP_OK) {
return rc;
}
ESP_LOGI(TAG, "Adv. parmeters set successfully");
return rc;
}
/**
* #brief Handles the event CONNECT, which is triggered by a host connection.
* Updates the current connection params. with those from the incoming
* connection. Also, stores the incoming GATTS interface ID (which signals
* which service is being read) and connection ID.
*
* #todo Does gatts_if actually designate the app./profile rather than the
* service?
*
*/
static esp_err_t gatts_connect_evt_handler(esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t* param)
{
ARG_UNUSED(event);
update_conn_params(param->connect.remote_bda);
service_def.gatts_if = gatts_if;
service_def.client_write_conn = param->write.conn_id;
ESP_LOGI(TAG,
"Host connected, GATTS if. ID = %x, conn. ID = %x",
service_def.gatts_if,
service_def.client_write_conn);
return ESP_OK;
}
/**
* #brief Handles the event READ, which is triggered by a host read.
*
*/
static esp_err_t gatts_read_evt_handler(esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t* param)
{
ARG_UNUSED(event);
ESP_LOGI(TAG, "Read on %x detected", param->read.handle);
esp_gatt_rsp_t rsp = {0};
rsp.attr_value.handle = param->read.handle;
rsp.attr_value.len = sensor_data.attr_len;
memcpy(rsp.attr_value.value, sensor_data.attr_value, sensor_data.attr_len);
return esp_ble_gatts_send_response(gatts_if,
param->read.conn_id,
param->read.trans_id,
ESP_GATT_OK,
&rsp);
}
/**
* #brief GATT event handler.
*
* #see esp_ble_gatts_app_register
*/
static void gatt_handler(esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t* param)
{
esp_err_t rc = ESP_OK;
switch (event) {
case ESP_GATTS_REG_EVT:
rc = gatts_register_evt_handler(event, gatts_if, param);
break;
case ESP_GATTS_CREATE_EVT:
rc = gatts_create_evt_handler(event, gatts_if, param);
break;
case ESP_GATTS_ADD_CHAR_EVT:
rc = gatts_add_char_evt_handler(event, gatts_if, param);
break;
case ESP_GATTS_ADD_CHAR_DESCR_EVT:
rc = gatts_add_char_descr_evt_handler(event, gatts_if, param);
break;
case ESP_GATTS_CONNECT_EVT: {
rc = gatts_connect_evt_handler(event, gatts_if, param);
break;
}
case ESP_GATTS_READ_EVT:
rc = gatts_read_evt_handler(event, gatts_if, param);
break;
case ESP_GATTS_WRITE_EVT: {
ESP_LOGI(TAG,
"ESP_GATTS_WRITE_EVT %x %x",
service_def.descr_handle,
param->write.handle);
if (service_def.descr_handle == param->write.handle) {
uint16_t descr_value =
param->write.value[1] << 8 | param->write.value[0];
if (descr_value != 0x0000) {
ESP_LOGI(TAG, "notify enable");
esp_ble_gatts_send_indicate(gatts_if,
param->write.conn_id,
service_def.char_handle,
sensor_data.attr_len,
sensor_data.attr_value,
false);
} else {
ESP_LOGI(TAG, "notify disable");
}
esp_ble_gatts_send_response(gatts_if,
param->write.conn_id,
param->write.trans_id,
ESP_GATT_OK,
NULL);
} else {
esp_ble_gatts_send_response(gatts_if,
param->write.conn_id,
param->write.trans_id,
ESP_GATT_WRITE_NOT_PERMIT,
NULL);
}
break;
}
case ESP_GATTS_DISCONNECT_EVT:
service_def.gatts_if = 0;
esp_ble_gap_start_advertising(&adv_params);
break;
default:
break;
}
if (rc != ESP_OK) {
ESP_LOGE(TAG, "GATTS error %d", rc);
}
}
static int16_t asd = 0;
static int dht_read_data(int16_t* hum, int16_t* temp)
{
if (asd == 255) {
asd = 0;
} else {
asd++;
}
*hum = asd;
*temp = 2 * asd;
return ESP_OK;
}
static void read_temp_task(void* arg)
{
while (1) {
vTaskDelay(2000 / portTICK_PERIOD_MS);
if (dht_read_data(&hum, &temp) == ESP_OK) {
temp /= 10;
if (service_def.gatts_if > 0) {
esp_ble_gatts_send_indicate(service_def.gatts_if,
service_def.client_write_conn,
service_def.char_handle,
sensor_data.attr_len,
sensor_data.attr_value,
false);
}
} else {
ESP_LOGE(TAG, "DHT11 read failed");
}
}
}
void app_main(void)
{
init_service_def();
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES
|| ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ESP_ERROR_CHECK(nvs_flash_init());
}
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
esp_bt_controller_init(&bt_cfg);
esp_bt_controller_enable(ESP_BT_MODE_BLE);
esp_bluedroid_init();
esp_bluedroid_enable();
esp_ble_gap_register_callback(gap_handler);
esp_ble_gatts_register_callback(gatt_handler);
esp_ble_gatts_app_register(0);
xTaskCreate(read_temp_task,
"temp",
configMINIMAL_STACK_SIZE * 3,
NULL,
5,
NULL);
}
app.c
#include "app.h"
#include <string.h>
static uint8_t adv_service_uuid128[32] = {
0xfb,
0x34,
0x9b,
0x5f,
0x80,
0x00,
0x00,
0x80,
0x00,
0x10,
0x00,
0x00,
0xFF,
0x00,
0x00,
0x00,
};
esp_ble_adv_data_t adv_data = {
.set_scan_rsp = false,
.include_name = true,
.include_txpower = false,
.min_interval = 0x0006,
.max_interval = 0x0010,
.appearance = 0x00,
.manufacturer_len = 0,
.p_manufacturer_data = NULL,
.service_data_len = 0,
.p_service_data = NULL,
.service_uuid_len = sizeof(adv_service_uuid128),
.p_service_uuid = adv_service_uuid128,
.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
};
esp_ble_adv_params_t adv_params = {
.adv_int_min = 0x20,
.adv_int_max = 0x40,
.adv_type = ADV_TYPE_IND,
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
.channel_map = ADV_CHNL_ALL,
.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
};
service_info_t service_def;
void init_service_def(void)
{
service_def.service_id.is_primary = true;
service_def.service_id.id.inst_id = 0x00;
service_def.service_id.id.uuid.len = ESP_UUID_LEN_16;
service_def.service_id.id.uuid.uuid.uuid16 = GATT_SERVICE_UUID;
service_def.char_uuid.len = ESP_UUID_LEN_16;
service_def.char_uuid.uuid.uuid16 = GATT_CHARACTERISTIC_UUID;
service_def.descr_uuid.len = ESP_UUID_LEN_16;
service_def.descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG;
service_def.gatts_if = 0;
}
void update_conn_params(esp_bd_addr_t remote_bda)
{
esp_ble_conn_update_params_t conn_params = {0};
memcpy(conn_params.bda, remote_bda, sizeof(esp_bd_addr_t));
conn_params.latency = 0;
conn_params.max_int = 0x20;
conn_params.min_int = 0x10;
conn_params.timeout = 400;
esp_ble_gap_update_conn_params(&conn_params);
}
app.h
#ifndef gattex_app_h_
#define gattex_app_h_
#include <stdint.h>
#include <stddef.h>
#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#define GATT_SERVICE_UUID 0x00FF
#define GATT_CHARACTERISTIC_UUID 0xFF01
#define GATT_HANDLE_COUNT 4
typedef struct
{
uint16_t service_handle;
esp_gatt_srvc_id_t service_id;
uint16_t char_handle;
esp_bt_uuid_t char_uuid;
uint16_t descr_handle;
esp_bt_uuid_t descr_uuid;
esp_gatt_if_t gatts_if;
uint16_t client_write_conn;
} service_info_t;
extern esp_ble_adv_data_t adv_data;
extern esp_ble_adv_params_t adv_params;
extern service_info_t service_def;
void init_service_def(void);
void update_conn_params(esp_bd_addr_t remote_bda);
#endif
The UUID (e.g. 00001801-0000-1000-8000-00805f9b34fb) is the identifier of the GATT service/characteristic etc. This is used for identification GATT entries at a high level. There is a reserved range for Bluetooth SIG adopted UUIDs. Outside of that range can be used for Custom UUIDs
The handle (e.g. 0x0009) is the identifier for that specific entry in the local GATT database that gets built during service discovery. For example, there might a characteristic UUID used under multiple GATT services. However each database entry will have a different handle.
bluetoothctl communicates with the Bluetooth Daemon using D-Bus for inter process communication (IPC). For D-Bus there are three bits of information that are required: the D-Bus Service Name (org.bluez), the D-Bus interface (e.g. org.bluez.GattService1) and the D-Bus object path (e.g. /org/bluez/hci0/dev_0C_B8_15_F6_61_3E/service0001). The BlueZ API for GATT is documented at: https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/gatt-api.txt
The LE attribute data is stored as a sequence of handles with data as in this example:
Handle
0001 UUID = 2800 (Primary service) Value = 1800 (UUID of the primary service)
The attributes that belong to this primary service follow:
0002 UUID = 2803 (Characteristic info) Value = Handle (0003) permissions and UUID of the following characteristic
0003 UUID = as in previous handle Value = Value of the characteristic
0004 UUID = 2803 (Characteristic info) Value = Handle (0005) permissions and UUID of the following characteristic
0005 UUID = as in previous handle Value = Value of the characteristic
Some characteristics are followed by a descriptor (e.g. notify enable)
0006 UUID = 2803 (Characteristic info) Value = Handle (0007) permissions and UUID of the following characteristic
0007 UUID = as in previous handle Value = Value of the characteristic
0008 UUID = 2902 (Descriptor for notify enable) Value = 00 00 (notify off) or 00 01 (notify on)
There might then be another primary service
0009 UUID = 2800 (Primary service) Value = 1801 (UUID of the primary service)
etc....
A client only needs the value handle (e.g. 0003 above) to read/write the characteristic. If it does not know the handle, it must read this database to find the handle from the UUID. It will read all the handles looking for UUID=2803 entries. A 2-byte UUID (e.g. 1801) may also appear as a 16-byte standard value with the 2 bytes at bytes 3/4: 00001801-0000-1000-8000-00805F9B34FB.
Normally, the client will read the database on connection and set up the dbus objects using the handles it finds. So the first characteristic in the example is a member of the primary service with handle 0001, and has an info handle of 0002, so it will appear as ..../service0001/char0002. It will read and write using handle 0003. The other handles listed first (like Characteristic (Handle 0xae24)) seem to invented by bluetoothctl for its own purposes and are unknown to the LE device, so confusion is created by two types of handle.

How do I properly use scanf to scan into struct members correctly? Currently I overwrite price and quantity into name

I am having an issue with my read item function. When I use scanf I am not properly scanning into the correct struc member. When I used my debugger I see incorrect numbers in amount and price fields. The idea is that I would like to be able to accept a produce name that might include spaces, then scan in the price and scan in the amount.
Like this
scanf("%63[^\n]s %f %u", productName, &(myItems->fPrice), &(myItems->iQuantity));
IDE: Netbeans
Compiler: Cygwin
OS: Windows 7 64bit
enter code here
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/*
* File: main.c
* Author: Armando Valencia
*
* Created on February 2, 2019, 3:05 PM
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define STRING_LENGTH 64
/*
* C program that creates a structure pointer and passes it to func
* if you define the stuct outside main you are not allocating any memory for it
*/
typedef struct Item{
char *pItemName;
unsigned int iQuantity;
float fPrice;
float fAmount;
}sItem;
void readItem(sItem *myItems);
void printfItem(sItem *myItems);
int main(int argc, char** argv) {
sItem sPurchase;
sItem *psItems = &sPurchase;
readItem(psItems);
printfItem(psItems);
return (EXIT_SUCCESS);
}
/*
* Create a function named readItem that takes a structure pointer
* of type item as a parameter. This function should read in
* (from the user) a product name, price, and quantity
* the contents be stored in the passed in structure to the
* function
*/
void readItem(sItem *myItems){
char productName[STRING_LENGTH] = {0};
printf("Enter the name of your product, the price and quantity: ");
scanf("%63[^\n]s %f %u", productName, &(myItems->fPrice), &(myItems->iQuantity));
productName[STRING_LENGTH-1] = '\0';
//allocate memory to hold name
myItems->pItemName = (char *) calloc(strlen(productName) + 1, sizeof(char));
//copy name to allocated memory
strncpy(myItems->pItemName, productName, strlen(productName));
//calculate amount
myItems->fAmount = myItems->iQuantity * myItems->fPrice;
return;
}
void printfItem(sItem *myItems){
printf("Purchase information\n");
printf("Item name: %s\nQuantity: %u\nPrice: %.2f\nTotal amount: %.2f\n",
myItems->pItemName, myItems->iQuantity, myItems->fPrice, myItems->fAmount);
}

How can i specify an area of code to instrument it by pintool?

There are four levels of granularity in Pin: routine, instruction and image, trace.
Can i specify an limits/area to start and stop inserting instrumentation code.
may by like directive like ( # start instrumentation , # end instrumentation )
or some thing like that,
An example:
for( int i=0; i< x; i++)
{
#startInstrumentation
for( ....;.....;.....)
{
// some code
// function call, conditions , loops, ....
}
#endInstrumentation
}
Is there are any way to do this ?
You can use trace-based instrumentation to do what you want. At the beginning of each trace, check its start address and if it is not in range of interest, avoid adding analysis functions and return immediately from the routine.
It's possible that a trace will begin outside a region of interest but end inside it, or the other way around. If this can happen, you will need to perform more fine grained choice about what to instrument. I would check if this is a real concern before investing an effort.
If you're interested in instrumenting specific routines or images, consider using filter.cpp from InstLib in the kit. An example for use can be found in InstLibExamples.
Now, as for how to target these regions of interest, you have several options. If you have no control over the target binary, you can specify the region in a command line parameter, as a pair of offsets into the image of interest.
If you have control of the binary, you can insert two symbols, that specify the start and end of the rgion of interest, and then iterate over image symbols using the SYM interface.
My solution would be:
1) Insert Region Of Interest (ROI) begin and end functions in code
2) Set a flag after ROI begin is executed and unset it before ROI end is executed
3) Return immediately from instrumenting calls if the flat is unset
Here you have an example where I have modified the memory reference trace to trace only a ROI.
#include <stdio.h>
#include "pin.H"
#include <string>
const CHAR * ROI_BEGIN = "__parsec_roi_begin";
const CHAR * ROI_END = "__parsec_roi_end";
FILE * trace;
bool isROI = false;
// Print a memory read record
VOID RecordMemRead(VOID * ip, VOID * addr, CHAR * rtn)
{
// Return if not in ROI
if(!isROI)
{
return;
}
// Log memory access in CSV
fprintf(trace,"%p,R,%p,%s\n", ip, addr, rtn);
}
// Print a memory write record
VOID RecordMemWrite(VOID * ip, VOID * addr, CHAR * rtn)
{
// Return if not in ROI
if(!isROI)
{
return;
}
// Log memory access in CSV
fprintf(trace,"%p,W,%p,%s\n", ip, addr, rtn);
}
// Set ROI flag
VOID StartROI()
{
isROI = true;
}
// Set ROI flag
VOID StopROI()
{
isROI = false;
}
// Is called for every instruction and instruments reads and writes
VOID Instruction(INS ins, VOID *v)
{
// Instruments memory accesses using a predicated call, i.e.
// the instrumentation is called iff the instruction will actually be executed.
//
// On the IA-32 and Intel(R) 64 architectures conditional moves and REP
// prefixed instructions appear as predicated instructions in Pin.
UINT32 memOperands = INS_MemoryOperandCount(ins);
// Iterate over each memory operand of the instruction.
for (UINT32 memOp = 0; memOp < memOperands; memOp++)
{
// Get routine name if valid
const CHAR * name = "invalid";
if(RTN_Valid(INS_Rtn(ins)))
{
name = RTN_Name(INS_Rtn(ins)).c_str();
}
if (INS_MemoryOperandIsRead(ins, memOp))
{
INS_InsertPredicatedCall(
ins, IPOINT_BEFORE, (AFUNPTR)RecordMemRead,
IARG_INST_PTR,
IARG_MEMORYOP_EA, memOp,
IARG_ADDRINT, name,
IARG_END);
}
// Note that in some architectures a single memory operand can be
// both read and written (for instance incl (%eax) on IA-32)
// In that case we instrument it once for read and once for write.
if (INS_MemoryOperandIsWritten(ins, memOp))
{
INS_InsertPredicatedCall(
ins, IPOINT_BEFORE, (AFUNPTR)RecordMemWrite,
IARG_INST_PTR,
IARG_MEMORYOP_EA, memOp,
IARG_ADDRINT, name,
IARG_END);
}
}
}
// Pin calls this function every time a new rtn is executed
VOID Routine(RTN rtn, VOID *v)
{
// Get routine name
const CHAR * name = RTN_Name(rtn).c_str();
if(strcmp(name,ROI_BEGIN) == 0) {
// Start tracing after ROI begin exec
RTN_Open(rtn);
RTN_InsertCall(rtn, IPOINT_AFTER, (AFUNPTR)StartROI, IARG_END);
RTN_Close(rtn);
} else if (strcmp(name,ROI_END) == 0) {
// Stop tracing before ROI end exec
RTN_Open(rtn);
RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)StopROI, IARG_END);
RTN_Close(rtn);
}
}
// Pin calls this function at the end
VOID Fini(INT32 code, VOID *v)
{
fclose(trace);
}
/* ===================================================================== */
/* Print Help Message */
/* ===================================================================== */
INT32 Usage()
{
PIN_ERROR( "This Pintool prints a trace of memory addresses\n"
+ KNOB_BASE::StringKnobSummary() + "\n");
return -1;
}
/* ===================================================================== */
/* Main */
/* ===================================================================== */
int main(int argc, char *argv[])
{
// Initialize symbol table code, needed for rtn instrumentation
PIN_InitSymbols();
// Usage
if (PIN_Init(argc, argv)) return Usage();
// Open trace file and write header
trace = fopen("roitrace.csv", "w");
fprintf(trace,"pc,rw,addr,rtn\n");
// Add instrument functions
RTN_AddInstrumentFunction(Routine, 0);
INS_AddInstrumentFunction(Instruction, 0);
PIN_AddFiniFunction(Fini, 0);
// Never returns
PIN_StartProgram();
return 0;
}

How to use the XGraphicsExposeEvent to draw something by Qt

typedef struct {
int type;
unsigned long serial; /* # of last request processed by server */
Bool send_event; /* true if this came from a SendEvent request */
Display *display; /* Display the event was read from */
Drawable drawable;
int x, y;
int width, height;
int count; /* if non-zero, at least this many more */
int major_code; /* core is CopyArea or CopyPlane */
int minor_code; /* not defined in the core */
} XGraphicsExposeEvent;
we always can use the field [drawable] to paint something by GTK, like this :
GdkNativeWindow nativeWinId = (XID)((void*)(expose->drawable));
...
...
// draw something by GdkNativeWindow with gtk APIs.
but how about Qt?

QT C++ wait till specific time to execute function

I am trying to create an app that holds a list of tasks and for each time a deadline, now i want to execute a function (show a popup) once a deadline is met.
i have this:
#ifndef TIMER_H
#define TIMER_H
#include <QWidget>
#include <QTimer>
#include <QtGui>
#include <QObject>
class Timer : public QWidget
{
Q_OBJECT
public:
Timer(QWidget * parent = 0);
void setTimer(QString title, QString description, QDate date, QTime reminderTime);
public slots:
void showWarning() {QString show = tit;
QPushButton * thanks = new QPushButton(QObject::tr("Thank you for reminding me!"));
show.append("\n");
show.append(des);
QMessageBox popup;
popup.setText(show);
popup.setWindowTitle("Calendar : Reminder");
popup.setDefaultButton(thanks);
popup.exec();
}
private:
QString tit;
QString des;
QDateTime now;
QDateTime timeoftheaction;
QTimer *timer;
};
cpp file:
#endif // TIMER_H
#include "timer.h"
#include <iostream>
using namespace std;
Timer::Timer(QWidget * parent)
: QWidget(parent)
{
}
void Timer::setTimer(QString title, QString description, QDate date, QTime reminderTime)
{
now.currentDateTime();
timer = new QTimer;
tit = title;
des = description;
timeoftheaction.setDate(date);
timeoftheaction.setTime(reminderTime);
connect(timer, SIGNAL(timeout()),this,SLOT(showWarning()));
timer->start(now.secsTo(timeoftheaction)*1000);
}
Yet function showWarning is never being called...
no compilation errors, function showWarning works perfectly (tested)
I think the error is in the connect but i am not sure...
Short answer:
Change:
now.currentDateTime();
to
now = QDateTime::currentDateTime();
Longish answer:
currentDateTime() is a static function which instead of changing your existing object, actually returns a new QDataTime object. Although you are calling it as a member function, it's still called as a static one and leaves your object intact, which is still invalid.
Your later call to secsTo() on an invalid data time probably gets you an negative or really large number that either has passed (never going to trigger) or really late in the future.
Here is something that might be a more generic solution.
#include <QThread>
#include <QTimer>
#include <QObject>
#include <map>
/**
* Singleton to implement simple 'relative' timer.
* Implements busy wait and also timeout-notifications (useful to monitor operations that could hang, etc).
*
* If the whole application is stalled (e.g. when a new device is connected), and we only want to
* wait for a period during which application was 'really' working (not just hanging waiting for OS)
* - then ticks will be missed too. This way - it's should be possible to avoid unnecessary timeouts
* that could happen if global time was measured (especially annoying on WINdows platforms)
*/
class RelativeTimer : public QObject
{
Q_OBJECT
typedef std::multimap <unsigned int, std::pair <QObject*, QString> > Notifications;
public:
/**
* Call to busy-wait for number of ticks.
*/
static void wait_num_of_ticks(unsigned int num_of_ticks_to_wait)
{
if(self.timer_id == 0)
{
qDebug("timer not initialised, call 'RelativeTimer::Init()'");
return;
}
if(num_of_ticks_to_wait > 0)
{
unsigned long until = self.tick_counter + num_of_ticks_to_wait; // it's ok if it wraps around..
while(self.tick_counter != until)
{
QCoreApplication::processEvents(); // let others to their job..
// or comment above out and just busy wait..
}
}
}
/**
* Call to busy-wait until ms_to_wait have elapsed.
* If ms_to_wait is < tick period
* Interval will define 'tick' frequency (and accuracy).
*/
static void wait_ms(unsigned int ms_to_wait)
{
wait_num_of_ticks(num_of_ticks_to_wait(ms_to_wait));
}
/**
* Call to schedule a notification after a given timeout.
* returns notification_id that can be used to cancel this notification.
*/
static unsigned long notify_timeout_ms(unsigned int ms_to_wait,
QObject *receiver,
const char* method_name)
{
unsigned long ticks_to_wait = 0;
if(receiver && method_name)
{
ticks_to_wait = num_of_ticks_to_wait(ms_to_wait);
if(ticks_to_wait > 1)
{
ticks_to_wait += self.tick_counter;
if(ticks_to_wait == 0) // avoid 0 - make it one tick more (to alow to see if successfully added this notif)
{
ticks_to_wait = 1;
}
self.notifications.insert(std::make_pair(ticks_to_wait,
std::make_pair(receiver, method_name)));
qDebug("added delayed call..");
}
else
{
QMetaObject::invokeMethod(receiver, method_name, Qt::QueuedConnection);
ticks_to_wait = 0;
}
}
return ticks_to_wait;
}
/**
* Call to cancel a notification with a given id.
* Specify name if there were more notification with the same id (scheduled for the same tick).
* returns true on successfull cancellation, false otherwise.
*/
static bool cancel_timeout_notification(unsigned long notification_id, QString notification_name="")
{
bool cancelled = false;
if(self.notifications.size())
{
std::pair<Notifications::iterator, Notifications::iterator> to_cancel = self.notifications.equal_range(notification_id);
Notifications::iterator n = to_cancel.first;
for( ;n != to_cancel.second; ++n)
{
if(notification_name.size()== 0 || n->second.second == notification_name)
{
self.notifications.erase(n);
cancelled = true;
break;
}
}
}
return cancelled;
}
static const unsigned int default_tick_period_ms = 100;
/**
* Call this method after event loop is created- to initiate (re-start) timer.
* tick period defines 'tick' frequency (and accuracy of the timer)
* (note on Windows that there's no point to go down below 100ms).
*/
static void Init(unsigned int tick_period_ms = default_tick_period_ms)
{
self.moveToThread(&self.thread);
self.thread.start();
while(!self.thread.isRunning());
self.current_interval = tick_period_ms;
// InitMe() should execute in the thread context..
QMetaObject::invokeMethod(&self, "InitMe", Qt::QueuedConnection);
}
private:
/**
* Internal method to convert ms to number of ticks.
*/
static unsigned int num_of_ticks_to_wait(unsigned int ms_to_wait)
{
if(ms_to_wait > self.current_interval)
{
if(ms_to_wait % self.current_interval)
{
// average it..
ms_to_wait = ms_to_wait + self.current_interval / 2;
}
ms_to_wait /= self.current_interval;
}
else
{
ms_to_wait = 0;
}
return ms_to_wait;
}
/**
* Internal method to handle tick. Increments counter and invokes notifications.
*/
void timerEvent ( QTimerEvent* /*event*/ )
{
tick_counter++;
if(notifications.size())
{
std::pair<Notifications::iterator, Notifications::iterator> to_notify = notifications.equal_range(tick_counter);
Notifications::iterator n = to_notify.first;
for( ;n != to_notify.second; ++n)
{
QMetaObject::invokeMethod(n->second.first,
n->second.second.toStdString().c_str(),
Qt::QueuedConnection);
}
notifications.erase(to_notify.first, to_notify.second);
}
}
private slots:
/**
* Internal slot to initialize the timer. Should be called in this->timer context.
*/
void InitMe()
{
if(timer_id != 0)
{
killTimer(timer_id);
timer_id = 0;
}
tick_counter = 0;
timer_id = self.startTimer(self.current_interval);
}
private:
RelativeTimer()
{
}
~RelativeTimer()
{
thread.quit();
thread.wait();
}
QThread thread;
Notifications notifications;
int timer_id;
unsigned int current_interval;
unsigned long tick_counter;
static RelativeTimer self; // implement it as a signleton.. Define it in your C file, e.g.:
// RelativeTimer RelativeTimer::self;
};
Can be used like:
CurrQObjectClass::OnTimeout()
{
// ...
}
CurrQObjectClass::SomeMethod()
{
RelativeTimer::notify_timeout_ms(5000, this, "OnTimeout");
}
but also for busy-waiting:
RelativeTimer::wait_ms(2000);
Enjoy.

Resources