/* main.c - Application main entry point */ /* * Copyright (c) 2016 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; }