diff options
Diffstat (limited to 'src/libs/mynewt-nimble/apps/blecsc/src/main.c')
-rw-r--r-- | src/libs/mynewt-nimble/apps/blecsc/src/main.c | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/src/libs/mynewt-nimble/apps/blecsc/src/main.c b/src/libs/mynewt-nimble/apps/blecsc/src/main.c new file mode 100644 index 00000000..60f0b3d8 --- /dev/null +++ b/src/libs/mynewt-nimble/apps/blecsc/src/main.c @@ -0,0 +1,310 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 <assert.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> + +#include "os/mynewt.h" +#include "console/console.h" +#include "config/config.h" +#include "nimble/ble.h" +#include "host/ble_hs.h" +#include "services/gap/ble_svc_gap.h" +#include "blecsc_sens.h" + +/* Wheel size for simulation calculations */ +#define CSC_SIM_WHEEL_CIRCUMFERENCE_MM 2000 +/* Simulated cadence lower limit */ +#define CSC_SIM_CRANK_RPM_MIN 20 +/* Simulated cadence upper limit */ +#define CSC_SIM_CRANK_RPM_MAX 100 +/* Simulated speed lower limit */ +#define CSC_SIM_SPEED_KPH_MIN 0 +/* Simulated speed upper limit */ +#define CSC_SIM_SPEED_KPH_MAX 35 + +/* Noticication status */ +static bool notify_state = false; + +/* Connection handle */ +static uint16_t conn_handle; + +static uint8_t blecsc_addr_type; + +/* Advertised device name */ +static const char *device_name = "blecsc_sensor"; + +/* Measurement and notification timer */ +static struct os_callout blecsc_measure_timer; + +/* Variable holds current CSC measurement state */ +static struct ble_csc_measurement_state csc_measurement_state; + +/* Variable holds simulted speed (kilometers per hour) */ +static uint16_t csc_sim_speed_kph = CSC_SIM_SPEED_KPH_MIN; + +/* Variable holds simulated cadence (RPM) */ +static uint8_t csc_sim_crank_rpm = CSC_SIM_CRANK_RPM_MIN; + +static int blecsc_gap_event(struct ble_gap_event *event, void *arg); + + +/* + * Enables advertising with parameters: + * o General discoverable mode + * o Undirected connectable mode + */ +static void +blecsc_advertise(void) +{ + struct ble_gap_adv_params adv_params; + struct ble_hs_adv_fields fields; + int rc; + + /* + * Set the advertisement data included in our advertisements: + * o Flags (indicates advertisement type and other general info) + * o Advertising tx power + * o Device name + */ + memset(&fields, 0, sizeof(fields)); + + /* + * Advertise two flags: + * o Discoverability in forthcoming advertisement (general) + * o BLE-only (BR/EDR unsupported) + */ + fields.flags = BLE_HS_ADV_F_DISC_GEN | + BLE_HS_ADV_F_BREDR_UNSUP; + + /* + * Indicate that the TX power level field should be included; have the + * stack fill this value automatically. This is done by assigning the + * special value BLE_HS_ADV_TX_PWR_LVL_AUTO. + */ + fields.tx_pwr_lvl_is_present = 1; + fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO; + + fields.name = (uint8_t *)device_name; + fields.name_len = strlen(device_name); + fields.name_is_complete = 1; + + /* + * Set appearance. + */ + fields.appearance = ble_svc_gap_device_appearance(); + fields.appearance_is_present = 1; + + rc = ble_gap_adv_set_fields(&fields); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc); + return; + } + + /* Begin advertising */ + memset(&adv_params, 0, sizeof(adv_params)); + adv_params.conn_mode = BLE_GAP_CONN_MODE_UND; + adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN; + rc = ble_gap_adv_start(blecsc_addr_type, NULL, BLE_HS_FOREVER, + &adv_params, blecsc_gap_event, NULL); + if (rc != 0) { + MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc); + return; + } +} + + +/* Update simulated CSC measurements. + * Each call increments wheel and crank revolution counters by one and + * computes last event time in order to match simulated candence and speed. + * Last event time is expressedd in 1/1024th of second units. + * + * 60 * 1024 + * crank_dt = -------------- + * cadence[RPM] + * + * + * circumference[mm] * 1024 * 60 * 60 + * wheel_dt = ------------------------------------- + * 10^6 * speed [kph] + */ +static void +blecsc_simulate_speed_and_cadence(void) +{ + uint16_t wheel_rev_period; + uint16_t crank_rev_period; + + /* Update simulated crank and wheel rotation speed */ + csc_sim_speed_kph++; + if (csc_sim_speed_kph >= CSC_SIM_SPEED_KPH_MAX) { + csc_sim_speed_kph = CSC_SIM_SPEED_KPH_MIN; + } + + csc_sim_crank_rpm++; + if (csc_sim_crank_rpm >= CSC_SIM_CRANK_RPM_MAX) { + csc_sim_crank_rpm = CSC_SIM_CRANK_RPM_MIN; + } + + /* Calculate simulated measurement values */ + if (csc_sim_speed_kph > 0){ + wheel_rev_period = (36*64*CSC_SIM_WHEEL_CIRCUMFERENCE_MM) / + (625*csc_sim_speed_kph); + csc_measurement_state.cumulative_wheel_rev++; + csc_measurement_state.last_wheel_evt_time += wheel_rev_period; + } + + if (csc_sim_crank_rpm > 0){ + crank_rev_period = (60*1024) / csc_sim_crank_rpm; + csc_measurement_state.cumulative_crank_rev++; + csc_measurement_state.last_crank_evt_time += crank_rev_period; + } + + MODLOG_DFLT(INFO, "CSC simulated values: speed = %d kph, cadence = %d \n", + csc_sim_speed_kph, csc_sim_crank_rpm); +} + +/* Run CSC measurement simulation and notify it to the client */ +static void +blecsc_measurement(struct os_event *ev) +{ + int rc; + + rc = os_callout_reset(&blecsc_measure_timer, OS_TICKS_PER_SEC); + assert(rc == 0); + + blecsc_simulate_speed_and_cadence(); + + if (notify_state) { + rc = gatt_svr_chr_notify_csc_measurement(conn_handle); + assert(rc == 0); + } +} + +static int +blecsc_gap_event(struct ble_gap_event *event, void *arg) +{ + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed */ + MODLOG_DFLT(INFO, "connection %s; status=%d\n", + event->connect.status == 0 ? "established" : "failed", + event->connect.status); + + if (event->connect.status != 0) { + /* Connection failed; resume advertising */ + blecsc_advertise(); + conn_handle = 0; + } + else { + conn_handle = event->connect.conn_handle; + } + break; + + case BLE_GAP_EVENT_DISCONNECT: + MODLOG_DFLT(INFO, "disconnect; reason=%d\n", event->disconnect.reason); + conn_handle = 0; + /* Connection terminated; resume advertising */ + blecsc_advertise(); + break; + + case BLE_GAP_EVENT_ADV_COMPLETE: + MODLOG_DFLT(INFO, "adv complete\n"); + break; + + case BLE_GAP_EVENT_SUBSCRIBE: + MODLOG_DFLT(INFO, "subscribe event attr_handle=%d\n", + event->subscribe.attr_handle); + + if (event->subscribe.attr_handle == csc_measurement_handle) { + notify_state = event->subscribe.cur_notify; + MODLOG_DFLT(INFO, "csc measurement notify state = %d\n", + notify_state); + } + else if (event->subscribe.attr_handle == csc_control_point_handle) { + gatt_svr_set_cp_indicate(event->subscribe.cur_indicate); + MODLOG_DFLT(INFO, "csc control point indicate state = %d\n", + event->subscribe.cur_indicate); + } + break; + + case BLE_GAP_EVENT_MTU: + MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d mtu=%d\n", + event->mtu.conn_handle, + event->mtu.value); + break; + + } + + return 0; +} + +static void +blecsc_on_sync(void) +{ + int rc; + + /* Figure out address to use while advertising (no privacy) */ + rc = ble_hs_id_infer_auto(0, &blecsc_addr_type); + assert(rc == 0); + + /* Begin advertising */ + blecsc_advertise(); +} + +/* + * main + * + * The main task for the project. This function initializes the packages, + * then starts serving events from default event queue. + * + * @return int NOTE: this function should never return! + */ +int +main(void) +{ + int rc; + + /* Initialize OS */ + sysinit(); + + /* Initialize the NimBLE host configuration */ + ble_hs_cfg.sync_cb = blecsc_on_sync; + + /* Initialize measurement and notification timer */ + os_callout_init(&blecsc_measure_timer, os_eventq_dflt_get(), + blecsc_measurement, NULL); + rc = os_callout_reset(&blecsc_measure_timer, OS_TICKS_PER_SEC); + assert(rc == 0); + + rc = gatt_svr_init(&csc_measurement_state); + assert(rc == 0); + + /* Set the default device name */ + rc = ble_svc_gap_device_name_set(device_name); + assert(rc == 0); + + /* As the last thing, process events from default event queue */ + while (1) { + os_eventq_run(os_eventq_dflt_get()); + } + return 0; +} + |