blob: 0ac62a42627eff2bb257b4ef84d107c1574bef29 [file] [log] [blame]
/* main.c - Application main entry point */
/*
* Copyright (c) 2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdbool.h>
#include <zephyr/types.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/services/bas.h>
#define SENSOR_1_NAME "Temperature Sensor 1"
#define SENSOR_2_NAME "Temperature Sensor 2"
#define SENSOR_3_NAME "Humidity Sensor"
/* 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_TRIGGER_FIXED_TIME_INTERVAL 0x01
#define ESS_TRIGGER_NO_LESS_THAN_SPECIFIED_TIME 0x02
#define ESS_TRIGGER_VALUE_CHANGED 0x03
#define ESS_TRIGGER_LESS_THAN_REF_VALUE 0x04
#define ESS_TRIGGER_LESS_OR_EQUAL_TO_REF_VALUE 0x05
#define ESS_TRIGGER_GREATER_THAN_REF_VALUE 0x06
#define ESS_TRIGGER_GREATER_OR_EQUAL_TO_REF_VALUE 0x07
#define ESS_TRIGGER_EQUAL_TO_REF_VALUE 0x08
#define ESS_TRIGGER_NOT_EQUAL_TO_REF_VALUE 0x09
/* ESS Measurement Descriptor – Sampling Functions */
#define ESS_DESC_SAMPLING_UNSPECIFIED 0x00
#define ESS_DESC_SAMPLING_INSTANTANEOUS 0x01
#define ESS_DESC_SAMPLING_ARITHMETIC_MEAN 0x02
#define ESS_DESC_SAMPLING_RMS 0x03
#define ESS_DESC_SAMPLING_MAXIMUM 0x04
#define ESS_DESC_SAMPLING_MINIMUM 0x05
#define ESS_DESC_SAMPLING_ACCUMULATED 0x06
#define ESS_DESC_SAMPLING_COUNT 0x07
/* ES Measurement Descriptor - Applications */
#define ESS_DESC_APP_UNSPECIFIED 0x00
#define ESS_DESC_APP_AIR 0x01
#define ESS_DESC_APP_WATER 0x02
#define ESS_DESC_APP_BAROMETRIC 0x03
#define ESS_DESC_APP_SOIL 0x04
#define ESS_DESC_APP_INFRARED 0x05
#define ESS_DESC_APP_MAP_DATABASE 0x06
#define ESS_DESC_APP_BAROMETRIC_ELEVATION_SOURCE 0x07
#define ESS_DESC_APP_GPS_ONLY_ELEVATION_SOURCE 0x08
#define ESS_DESC_APP_GPS_AND_MAP_DATABASE_ELEVATION_SOURCE 0x09
#define ESS_DESC_APP_VERTICAL_DATUM_ELEVATION_SOURCE 0x0A
#define ESS_DESC_APP_ONSHORE 0x0B
#define ESS_DESC_APP_ONBOARD_VESSEL_OR_VEHICLE 0x0C
#define ESS_DESC_APP_FRONT 0x0D
#define ESS_DESC_APP_BACK_REAR 0x0E
#define ESS_DESC_APP_UPPER 0x0F
#define ESS_DESC_APP_LOWER 0x10
#define ESS_DESC_APP_PRIMARY 0x11
#define ESS_DESC_APP_SECONDARY 0x12
#define ESS_DESC_APP_OUTDOOR 0x13
#define ESS_DESC_APP_INDOOR 0x14
#define ESS_DESC_APP_TOP 0x15
#define ESS_DESC_APP_BOTTOM 0x16
#define ESS_DESC_APP_MAIN 0x17
#define ESS_DESC_APP_BACKUP 0x18
#define ESS_DESC_APP_AUXILIARY 0x19
#define ESS_DESC_APP_SUPPLEMENTARY 0x1A
#define ESS_DESC_APP_INSIDE 0x1B
#define ESS_DESC_APP_OUTSIDE 0x1C
#define ESS_DESC_APP_LEFT 0x1D
#define ESS_DESC_APP_RIGHT 0x1E
#define ESS_DESC_APP_INTERNAL 0x1F
#define ESS_DESC_APP_EXTERNAL 0x20
#define ESS_DESC_APP_SOLAR 0x21
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 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_TRIGGER_VALUE_CHANGED,
.meas.sampling_func = ESS_DESC_SAMPLING_UNSPECIFIED,
.meas.meas_period = 0x01,
.meas.update_interval = SENSOR_1_UPDATE_IVAL,
.meas.application = ESS_DESC_APP_OUTSIDE,
.meas.meas_uncertainty = 0x04,
};
static struct temperature_sensor sensor_2 = {
.temp_value = 1800,
.lower_limit = -1000,
.upper_limit = 5000,
.condition = ESS_TRIGGER_VALUE_CHANGED,
.meas.sampling_func = ESS_DESC_SAMPLING_UNSPECIFIED,
.meas.meas_period = 0x01,
.meas.update_interval = SENSOR_2_UPDATE_IVAL,
.meas.application = ESS_DESC_APP_INSIDE,
.meas.meas_uncertainty = 0x04,
};
static struct humidity_sensor sensor_3 = {
.humid_value = 6233,
.meas.sampling_func = ESS_DESC_SAMPLING_ARITHMETIC_MEAN,
.meas.meas_period = 0x0e10,
.meas.update_interval = SENSOR_3_UPDATE_IVAL,
.meas.application = ESS_DESC_APP_OUTSIDE,
.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;
sys_put_le24(value->meas_period, rsp.measurement_period);
sys_put_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_TRIGGER_VALUE_CHANGED:
return bt_gatt_attr_read(conn, attr, buf, len, offset,
&sensor->condition,
sizeof(sensor->condition));
/* Seconds */
case ESS_TRIGGER_FIXED_TIME_INTERVAL:
__fallthrough;
case ESS_TRIGGER_NO_LESS_THAN_SPECIFIED_TIME: {
struct es_trigger_setting_seconds rp;
rp.condition = sensor->condition;
sys_put_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_TRIGGER_FIXED_TIME_INTERVAL:
case ESS_TRIGGER_NO_LESS_THAN_SPECIFIED_TIME:
/* TODO: Check time requirements */
return false;
case ESS_TRIGGER_VALUE_CHANGED:
return new_val != old_val;
case ESS_TRIGGER_LESS_THAN_REF_VALUE:
return new_val < ref_val;
case ESS_TRIGGER_LESS_OR_EQUAL_TO_REF_VALUE:
return new_val <= ref_val;
case ESS_TRIGGER_GREATER_THAN_REF_VALUE:
return new_val > ref_val;
case ESS_TRIGGER_GREATER_OR_EQUAL_TO_REF_VALUE:
return new_val >= ref_val;
case ESS_TRIGGER_EQUAL_TO_REF_VALUE:
return new_val == ref_val;
case ESS_TRIGGER_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));
}
}
BT_GATT_SERVICE_DEFINE(ess_svc,
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_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(temp_ccc_cfg_changed,
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
/* Temperature Sensor 2 */
BT_GATT_CHARACTERISTIC(BT_UUID_TEMPERATURE,
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
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(temp_ccc_cfg_changed,
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
/* Humidity Sensor */
BT_GATT_CHARACTERISTIC(BT_UUID_HUMIDITY, BT_GATT_CHRC_READ,
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_svc.attrs[2], val, &sensor_1);
}
if (!(i % SENSOR_2_UPDATE_IVAL)) {
val = 1800 + i;
update_temperature(NULL, &ess_svc.attrs[9], val, &sensor_2);
}
if (!(i % SENSOR_3_UPDATE_IVAL)) {
sensor_3.humid_value = 6233 + (i % 13);
}
if (!(i % INT8_MAX)) {
i = 0U;
}
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,
BT_UUID_16_ENCODE(BT_UUID_ESS_VAL),
BT_UUID_16_ENCODE(BT_UUID_BAS_VAL)),
};
static const struct bt_data sd[] = {
BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
};
static void connected(struct bt_conn *conn, uint8_t err)
{
if (err) {
printk("Connection failed, err 0x%02x %s\n", err, bt_hci_err_to_str(err));
} else {
printk("Connected\n");
}
}
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
printk("Disconnected, reason 0x%02x %s\n", reason, bt_hci_err_to_str(reason));
}
BT_CONN_CB_DEFINE(conn_callbacks) = {
.connected = connected,
.disconnected = disconnected,
};
static void bt_ready(void)
{
int err;
printk("Bluetooth initialized\n");
err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, 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: %06u\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,
};
static void bas_notify(void)
{
uint8_t battery_level = bt_bas_get_battery_level();
battery_level--;
if (!battery_level) {
battery_level = 100U;
}
bt_bas_set_battery_level(battery_level);
}
int main(void)
{
int err;
err = bt_enable(NULL);
if (err) {
printk("Bluetooth init failed (err %d)\n", err);
return 0;
}
bt_ready();
bt_conn_auth_cb_register(&auth_cb_display);
while (1) {
k_sleep(K_SECONDS(1));
/* Temperature simulation */
if (simulate_temp) {
ess_simulate();
}
/* Battery level simulation */
bas_notify();
}
return 0;
}