|  | /* 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; | 
|  | } |