zephyr/samples/bluetooth/peripheral_esp/src/main.c
Luiz Augusto von Dentz ee9f5c1784 Bluetooth: GATT: Pass CCC attribute to changed callback
This way the application can reuse the same callback for multiple CCC
since it can track what CCC is affect by checking the attribute pointer.

Change-Id: I608da643aea07de26b65d67e6db3268d717d0f53
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
2016-10-21 07:47:31 +03:00

473 lines
12 KiB
C

/* main.c - Application main entry point */
/*
* Copyright (c) 2016 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <misc/printk.h>
#include <misc/byteorder.h>
#include <zephyr.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/conn.h>
#include <bluetooth/uuid.h>
#include <bluetooth/gatt.h>
#include <gatt/gap.h>
#include <gatt/dis.h>
#include <gatt/bas.h>
#define DEVICE_NAME "ESP peripheral"
#define DEVICE_NAME_LEN (sizeof(DEVICE_NAME) - 1)
#define SENSOR_1_NAME "Temperature Sensor 1"
#define SENSOR_2_NAME "Temperature Sensor 2"
#define SENSOR_3_NAME "Humidity Sensor"
#define APPEARANCE_THERMOMETER 0x0300
/* Sensor Internal Update Interval [seconds] */
#define SENSOR_1_UPDATE_IVAL 5
#define SENSOR_2_UPDATE_IVAL 12
#define SENSOR_3_UPDATE_IVAL 60
/* ESS error definitions */
#define ESS_ERR_WRITE_REJECT 0x80
#define ESS_ERR_COND_NOT_SUPP 0x81
/* ESS Trigger Setting conditions */
#define ESS_TRIGGER_INACTIVE 0x00
#define ESS_FIXED_TIME_INTERVAL 0x01
#define ESS_NO_LESS_THAN_SPECIFIED_TIME 0x02
#define ESS_VALUE_CHANGED 0x03
#define ESS_LESS_THAN_REF_VALUE 0x04
#define ESS_LESS_OR_EQUAL_TO_REF_VALUE 0x05
#define ESS_GREATER_THAN_REF_VALUE 0x06
#define ESS_GREATER_OR_EQUAL_TO_REF_VALUE 0x07
#define ESS_EQUAL_TO_REF_VALUE 0x08
#define ESS_NOT_EQUAL_TO_REF_VALUE 0x09
static inline void int_to_le24(uint32_t value, uint8_t *u24)
{
u24[0] = value & 0xff;
u24[1] = (value >> 8) & 0xff;
u24[2] = (value >> 16) & 0xff;
}
static inline uint32_t le24_to_int(const uint8_t *u24)
{
return ((uint32_t)u24[0] |
(uint32_t)u24[1] << 8 |
(uint32_t)u24[2] << 16);
}
static ssize_t read_u16(struct bt_conn *conn, const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset)
{
const uint16_t *u16 = attr->user_data;
uint16_t value = sys_cpu_to_le16(*u16);
return bt_gatt_attr_read(conn, attr, buf, len, offset, &value,
sizeof(value));
}
/* Environmental Sensing Service Declaration */
struct es_measurement {
uint16_t flags; /* Reserved for Future Use */
uint8_t sampling_func;
uint32_t meas_period;
uint32_t update_interval;
uint8_t application;
uint8_t meas_uncertainty;
};
struct temperature_sensor {
int16_t temp_value;
/* Valid Range */
int16_t lower_limit;
int16_t upper_limit;
/* ES trigger setting - Value Notification condition */
uint8_t condition;
union {
uint32_t seconds;
int16_t ref_val; /* Reference temperature */
};
struct bt_gatt_ccc_cfg ccc_cfg[CONFIG_BLUETOOTH_MAX_PAIRED];
struct es_measurement meas;
};
struct humidity_sensor {
int16_t humid_value;
struct es_measurement meas;
};
static bool simulate_temp;
static struct temperature_sensor sensor_1 = {
.temp_value = 1200,
.lower_limit = -10000,
.upper_limit = 10000,
.condition = ESS_VALUE_CHANGED,
.meas.sampling_func = 0x00,
.meas.meas_period = 0x01,
.meas.update_interval = SENSOR_1_UPDATE_IVAL,
.meas.application = 0x1c,
.meas.meas_uncertainty = 0x04,
};
static struct temperature_sensor sensor_2 = {
.temp_value = 1800,
.lower_limit = -1000,
.upper_limit = 5000,
.condition = ESS_VALUE_CHANGED,
.meas.sampling_func = 0x00,
.meas.meas_period = 0x01,
.meas.update_interval = SENSOR_2_UPDATE_IVAL,
.meas.application = 0x1b,
.meas.meas_uncertainty = 0x04,
};
static struct humidity_sensor sensor_3 = {
.humid_value = 6233,
.meas.sampling_func = 0x02,
.meas.meas_period = 0x0e10,
.meas.update_interval = SENSOR_3_UPDATE_IVAL,
.meas.application = 0x1c,
.meas.meas_uncertainty = 0x01,
};
static void temp_ccc_cfg_changed(const struct bt_gatt_attr *attr,
uint16_t value)
{
simulate_temp = value == BT_GATT_CCC_NOTIFY;
}
struct read_es_measurement_rp {
uint16_t flags; /* Reserved for Future Use */
uint8_t sampling_function;
uint8_t measurement_period[3];
uint8_t update_interval[3];
uint8_t application;
uint8_t measurement_uncertainty;
} __packed;
static ssize_t read_es_measurement(struct bt_conn *conn,
const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset)
{
const struct es_measurement *value = attr->user_data;
struct read_es_measurement_rp rsp;
rsp.flags = sys_cpu_to_le16(value->flags);
rsp.sampling_function = value->sampling_func;
int_to_le24(value->meas_period, rsp.measurement_period);
int_to_le24(value->update_interval, rsp.update_interval);
rsp.application = value->application;
rsp.measurement_uncertainty = value->meas_uncertainty;
return bt_gatt_attr_read(conn, attr, buf, len, offset, &rsp,
sizeof(rsp));
}
static ssize_t read_temp_valid_range(struct bt_conn *conn,
const struct bt_gatt_attr *attr, void *buf,
uint16_t len, uint16_t offset)
{
const struct temperature_sensor *sensor = attr->user_data;
uint16_t tmp[] = {sys_cpu_to_le16(sensor->lower_limit),
sys_cpu_to_le16(sensor->upper_limit)};
return bt_gatt_attr_read(conn, attr, buf, len, offset, tmp,
sizeof(tmp));
}
struct es_trigger_setting_seconds {
uint8_t condition;
uint8_t sec[3];
} __packed;
struct es_trigger_setting_reference {
uint8_t condition;
int16_t ref_val;
} __packed;
static ssize_t read_temp_trigger_setting(struct bt_conn *conn,
const struct bt_gatt_attr *attr,
void *buf, uint16_t len,
uint16_t offset)
{
const struct temperature_sensor *sensor = attr->user_data;
switch (sensor->condition) {
/* Operand N/A */
case ESS_TRIGGER_INACTIVE:
/* fallthrough */
case ESS_VALUE_CHANGED:
return bt_gatt_attr_read(conn, attr, buf, len, offset,
&sensor->condition,
sizeof(sensor->condition));
/* Seconds */
case ESS_FIXED_TIME_INTERVAL:
/* fallthrough */
case ESS_NO_LESS_THAN_SPECIFIED_TIME: {
struct es_trigger_setting_seconds rp;
rp.condition = sensor->condition;
int_to_le24(sensor->seconds, rp.sec);
return bt_gatt_attr_read(conn, attr, buf, len, offset,
&rp, sizeof(rp));
}
/* Reference temperature */
default: {
struct es_trigger_setting_reference rp;
rp.condition = sensor->condition;
rp.ref_val = sys_cpu_to_le16(sensor->ref_val);
return bt_gatt_attr_read(conn, attr, buf, len, offset,
&rp, sizeof(rp));
}
}
}
static bool check_condition(uint8_t condition, int16_t old_val, int16_t new_val,
int16_t ref_val)
{
switch (condition) {
case ESS_TRIGGER_INACTIVE:
return false;
case ESS_FIXED_TIME_INTERVAL:
case ESS_NO_LESS_THAN_SPECIFIED_TIME:
/* TODO: Check time requirements */
return false;
case ESS_VALUE_CHANGED:
return new_val != old_val;
case ESS_LESS_THAN_REF_VALUE:
return new_val < ref_val;
case ESS_LESS_OR_EQUAL_TO_REF_VALUE:
return new_val <= ref_val;
case ESS_GREATER_THAN_REF_VALUE:
return new_val > ref_val;
case ESS_GREATER_OR_EQUAL_TO_REF_VALUE:
return new_val >= ref_val;
case ESS_EQUAL_TO_REF_VALUE:
return new_val == ref_val;
case ESS_NOT_EQUAL_TO_REF_VALUE:
return new_val != ref_val;
default:
return false;
}
}
static void update_temperature(struct bt_conn *conn,
const struct bt_gatt_attr *chrc, int16_t value,
struct temperature_sensor *sensor)
{
bool notify = check_condition(sensor->condition,
sensor->temp_value, value,
sensor->ref_val);
/* Update temperature value */
sensor->temp_value = value;
/* Trigger notification if conditions are met */
if (notify) {
value = sys_cpu_to_le16(sensor->temp_value);
bt_gatt_notify(conn, chrc, &value, sizeof(value));
}
}
static struct bt_gatt_attr ess_attrs[] = {
BT_GATT_PRIMARY_SERVICE(BT_UUID_ESS),
/* Temperature Sensor 1 */
BT_GATT_CHARACTERISTIC(BT_UUID_TEMPERATURE,
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY),
BT_GATT_DESCRIPTOR(BT_UUID_TEMPERATURE, BT_GATT_PERM_READ,
read_u16, NULL, &sensor_1.temp_value),
BT_GATT_DESCRIPTOR(BT_UUID_ES_MEASUREMENT, BT_GATT_PERM_READ,
read_es_measurement, NULL, &sensor_1.meas),
BT_GATT_CUD(SENSOR_1_NAME, BT_GATT_PERM_READ),
BT_GATT_DESCRIPTOR(BT_UUID_VALID_RANGE, BT_GATT_PERM_READ,
read_temp_valid_range, NULL, &sensor_1),
BT_GATT_DESCRIPTOR(BT_UUID_ES_TRIGGER_SETTING,
BT_GATT_PERM_READ, read_temp_trigger_setting,
NULL, &sensor_1),
BT_GATT_CCC(sensor_1.ccc_cfg, temp_ccc_cfg_changed),
/* Temperature Sensor 2 */
BT_GATT_CHARACTERISTIC(BT_UUID_TEMPERATURE,
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY),
BT_GATT_DESCRIPTOR(BT_UUID_TEMPERATURE, BT_GATT_PERM_READ,
read_u16, NULL, &sensor_2.temp_value),
BT_GATT_DESCRIPTOR(BT_UUID_ES_MEASUREMENT, BT_GATT_PERM_READ,
read_es_measurement, NULL, &sensor_2.meas),
BT_GATT_CUD(SENSOR_2_NAME, BT_GATT_PERM_READ),
BT_GATT_DESCRIPTOR(BT_UUID_VALID_RANGE, BT_GATT_PERM_READ,
read_temp_valid_range, NULL, &sensor_2),
BT_GATT_DESCRIPTOR(BT_UUID_ES_TRIGGER_SETTING,
BT_GATT_PERM_READ, read_temp_trigger_setting,
NULL, &sensor_2),
BT_GATT_CCC(sensor_2.ccc_cfg, temp_ccc_cfg_changed),
/* Humidity Sensor */
BT_GATT_CHARACTERISTIC(BT_UUID_HUMIDITY, BT_GATT_CHRC_READ),
BT_GATT_DESCRIPTOR(BT_UUID_HUMIDITY, BT_GATT_PERM_READ,
read_u16, NULL, &sensor_3.humid_value),
BT_GATT_CUD(SENSOR_3_NAME, BT_GATT_PERM_READ),
BT_GATT_DESCRIPTOR(BT_UUID_ES_MEASUREMENT, BT_GATT_PERM_READ,
read_es_measurement, NULL, &sensor_3.meas),
};
static void ess_simulate(void)
{
static uint8_t i;
uint16_t val;
if (!(i % SENSOR_1_UPDATE_IVAL)) {
val = 1200 + i;
update_temperature(NULL, &ess_attrs[2], val, &sensor_1);
}
if (!(i % SENSOR_2_UPDATE_IVAL)) {
val = 1800 + i;
update_temperature(NULL, &ess_attrs[9], val, &sensor_2);
}
if (!(i % SENSOR_3_UPDATE_IVAL)) {
sensor_3.humid_value = 6233 + (i % 13);
}
if (!(i % INT8_MAX)) {
i = 0;
}
i++;
}
static const struct bt_data ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA_BYTES(BT_DATA_GAP_APPEARANCE, 0x00, 0x03),
BT_DATA_BYTES(BT_DATA_UUID16_ALL, 0x1a, 0x18),
/* TODO: Include Service Data AD */
};
static struct bt_data sd[] = {
BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
};
static void connected(struct bt_conn *conn, uint8_t err)
{
if (err) {
printk("Connection failed (err %u)\n", err);
} else {
printk("Connected\n");
}
}
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
printk("Disconnected (reason %u)\n", reason);
}
static struct bt_conn_cb conn_callbacks = {
.connected = connected,
.disconnected = disconnected,
};
static void bt_ready(int err)
{
if (err) {
printk("Bluetooth init failed (err %d)\n", err);
return;
}
printk("Bluetooth initialized\n");
gap_init(DEVICE_NAME, APPEARANCE_THERMOMETER);
bt_gatt_register(ess_attrs, ARRAY_SIZE(ess_attrs));
bas_init();
dis_init(CONFIG_SOC, "ACME");
err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad),
sd, ARRAY_SIZE(sd));
if (err) {
printk("Advertising failed to start (err %d)\n", err);
return;
}
printk("Advertising successfully started\n");
}
static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
printk("Passkey for %s: %u\n", addr, passkey);
}
static void auth_cancel(struct bt_conn *conn)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
printk("Pairing cancelled: %s\n", addr);
}
static struct bt_conn_auth_cb auth_cb_display = {
.passkey_display = auth_passkey_display,
.passkey_entry = NULL,
.cancel = auth_cancel,
};
void main(void)
{
int err;
err = bt_enable(bt_ready);
if (err) {
printk("Bluetooth init failed (err %d)\n", err);
return;
}
bt_conn_cb_register(&conn_callbacks);
bt_conn_auth_cb_register(&auth_cb_display);
while (1) {
task_sleep(sys_clock_ticks_per_sec);
/* Temperature simulation */
if (simulate_temp) {
ess_simulate();
}
/* Battery level simulation */
bas_notify();
}
}