Add Nimble in libs directory

This commit is contained in:
JF 2020-04-26 10:25:59 +02:00
parent 032fad094c
commit bdc10744fb
633 changed files with 228083 additions and 834 deletions

View file

@ -1,783 +0,0 @@
#if 0
#include <libraries/util/sdk_errors.h>
#include <softdevice/common/nrf_sdh.h>
#include <libraries/util/app_error.h>
#include <softdevice/common/nrf_sdh_ble.h>
#include <libraries/log/nrf_log.h>
#include <ble/nrf_ble_qwr/nrf_ble_qwr.h>
#include <ble/ble_services/ble_cts_c/ble_cts_c.h>
#include <ble/nrf_ble_gatt/nrf_ble_gatt.h>
#include <ble/ble_advertising/ble_advertising.h>
#include <ble/peer_manager/peer_manager.h>
#include <ble/peer_manager/peer_manager_handler.h>
#include <ble/ble_services/ble_hrs/ble_hrs.h>
#include <ble/ble_services/ble_bas/ble_bas.h>
#include <ble/ble_services/ble_dis/ble_dis.h>
#include <ble/ble_services/ble_ans_c/ble_ans_c.h>
#include <ble/common/ble_conn_params.h>
#include <libraries/fds/fds.h>
#include "nrf_sdh_soc.h"
#include "BleManager.h"
void ble_manager_init_stack();
void ble_manager_init_gap_params();
void ble_manager_init_gatt();
void ble_manager_init_db_discovery();
void ble_manager_init_advertising();
void ble_manager_init_peer_manager();
void ble_manager_init_services();
void ble_manager_init_connection_params();
void ble_manager_event_handler(ble_evt_t const *p_ble_evt, void *p_context);
void ble_manager_discover_handler(ble_db_discovery_evt_t *p_evt);
void ble_manager_advertising_event_handler(ble_adv_evt_t ble_adv_evt);
void ble_manager_peer_manager_event_handler(pm_evt_t const *p_evt);
void ble_manager_delete_bonds();
void ble_manager_queue_write_error_handler(uint32_t nrf_error);
void ble_manager_cts_event_handler(ble_cts_c_t *p_cts, ble_cts_c_evt_t *p_evt);
void ble_manager_cts_error_handler(uint32_t nrf_error);
void ble_manager_cts_print_time(ble_cts_c_evt_t *p_evt);
void ble_manager_conn_params_event_handler(ble_conn_params_evt_t *p_evt);
void ble_manager_conn_params_error_handler(uint32_t nrf_error);
typedef enum
{
ALERT_NOTIFICATION_DISABLED, /**< Alert Notifications has been disabled. */
ALERT_NOTIFICATION_ENABLED, /**< Alert Notifications has been enabled. */
ALERT_NOTIFICATION_ON, /**< Alert State is on. */
} ble_ans_c_alert_state_t;
void on_ans_c_evt(ble_ans_c_evt_t * p_evt);
void alert_notification_error_handler(uint32_t nrf_error);
void handle_alert_notification(ble_ans_c_evt_t * p_evt);
void supported_alert_notification_read(void);
void alert_notification_setup(void);
void control_point_setup(ble_ans_c_evt_t * p_evt);
uint16_t ble_manager_connection_handle = BLE_CONN_HANDLE_INVALID; // Handle of the current connection.
NRF_BLE_QWR_DEF(ble_manager_queue_write); // Context for the Queued Write module.
BLE_CTS_C_DEF(ble_manager_cts_client); // Current Time service instance.
NRF_BLE_GATT_DEF(ble_manager_gatt); // GATT module instance.
BLE_ADVERTISING_DEF(ble_manager_advertising); // Advertising module instance.
BLE_DB_DISCOVERY_DEF(ble_manager_db_discovery);
BLE_ANS_C_DEF(m_ans_c);
static uint8_t m_alert_message_buffer[MESSAGE_BUFFER_SIZE]; /**< Message buffer for optional notify messages. */
static ble_ans_c_alert_state_t m_new_alert_state = ALERT_NOTIFICATION_DISABLED; /**< State that holds the current state of New Alert Notifications, i.e. Enabled, Alert On, Disabled. */
static ble_ans_c_alert_state_t m_unread_alert_state = ALERT_NOTIFICATION_DISABLED; /**< State that holds the current state of Unread Alert Notifications, i.e. Enabled, Alert On, Disabled. */
static ble_uuid_t ble_manager_advertising_uuids[] = /* Universally unique service identifiers.*/
{
// {BLE_UUID_HEART_RATE_SERVICE, BLE_UUID_TYPE_BLE},
// {BLE_UUID_BATTERY_SERVICE, BLE_UUID_TYPE_BLE},
{BLE_UUID_DEVICE_INFORMATION_SERVICE, BLE_UUID_TYPE_BLE},
{BLE_UUID_CURRENT_TIME_SERVICE, BLE_UUID_TYPE_BLE}
};
static char const *day_of_week[] =
{
"Unknown",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday"
};
static char const *month_of_year[] =
{
"Unknown",
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
};
static char const * lit_catid[BLE_ANS_NB_OF_CATEGORY_ID] =
{
"Simple alert",
"Email",
"News",
"Incoming call",
"Missed call",
"SMS/MMS",
"Voice mail",
"Schedule",
"High prioritized alert",
"Instant message"
};
void ble_manager_init() {
ble_manager_init_stack();
ble_manager_init_gap_params();
ble_manager_init_gatt();
ble_manager_init_db_discovery();
ble_manager_init_advertising();
ble_manager_init_services();
ble_manager_init_connection_params();
}
void ble_manager_init_stack() {
ret_code_t err_code;
err_code = nrf_sdh_enable_request();
APP_ERROR_CHECK(err_code);
// Configure the BLE stack using the default settings.
// Fetch the start address of the application RAM.
uint32_t ram_start = 0;
err_code = nrf_sdh_ble_default_cfg_set(BLE_MANAGER_CONN_CFG_TAG, &ram_start);
APP_ERROR_CHECK(err_code);
// Enable BLE stack.
err_code = nrf_sdh_ble_enable(&ram_start);
APP_ERROR_CHECK(err_code);
// Register a handler for BLE events.
NRF_SDH_BLE_OBSERVER(m_ble_observer, BLE_MANAGER__OBSERVER_PRIO, ble_manager_event_handler, NULL);
}
void (*OnNewTimeCallback)(current_time_char_t*);
void ble_manager_set_new_time_callback(void (*OnNewTime)(current_time_char_t*)) {
OnNewTimeCallback = OnNewTime;
}
void (*OnBleConnectionCallback)();
void ble_manager_set_ble_connection_callback(void (*OnBleConnection)()) {
OnBleConnectionCallback = OnBleConnection;
}
void (*OnBleDisconnectionCallback)();
void ble_manager_set_ble_disconnection_callback(void (*OnBleDisconnection)()) {
OnBleDisconnectionCallback = OnBleDisconnection;
}
void (*OnNewNotificationCallback)(const char* message, uint8_t size);
void ble_manager_set_new_notification_callback(void (*OnNewNotification)(const char*, uint8_t size)) {
OnNewNotificationCallback = OnNewNotification;
}
void ble_manager_event_handler(ble_evt_t const *p_ble_evt, void *p_context) {
uint32_t err_code;
switch (p_ble_evt->header.evt_id) {
case BLE_GAP_EVT_CONNECTED:
NRF_LOG_INFO("[BLE] Connected to peer");
ble_manager_connection_handle = p_ble_evt->evt.gap_evt.conn_handle;
err_code = nrf_ble_qwr_conn_handle_assign(&ble_manager_queue_write, ble_manager_connection_handle);
OnBleConnectionCallback();
APP_ERROR_CHECK(err_code);
break;
case BLE_GAP_EVT_DISCONNECTED:
NRF_LOG_INFO("[Ble] Disconnected from peer]");
ble_manager_connection_handle = BLE_CONN_HANDLE_INVALID;
if (p_ble_evt->evt.gap_evt.conn_handle == ble_manager_cts_client.conn_handle) {
ble_manager_cts_client.conn_handle = BLE_CONN_HANDLE_INVALID;
}
OnBleDisconnectionCallback();
break;
case BLE_GAP_EVT_PHY_UPDATE_REQUEST: {
NRF_LOG_INFO("[BLE] PHY update request.");
ble_gap_phys_t const phys =
{
.tx_phys = BLE_GAP_PHY_AUTO,
.rx_phys = BLE_GAP_PHY_AUTO,
};
err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
APP_ERROR_CHECK(err_code);
}
break;
case BLE_GATTC_EVT_TIMEOUT:
// Disconnect on GATT Client timeout event.
NRF_LOG_INFO("[BLE] GATT Client Timeout.");
err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
APP_ERROR_CHECK(err_code);
break;
case BLE_GATTS_EVT_TIMEOUT:
// Disconnect on GATT Server timeout event.
NRF_LOG_INFO("[BLE] GATT Server Timeout.");
err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
APP_ERROR_CHECK(err_code);
break;
default:
// No implementation needed.
break;
}
}
void ble_manager_init_gap_params() {
ret_code_t err_code;
ble_gap_conn_params_t gap_conn_params;
ble_gap_conn_sec_mode_t sec_mode;
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
err_code = sd_ble_gap_device_name_set(&sec_mode, (const uint8_t *) BLE_MANAGER_DEVICE_NAME,
strlen(BLE_MANAGER_DEVICE_NAME));
APP_ERROR_CHECK(err_code);
err_code = sd_ble_gap_appearance_set(BLE_APPEARANCE_HEART_RATE_SENSOR_HEART_RATE_BELT);
APP_ERROR_CHECK(err_code);
memset(&gap_conn_params, 0, sizeof(gap_conn_params));
gap_conn_params.min_conn_interval = BLE_MANAGER_MIN_CONN_INTERVAL;
gap_conn_params.max_conn_interval = BLE_MANAGER_MAX_CONN_INTERVAL;
gap_conn_params.slave_latency = BLE_MANAGER_SLAVE_LATENCY;
gap_conn_params.conn_sup_timeout = BLE_MANAGER_CONN_SUP_TIMEOUT;
err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
APP_ERROR_CHECK(err_code);
}
void ble_manager_init_gatt() {
ret_code_t err_code = nrf_ble_gatt_init(&ble_manager_gatt, NULL);
APP_ERROR_CHECK(err_code);
}
void ble_manager_init_db_discovery() {
ret_code_t err_code = ble_db_discovery_init(ble_manager_discover_handler);
APP_ERROR_CHECK(err_code);
}
void ble_manager_discover_handler(ble_db_discovery_evt_t *p_evt) {
ble_cts_c_on_db_disc_evt(&ble_manager_cts_client, p_evt);
ble_ans_c_on_db_disc_evt(&m_ans_c, p_evt);
}
void ble_manager_init_advertising() {
ret_code_t err_code;
ble_advertising_init_t init;
memset(&init, 0, sizeof(init));
init.advdata.name_type = BLE_ADVDATA_FULL_NAME;
init.advdata.include_appearance = true;
init.advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
init.advdata.uuids_complete.uuid_cnt =
sizeof(ble_manager_advertising_uuids) / sizeof(ble_manager_advertising_uuids[0]);
init.advdata.uuids_complete.p_uuids = ble_manager_advertising_uuids;
init.config.ble_adv_whitelist_enabled = true;
init.config.ble_adv_fast_enabled = true;
init.config.ble_adv_fast_interval = BLE_MANAGER_ADV_INTERVAL;
init.config.ble_adv_fast_timeout = BLE_MANAGER_ADV_DURATION;
init.evt_handler = ble_manager_advertising_event_handler;
err_code = ble_advertising_init(&ble_manager_advertising, &init);
APP_ERROR_CHECK(err_code);
ble_advertising_conn_cfg_tag_set(&ble_manager_advertising, BLE_MANAGER_CONN_CFG_TAG);
}
void ble_manager_advertising_event_handler(ble_adv_evt_t ble_adv_evt) {
uint32_t err_code;
switch (ble_adv_evt) {
case BLE_ADV_EVT_FAST:
NRF_LOG_INFO("[Advertising] Fast advertising started.");
break;
case BLE_ADV_EVT_IDLE:
NRF_LOG_INFO("[Advertising] Idling...");
break;
default:
break;
}
}
bool record_delete_next(void)
{
fds_find_token_t tok = {0};
fds_record_desc_t desc = {0};
if (fds_record_iterate(&desc, &tok) == FDS_SUCCESS)
{
ret_code_t rc = fds_record_delete(&desc);
if (rc != FDS_SUCCESS)
{
return false;
}
return true;
}
else
{
/* No records left to delete. */
return false;
}
}
void ble_manager_init_peer_manager() {
ble_gap_sec_params_t sec_param;
ret_code_t err_code;
err_code = pm_init();
if(err_code != NRF_SUCCESS) {
// Many errors can occur here, but the most probable is the "Storage full" error from sdk/components/libraries/fds/fds.c : FDS_ERR_NO_PAGES.
// TODO : it erases the whole memory, it's not nice to do that...
NRF_LOG_WARNING("Error while initializing BLE peer management, Erasing all record from memory");
do {
} while(record_delete_next());
err_code = pm_init();
}
APP_ERROR_CHECK(err_code);
memset(&sec_param, 0, sizeof(ble_gap_sec_params_t));
// Security parameters to be used for all security procedures.
sec_param.bond = BLE_MANAGER_SEC_PARAM_BOND;
sec_param.mitm = BLE_MANAGER_SEC_PARAM_MITM;
sec_param.lesc = BLE_MANAGER_SEC_PARAM_LESC;
sec_param.keypress = BLE_MANAGER_SEC_PARAM_KEYPRESS;
sec_param.io_caps = BLE_MANAGER_SEC_PARAM_IO_CAPABILITIES;
sec_param.oob = BLE_MANAGER_SEC_PARAM_OOB;
sec_param.min_key_size = BLE_MANAGER_SEC_PARAM_MIN_KEY_SIZE;
sec_param.max_key_size = BLE_MANAGER_SEC_PARAM_MAX_KEY_SIZE;
sec_param.kdist_own.enc = 1;
sec_param.kdist_own.id = 1;
sec_param.kdist_peer.enc = 1;
sec_param.kdist_peer.id = 1;
err_code = pm_sec_params_set(&sec_param);
APP_ERROR_CHECK(err_code);
err_code = pm_register(ble_manager_peer_manager_event_handler);
APP_ERROR_CHECK(err_code);
}
void ble_manager_peer_manager_event_handler(pm_evt_t const *p_evt) {
bool delete_bonds = false;
ret_code_t err_code;
pm_handler_on_pm_evt(p_evt);
pm_handler_flash_clean(p_evt);
switch (p_evt->evt_id) {
case PM_EVT_CONN_SEC_SUCCEEDED: {
NRF_LOG_INFO("[Peer management] A link has been secured, starting service discovery.");
err_code = ble_db_discovery_start(&ble_manager_db_discovery, p_evt->conn_handle);
APP_ERROR_CHECK(err_code);
}
break;
case PM_EVT_PEERS_DELETE_SUCCEEDED:
NRF_LOG_INFO("[Peer management] All peers data has been successfuly deleted.");
ble_manager_start_advertising(&delete_bonds);
break;
case PM_EVT_STORAGE_FULL: {
NRF_LOG_INFO("[Peer management] Storage full, trying to run garbage collection on flash storage.");
// Run garbage collection on the flash.
err_code = fds_gc();
if (err_code == FDS_ERR_BUSY || err_code == FDS_ERR_NO_SPACE_IN_QUEUES)
{
NRF_LOG_INFO("[Peer management] Garbage collection issue.");
// Retry.
}
else
{
APP_ERROR_CHECK(err_code);
NRF_LOG_INFO("[Peer management] Garbage collection done.");
}
}break;
default:
break;
}
}
void ble_manager_start_advertising(void *p_erase_bonds) {
bool erase_bonds = *(bool *) p_erase_bonds;
if (erase_bonds) {
ble_manager_delete_bonds();
// Advertising is started by PM_EVT_PEERS_DELETE_SUCCEEDED event.
} else {
ret_code_t err_code = ble_advertising_start(&ble_manager_advertising, BLE_ADV_MODE_FAST);
APP_ERROR_CHECK(err_code);
}
}
void handle_alert_notification(ble_ans_c_evt_t * p_evt)
{
ret_code_t err_code;
if (p_evt->uuid.uuid == BLE_UUID_UNREAD_ALERT_CHAR)
{
if (m_unread_alert_state == ALERT_NOTIFICATION_ENABLED)
{
// err_code = bsp_indication_set(BSP_INDICATE_ALERT_1);
APP_ERROR_CHECK(err_code);
m_unread_alert_state = ALERT_NOTIFICATION_ON;
NRF_LOG_INFO("Unread Alert state: On.");
NRF_LOG_INFO(" Category: %s",
(uint32_t)lit_catid[p_evt->data.alert.alert_category]);
NRF_LOG_INFO(" Number of unread alerts: %d",
p_evt->data.alert.alert_category_count);
}
}
else if (p_evt->uuid.uuid == BLE_UUID_NEW_ALERT_CHAR)
{
m_new_alert_state = ALERT_NOTIFICATION_ON;
NRF_LOG_INFO("New Alert state: On.");
NRF_LOG_INFO(" Category: %s",
(uint32_t)lit_catid[p_evt->data.alert.alert_category]);
NRF_LOG_INFO(" Number of new alerts: %d",
p_evt->data.alert.alert_category_count);
NRF_LOG_INFO(" Text String Information: (%d) %s",
p_evt->data.alert.alert_msg_length, (uint32_t)p_evt->data.alert.p_alert_msg_buf);
OnNewNotificationCallback(p_evt->data.alert.p_alert_msg_buf, p_evt->data.alert.alert_msg_length);
}
else
{
// Only Unread and New Alerts exists, thus do nothing.
}
}
void supported_alert_notification_read(void)
{
NRF_LOG_INFO("Read supported Alert Notification characteristics on the connected peer.");
ret_code_t err_code;
err_code = ble_ans_c_new_alert_read(&m_ans_c);
APP_ERROR_CHECK(err_code);
err_code = ble_ans_c_unread_alert_read(&m_ans_c);
APP_ERROR_CHECK(err_code);
}
void alert_notification_setup(void)
{
ret_code_t err_code;
err_code = ble_ans_c_enable_notif_new_alert(&m_ans_c);
APP_ERROR_CHECK(err_code);
m_new_alert_state = ALERT_NOTIFICATION_ENABLED;
NRF_LOG_INFO("New Alert State: Enabled.");
err_code = ble_ans_c_enable_notif_unread_alert(&m_ans_c);
APP_ERROR_CHECK(err_code);
m_unread_alert_state = ALERT_NOTIFICATION_ENABLED;
NRF_LOG_INFO("Unread Alert State: Enabled.");
NRF_LOG_INFO("Notifications enabled.");
}
void control_point_setup(ble_ans_c_evt_t * p_evt)
{
uint32_t err_code;
ble_ans_control_point_t setting;
if (p_evt->uuid.uuid == BLE_UUID_SUPPORTED_UNREAD_ALERT_CATEGORY_CHAR)
{
setting.command = ANS_ENABLE_UNREAD_CATEGORY_STATUS_NOTIFICATION;
setting.category = (ble_ans_category_id_t)p_evt->data.alert.alert_category;
NRF_LOG_INFO("Unread status notification enabled for received categories.");
}
else if (p_evt->uuid.uuid == BLE_UUID_SUPPORTED_NEW_ALERT_CATEGORY_CHAR)
{
setting.command = ANS_ENABLE_NEW_INCOMING_ALERT_NOTIFICATION;
setting.category = (ble_ans_category_id_t)p_evt->data.alert.alert_category;
NRF_LOG_INFO("New incoming notification enabled for received categories.");
}
else
{
return;
}
err_code = ble_ans_c_control_point_write(&m_ans_c, &setting);
APP_ERROR_CHECK(err_code);
}
void on_ans_c_evt(ble_ans_c_evt_t * p_evt)
{
ret_code_t err_code;
switch (p_evt->evt_type) {
case BLE_ANS_C_EVT_DISCOVERY_FAILED:
// TODO When another service is found, this event is sent to all the other service handled.
// In this case, this is not an error, it just tells that the service that have just been found is not this one...
NRF_LOG_INFO("[ANS] Discovery failed");
break;
case BLE_ANS_C_EVT_NOTIFICATION:
handle_alert_notification(p_evt);
NRF_LOG_INFO("[ANS] Alert Notification received from server, UUID: %X.", p_evt->uuid.uuid);
break; // BLE_ANS_C_EVT_NOTIFICATION
case BLE_ANS_C_EVT_DISCOVERY_COMPLETE:
NRF_LOG_INFO("[ANS] Alert Notification Service discovered on the server.");
err_code = ble_ans_c_handles_assign(&m_ans_c,
p_evt->conn_handle,
&p_evt->data.service);
APP_ERROR_CHECK(err_code);
supported_alert_notification_read();
alert_notification_setup();
break; // BLE_ANS_C_EVT_DISCOVERY_COMPLETE
case BLE_ANS_C_EVT_READ_RESP:
NRF_LOG_INFO("[ANS] Alert Setup received from server, UUID: %X.", p_evt->uuid.uuid);
control_point_setup(p_evt);
break; // BLE_ANS_C_EVT_READ_RESP
case BLE_ANS_C_EVT_DISCONN_COMPLETE:
NRF_LOG_INFO("[ANS] ANS : disconnecting from server");
m_new_alert_state = ALERT_NOTIFICATION_DISABLED;
m_unread_alert_state = ALERT_NOTIFICATION_DISABLED;
break; // BLE_ANS_C_EVT_DISCONN_COMPLETE
default:
// No implementation needed.
break;
}
}
void alert_notification_error_handler(uint32_t nrf_error)
{
APP_ERROR_HANDLER(nrf_error);
}
void ble_manager_init_services() {
ret_code_t err_code;
ble_hrs_init_t hrs_init;
ble_bas_init_t bas_init;
ble_dis_init_t dis_init;
ble_cts_c_init_t cts_init;
ble_ans_c_init_t ans_init_obj;
nrf_ble_qwr_init_t qwr_init = {0};
uint8_t body_sensor_location;
// Initialize Queued Write Module.
qwr_init.error_handler = ble_manager_queue_write_error_handler;
err_code = nrf_ble_qwr_init(&ble_manager_queue_write, &qwr_init);
APP_ERROR_CHECK(err_code);
// Initialize Heart Rate Service.
body_sensor_location = BLE_HRS_BODY_SENSOR_LOCATION_FINGER;
memset(&hrs_init, 0, sizeof(hrs_init));
hrs_init.evt_handler = NULL;
hrs_init.is_sensor_contact_supported = true;
hrs_init.p_body_sensor_location = &body_sensor_location;
// Here the sec level for the Heart Rate Service can be changed/increased.
hrs_init.hrm_cccd_wr_sec = SEC_OPEN;
hrs_init.bsl_rd_sec = SEC_OPEN;
// Initialize Battery Service.
// memset(&bas_init, 0, sizeof(bas_init));
//
// // Here the sec level for the Battery Service can be changed/increased.
// bas_init.bl_rd_sec = SEC_OPEN;
// bas_init.bl_cccd_wr_sec = SEC_OPEN;
// bas_init.bl_report_rd_sec = SEC_OPEN;
//
// bas_init.evt_handler = NULL;
// bas_init.support_notification = true;
// bas_init.p_report_ref = NULL;
// bas_init.initial_batt_level = 100;
//
// err_code = ble_bas_init(&m_bas, &bas_init);
// APP_ERROR_CHECK(err_code);
// Initialize Device Information Service.
memset(&dis_init, 0, sizeof(dis_init));
ble_srv_ascii_to_utf8(&dis_init.manufact_name_str, (char *) BLE_MANAGER_MANUFACTURER_NAME);
dis_init.dis_char_rd_sec = SEC_OPEN;
err_code = ble_dis_init(&dis_init);
APP_ERROR_CHECK(err_code);
// Initialize CTS.
cts_init.evt_handler = ble_manager_cts_event_handler;
cts_init.error_handler = ble_manager_cts_error_handler;
err_code = ble_cts_c_init(&ble_manager_cts_client, &cts_init);
APP_ERROR_CHECK(err_code);
// Alert Notification service
memset(&ans_init_obj, 0, sizeof(ans_init_obj));
memset(m_alert_message_buffer, 0, MESSAGE_BUFFER_SIZE);
ans_init_obj.evt_handler = on_ans_c_evt;
ans_init_obj.message_buffer_size = MESSAGE_BUFFER_SIZE;
ans_init_obj.p_message_buffer = m_alert_message_buffer;
ans_init_obj.error_handler = alert_notification_error_handler;
err_code = ble_ans_c_init(&m_ans_c, &ans_init_obj);
APP_ERROR_CHECK(err_code);
}
void ble_manager_queue_write_error_handler(uint32_t nrf_error) {
APP_ERROR_HANDLER(nrf_error);
}
void ble_manager_cts_event_handler(ble_cts_c_t *p_cts, ble_cts_c_evt_t *p_evt) {
ret_code_t err_code;
switch (p_evt->evt_type) {
case BLE_CTS_C_EVT_DISCOVERY_COMPLETE:
NRF_LOG_INFO("[CTS] Current Time Service discovered on server, requesting current time...");
err_code = ble_cts_c_handles_assign(&ble_manager_cts_client,
p_evt->conn_handle,
&p_evt->params.char_handles);
ble_cts_c_current_time_read(&ble_manager_cts_client);
APP_ERROR_CHECK(err_code);
break;
case BLE_CTS_C_EVT_DISCOVERY_FAILED:
// TODO When another service is found, this event is sent to all the other service handled.
// In this case, this is not an error, it just tells that the service that have just been found is not this one...
NRF_LOG_INFO("[CTS] Current Time Service not found on server.");
break;
case BLE_CTS_C_EVT_DISCONN_COMPLETE:
NRF_LOG_INFO("[CTS] Disconnect Complete.");
break;
case BLE_CTS_C_EVT_CURRENT_TIME:
NRF_LOG_INFO("[CTS] Current Time received.");
ble_manager_cts_print_time(p_evt);
break;
case BLE_CTS_C_EVT_INVALID_TIME:
NRF_LOG_INFO("[CTS] Invalid Time received.");
break;
default:
break;
}
}
void ble_manager_cts_error_handler(uint32_t nrf_error) {
APP_ERROR_HANDLER(nrf_error);
}
void ble_manager_cts_print_time(ble_cts_c_evt_t *p_evt) {
NRF_LOG_INFO("\r\nCurrent Time:");
NRF_LOG_INFO("\r\nDate:");
NRF_LOG_INFO("\tDay of week %s", (uint32_t) day_of_week[p_evt->
params.
current_time.
exact_time_256.
day_date_time.
day_of_week]);
if (p_evt->params.current_time.exact_time_256.day_date_time.date_time.day == 0) {
NRF_LOG_INFO("\tDay of month Unknown");
} else {
NRF_LOG_INFO("\tDay of month %i",
p_evt->params.current_time.exact_time_256.day_date_time.date_time.day);
}
NRF_LOG_INFO("\tMonth of year %s",
(uint32_t) month_of_year[p_evt->params.current_time.exact_time_256.day_date_time.date_time.month]);
if (p_evt->params.current_time.exact_time_256.day_date_time.date_time.year == 0) {
NRF_LOG_INFO("\tYear Unknown");
} else {
NRF_LOG_INFO("\tYear %i",
p_evt->params.current_time.exact_time_256.day_date_time.date_time.year);
}
NRF_LOG_INFO("\r\nTime:");
NRF_LOG_INFO("\tHours %i",
p_evt->params.current_time.exact_time_256.day_date_time.date_time.hours);
NRF_LOG_INFO("\tMinutes %i",
p_evt->params.current_time.exact_time_256.day_date_time.date_time.minutes);
NRF_LOG_INFO("\tSeconds %i",
p_evt->params.current_time.exact_time_256.day_date_time.date_time.seconds);
NRF_LOG_INFO("\tFractions %i/256 of a second",
p_evt->params.current_time.exact_time_256.fractions256);
NRF_LOG_INFO("\r\nAdjust reason:\r");
NRF_LOG_INFO("\tDaylight savings %x",
p_evt->params.current_time.adjust_reason.change_of_daylight_savings_time);
NRF_LOG_INFO("\tTime zone %x",
p_evt->params.current_time.adjust_reason.change_of_time_zone);
NRF_LOG_INFO("\tExternal update %x",
p_evt->params.current_time.adjust_reason.external_reference_time_update);
NRF_LOG_INFO("\tManual update %x",
p_evt->params.current_time.adjust_reason.manual_time_update);
OnNewTimeCallback(&p_evt->params.current_time);
}
void ble_manager_init_connection_params() {
ret_code_t err_code;
ble_conn_params_init_t cp_init;
memset(&cp_init, 0, sizeof(cp_init));
cp_init.p_conn_params = NULL;
cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY;
cp_init.next_conn_params_update_delay = NEXT_CONN_PARAMS_UPDATE_DELAY;
cp_init.max_conn_params_update_count = MAX_CONN_PARAMS_UPDATE_COUNT;
cp_init.start_on_notify_cccd_handle = BLE_GATT_HANDLE_INVALID;
cp_init.disconnect_on_fail = false;
cp_init.evt_handler = ble_manager_conn_params_event_handler;
cp_init.error_handler = ble_manager_conn_params_error_handler;
err_code = ble_conn_params_init(&cp_init);
APP_ERROR_CHECK(err_code);
}
void ble_manager_conn_params_event_handler(ble_conn_params_evt_t *p_evt) {
ret_code_t err_code;
if(p_evt->evt_type == BLE_CONN_PARAMS_EVT_SUCCEEDED) {
NRF_LOG_INFO("BLE connection parameters negotiation successful!");
} else if(p_evt->evt_type == BLE_CONN_PARAMS_EVT_FAILED) {
NRF_LOG_ERROR("BLE connection parameters negotiation error, disconnecting.");
err_code = sd_ble_gap_disconnect(ble_manager_connection_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE);
APP_ERROR_CHECK(err_code);
}
}
void ble_manager_conn_params_error_handler(uint32_t nrf_error) {
APP_ERROR_HANDLER(nrf_error);
}
void ble_manager_delete_bonds() {
ret_code_t err_code;
NRF_LOG_INFO("Erase bonds!");
err_code = pm_peers_delete();
APP_ERROR_CHECK(err_code);
}
#endif

View file

@ -1,51 +0,0 @@
#pragma once
//#include <ble/ble_services/ble_cts_c/ble_cts_c.h>
#ifdef __cplusplus
extern "C" {
#endif
#define BLE_MANAGER_CONN_CFG_TAG 1 /* A tag identifying the SoftDevice BLE configuration. */
#define BLE_MANAGER__OBSERVER_PRIO 3 /* Application's BLE observer priority. You shouldn't need to modify this value. */
#define BLE_MANAGER_DEVICE_NAME "PineTime" /* Name of device. Will be included in the advertising data.*/
#define BLE_MANAGER_MANUFACTURER_NAME "Codingfield"
#define BLE_MANAGER_MIN_CONN_INTERVAL MSEC_TO_UNITS(100, UNIT_1_25_MS) /* Minimum acceptable connection interval (0.4 seconds).*/
#define BLE_MANAGER_MAX_CONN_INTERVAL MSEC_TO_UNITS(650, UNIT_1_25_MS) /*Maximum acceptable connection interval (0.65 second).*/
#define BLE_MANAGER_SLAVE_LATENCY 0 /* Slave latency.*/
#define BLE_MANAGER_CONN_SUP_TIMEOUT MSEC_TO_UNITS(4000, UNIT_10_MS) /* Connection supervisory time-out (4 seconds).*/
#define BLE_MANAGER_ADV_INTERVAL 300 /* The advertising interval (in units of 0.625 ms. This value corresponds to 187.5 ms).*/
#define BLE_MANAGER_ADV_DURATION 18000 /* The advertising duration (180 seconds) in units of 10 milliseconds.*/
#define BLE_MANAGER_SEC_PARAM_BOND 1 /* Perform bonding. */
#define BLE_MANAGER_SEC_PARAM_MITM 0 /* Man In The Middle protection not required. */
#define BLE_MANAGER_SEC_PARAM_LESC 0 /* LE Secure Connections not enabled. */
#define BLE_MANAGER_SEC_PARAM_KEYPRESS 0 /* Keypress notifications not enabled. */
#define BLE_MANAGER_SEC_PARAM_IO_CAPABILITIES BLE_GAP_IO_CAPS_NONE /* No I/O capabilities. */
#define BLE_MANAGER_SEC_PARAM_OOB 0 /* Out Of Band data not available. */
#define BLE_MANAGER_SEC_PARAM_MIN_KEY_SIZE 7 /* Minimum encryption key size. */
#define BLE_MANAGER_SEC_PARAM_MAX_KEY_SIZE 16 /* Maximum encryption key size. */
#define FIRST_CONN_PARAMS_UPDATE_DELAY 5000 /* Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (5 seconds). */
#define NEXT_CONN_PARAMS_UPDATE_DELAY 30000 /* Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */
#define MAX_CONN_PARAMS_UPDATE_COUNT 3 /* Number of attempts before giving up the connection parameter negotiation. */
#define MESSAGE_BUFFER_SIZE 18 /**< Size of buffer holding optional messages in notifications. */
#define BLE_ANS_NB_OF_CATEGORY_ID 10 /**< Number of categories. */
void ble_manager_init();
void ble_manager_start_advertising(void *p_erase_bonds);
void ble_manager_init_peer_manager();
// TODO use signals from RTOS to notify new time
//void ble_manager_set_new_time_callback(void (*OnNewTime)(current_time_char_t* currentTime));
void ble_manager_set_ble_disconnection_callback(void (*OnBleDisconnection)());
void ble_manager_set_ble_connection_callback(void (*OnBleConnection)());
void ble_manager_set_new_notification_callback(void (*OnNewNotification)(const char* message, uint8_t size));
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,101 @@
#include "DeviceInformationService.h"
using namespace Pinetime::Controllers;
constexpr ble_uuid16_t DeviceInformationService::manufacturerNameUuid;
constexpr ble_uuid16_t DeviceInformationService::modelNumberUuid;
constexpr ble_uuid16_t DeviceInformationService::serialNumberUuid;
constexpr ble_uuid16_t DeviceInformationService::fwRevisionUuid;
constexpr ble_uuid16_t DeviceInformationService::deviceInfoUuid;
constexpr ble_uuid16_t DeviceInformationService::hwRevisionUuid;
int DeviceInformationCallback(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) {
auto deviceInformationService = static_cast<DeviceInformationService*>(arg);
return deviceInformationService->OnDeviceInfoRequested(conn_handle, attr_handle, ctxt);
}
void DeviceInformationService::Init() {
ble_gatts_count_cfg(serviceDefinition);
ble_gatts_add_svcs(serviceDefinition);
}
int DeviceInformationService::OnDeviceInfoRequested(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt) {
const char *str;
switch (ble_uuid_u16(ctxt->chr->uuid)) {
case manufacturerNameId:
str = manufacturerName;
break;
case modelNumberId:
str = modelNumber;
break;
case serialNumberId:
str = serialNumber;
break;
case fwRevisionId:
str = fwRevision;
break;
case hwRevisionId:
str = hwRevision;
break;
default:
return BLE_ATT_ERR_UNLIKELY;
}
int res = os_mbuf_append(ctxt->om, str, strlen(str));
return (res == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
DeviceInformationService::DeviceInformationService() :
characteristicDefinition{
{
.uuid = (ble_uuid_t *) &manufacturerNameUuid,
.access_cb = DeviceInformationCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_READ,
},
{
.uuid = (ble_uuid_t *) &modelNumberUuid,
.access_cb = DeviceInformationCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_READ,
},
{
.uuid = (ble_uuid_t *) &serialNumberUuid,
.access_cb = DeviceInformationCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_READ,
},
{
.uuid = (ble_uuid_t *) &fwRevisionUuid,
.access_cb = DeviceInformationCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_READ,
},
{
.uuid = (ble_uuid_t *) &hwRevisionUuid,
.access_cb = DeviceInformationCallback,
.arg = this,
.flags = BLE_GATT_CHR_F_READ,
},
{
0
}
},
serviceDefinition{
{
/* Device Information Service */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = (ble_uuid_t *) &deviceInfoUuid,
.characteristics = characteristicDefinition
},
{
0
},
}
{
}

View file

@ -0,0 +1,67 @@
#pragma once
#include <cstdint>
#include <array>
#include <host/ble_gap.h>
namespace Pinetime {
namespace Controllers {
class DeviceInformationService {
public:
DeviceInformationService();
void Init();
int OnDeviceInfoRequested(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt);
private:
static constexpr uint16_t deviceInfoId {0x180a};
static constexpr uint16_t manufacturerNameId {0x2a29};
static constexpr uint16_t modelNumberId {0x2a24};
static constexpr uint16_t serialNumberId {0x2a25};
static constexpr uint16_t fwRevisionId {0x2a26};
static constexpr uint16_t hwRevisionId {0x2a27};
static constexpr char* manufacturerName = "Codingfield";
static constexpr char* modelNumber = "1";
static constexpr char* serialNumber = "9.8.7.6.5.4";
static constexpr char* fwRevision = "0.5.0";
static constexpr char* hwRevision = "1.0.0";
static constexpr ble_uuid16_t deviceInfoUuid {
.u { .type = BLE_UUID_TYPE_16 },
.value = deviceInfoId
};
static constexpr ble_uuid16_t manufacturerNameUuid {
.u { .type = BLE_UUID_TYPE_16 },
.value = manufacturerNameId
};
static constexpr ble_uuid16_t modelNumberUuid {
.u { .type = BLE_UUID_TYPE_16 },
.value = modelNumberId
};
static constexpr ble_uuid16_t serialNumberUuid {
.u { .type = BLE_UUID_TYPE_16 },
.value = serialNumberId
};
static constexpr ble_uuid16_t fwRevisionUuid {
.u { .type = BLE_UUID_TYPE_16 },
.value = fwRevisionId
};
static constexpr ble_uuid16_t hwRevisionUuid {
.u {.type = BLE_UUID_TYPE_16},
.value = hwRevisionId
};
struct ble_gatt_chr_def characteristicDefinition[6];
struct ble_gatt_svc_def serviceDefinition[2];
};
}
}

5
src/libs/mynewt-nimble/.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
# Dummy NPL build
*.o
/porting/examples/dummy/dummy
/porting/examples/linux/nimble-linux
/porting/examples/linux_blemesh/nimble-linux-blemesh

View file

@ -0,0 +1,30 @@
# Can't easily add license to rat-excludes file.
.rat-excludes
# Ignore documentation folder
docs
# Non-source files
RELEASE_NOTES.md
.gitignore
README.md
pts-gap.txt
pts-gatt.txt
pts-l2cap.txt
pts-sm.txt
94654-20170317-085122560.tpg
94654-20170317-085441153.pts
uncrustify.cfg
.style_ignored_dirs
# tinycrypt - BSD License.
tinycrypt
# Bluetooth Mesh - Apache 2.0 License
mesh
# Queue implementation - BSD License
queue.h
# mbuf implementation - BSD License
os_mbuf.c

View file

@ -0,0 +1,4 @@
# Skip those directories while doing style checks in the CI. Do not add '/' at
# the beginning!
ext/tinycrypt

View file

@ -0,0 +1,167 @@
# 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.
language: go
_addons: &addon_conf
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- gcc-multilib
- gcc-7-multilib
go:
- "1.12"
git:
depth: false
matrix:
include:
# Style checking
- os: linux
language: python
python:
- "3.5"
addons:
apt:
packages:
- "python3-pip"
env:
- TEST=STYLE
- DEBUG=1
# newt build <targets>
- os: linux
addons: *addon_conf
env:
- TEST=BUILD_TARGETS
- VM_AMOUNT=4
- TARGET_SET=1
- os: linux
addons: *addon_conf
env:
- TEST=BUILD_TARGETS
- VM_AMOUNT=4
- TARGET_SET=2
- os: linux
addons: *addon_conf
env:
- TEST=BUILD_TARGETS
- VM_AMOUNT=4
- TARGET_SET=3
- os: linux
addons: *addon_conf
env:
- TEST=BUILD_TARGETS
- VM_AMOUNT=4
- TARGET_SET=4
# newt test all (Linux)
- os: linux
addons: *addon_conf
env:
- TEST=TEST_ALL
- VM_AMOUNT=2
- TARGET_SET=1
- os: linux
addons: *addon_conf
env:
- TEST=TEST_ALL
- VM_AMOUNT=2
- TARGET_SET=2
# ports
- os: linux
addons: *addon_conf
env:
- TEST=BUILD_PORTS
- VM_AMOUNT=1
- TARGET_SET=1
# newt test all
- os: osx
osx_image: xcode9.2
env:
- TEST=TEST_ALL
- VM_AMOUNT=3
- TARGET_SET=1
- os: osx
osx_image: xcode9.2
env:
- TEST=TEST_ALL
- VM_AMOUNT=3
- TARGET_SET=2
- os: osx
osx_image: xcode9.2
env:
- TEST=TEST_ALL
- VM_AMOUNT=3
- TARGET_SET=3
- os: windows
env:
- TEST=BUILD_TARGETS_WINDOWS
- VM_AMOUNT=1
- TARGET_SET=1
before_install:
- printenv
- export GOPATH=$HOME/gopath
- go version
install:
- git clone https://github.com/JuulLabs-OSS/mynewt-travis-ci $HOME/ci
- chmod +x $HOME/ci/*.sh
- |
if [ "${TEST}" == "STYLE" ]; then
pip3 install requests
else
$HOME/ci/${TRAVIS_OS_NAME}_travis_install.sh
fi
before_script:
- |
if [ "${TEST}" == "STYLE" ]; then
$HOME/ci/install_uncrustify.sh
else
newt version
gcc --version
if [ "${TEST}" != "TEST_ALL" ]; then arm-none-eabi-gcc --version; fi
cp -R $HOME/ci/mynewt-nimble-project.yml project.yml
mkdir -p targets
cp -R $HOME/ci/mynewt-nimble-targets targets
$HOME/ci/prepare_test.sh $VM_AMOUNT
mkdir -p repos && pushd repos/
git clone --depth=1 https://github.com/apache/mynewt-core apache-mynewt-core
git clone --depth=1 https://github.com/JuulLabs-OSS/mcuboot mcuboot
git clone --depth=1 https://github.com/apache/mynewt-mcumgr apache-mynewt-mcumgr
popd
fi
script:
- |
if [ "${TEST}" == "STYLE" ]; then
python3 $HOME/ci/check_style.py
else
$HOME/ci/run_test.sh
fi
cache:
directories:
- $HOME/TOOLCHAIN
- $HOME/Library/Caches/Homebrew

View file

@ -0,0 +1,267 @@
# Coding Style for Apache NimBLE
Apache NimBLE project is part of Apache Mynewt projct and follows its coding
style.
# Coding Style for Apache Mynewt Core
This document is meant to define the coding style for Apache Mynewt, and
all subprojects of Apache Mynewt. This covers C and Assembly coding
conventions, *only*. Other languages (such as Go), have their own
coding conventions.
## Headers
* All files that are newly written, should have the Apache License clause
at the top of them.
* For files that are copied from another source, but contain an Apache
compatible license, the original license header shall be maintained.
* For more information on applying the Apache license, the definitive
source is here: http://www.apache.org/dev/apply-license.html
* The Apache License clause for the top of files is as follows:
```no-highlight
/*
* 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.
*/
```
## Whitespace and Braces
* Code must be indented to 4 spaces, tabs should not be used.
* Do not add whitespace at the end of a line.
* Put space after keywords (for, if, return, switch, while).
* for, else, if, while statements must have braces around their
code blocks, i.e., do:
```
if (x) {
assert(0);
} else {
assert(0);
}
```
Not:
```
if (x)
assert(0);
else
assert(0);
```
* Braces for statements must be on the same line as the statement. Good:
```
for (i = 0; i < 10; i++) {
if (i == 5) {
break;
} else {
continue;
}
}
```
Not:
```
for (i = 0; i < 10; i++)
{ <-- brace must be on same line as for
if (i == 5) {
break;
} <-- no new line between else
else {
continue;
}
}
```
* After a function declaration, the braces should be on a newline, i.e. do:
```
static void *
function(int var1, int var2)
{
```
not:
```
static void *
function(int var1, int var2) {
```
## Line Length and Wrap
* Line length should never exceed 79 columns.
* When you have to wrap a long statement, put the operator at the end of the
line. i.e.:
```
if (x &&
y == 10 &&
b)
```
Not:
```
if (x
&& y == 10
&& b)
```
## Comments
* No C++ style comments allowed.
* When using a single line comment, put it above the line of code that you
intend to comment, i.e., do:
```
/* check variable */
if (a) {
```
Not:
```
if (a) { /* check variable */
```
* All public APIs should be commented with Doxygen style comments describing
purpose, parameters and return values. Private APIs need not be documented.
## Header files
* Header files must contain the following structure:
* Apache License (see above)
* ```#ifdef``` aliasing, to prevent multiple includes
* ```#include``` directives for other required header files
* ```#ifdef __cplusplus``` wrappers to maintain C++ friendly APIs
* Contents of the header file
* ```#ifdef``` aliasing, shall be in the following format, where
the package name is "os" and the file name is "callout.h":
```no-highlight
#ifndef _OS_CALLOUT_H
#define _OS_CALLOUT_H
```
* ```#include``` directives must happen prior to the cplusplus
wrapper.
* The cplusplus wrapper must have the following format, and precedes
any contents of the header file:
```no-highlight
#ifdef __cplusplus
#extern "C" {
##endif
```
## Naming
* Names of functions, structures and variables must be in all lowercase.
* Names should be as short as possible, but no shorter.
* Globally visible names must be prefixed with the name of the module,
followed by the '_' character, i.e.:
```
os_callout_init(&c)
```
Not:
```
callout_init(c)
```
## Functions
* No spaces after function names when calling a function, i.e, do:
```
rc = function(a)
```
Not:
```
rc = function (a)
```
* Arguments to function calls should have spaces between the comma, i.e. do:
```
rc = function(a, b)
```
Not:
```
rc = function(a,b)
```
* The function type must be on a line by itself preceding the function, i.e. do:
```
static void *
function(int var1, int var2)
{
```
Not:
```
static void *function(int var1, int var2)
{
```
* In general, for functions that return values that denote success or error, 0
shall be success, and non-zero shall be the failure code.
## Variables and Macros
* Do not use typedefs for structures. This makes it impossible for
applications to use pointers to those structures opaquely.
* typedef may be used for non-structure types, where it is beneficial to
hide or alias the underlying type used (e.g. ```os_time_t```.) Indicate
typedefs by applying the ```_t``` marker to them.
* Place all function-local variable definitions at the top of the function body, before any statements.
## Compiler Directives
* Code must compile cleanly with -Wall enabled.

View file

@ -0,0 +1,217 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
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.
This product bundles queue.h 8.5, which is available under the "3-clause BSD"
license. For details, see porting/nimble/include/os/queue.h
This product partly derives from FreeBSD, which is available under the
"3-clause BSD" license. For details, see:
* porting/nimble/src/os_mbuf.c
This product bundles Gary S. Brown's CRC32 implementation, which is available
under the following license:
COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
code or tables extracted from it, as desired without restriction.
This product bundles tinycrypt, which is available under the "3-clause BSD"
license. For details, and bundled files see:
* ext/tinycrypt/LICENSE

View file

@ -0,0 +1,8 @@
Apache Mynewt NimBLE
Copyright 2015-2020 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
Portions of this software were developed at
Runtime Inc, copyright 2015.

View file

@ -0,0 +1,169 @@
<!--
#
# 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.
#
-->
<img src="http://mynewt.apache.org/img/logo.svg" width="250" alt="Apache Mynewt">
## Overview
Apache NimBLE is an open-source Bluetooth 5.1 stack (both Host & Controller)
that completely replaces the proprietary SoftDevice on Nordic chipsets. It is
part of [Apache Mynewt project](https://github.com/apache/mynewt-core).
Features highlight:
- Support for 251 byte packet size
- Support for all 4 roles concurrently - Broadcaster, Observer, Peripheral and Central
- Support for up to 32 simultaneous connections.
- Legacy and SC (secure connections) SMP support (pairing and bonding).
- Advertising Extensions.
- Coded (aka Long Range) and 2M PHYs.
- Bluetooth Mesh.
## Supported hardware
Controller supports Nordic nRF51 and nRF52 chipsets. Host runs on any board
and architecture [supported](https://github.com/apache/mynewt-core#overview)
by Apache Mynewt OS.
## Browsing
If you are browsing around the source tree, and want to see some of the
major functional chunks, here are a few pointers:
- nimble/controller: Contains code for controller including Link Layer and HCI implementation
([controller](https://github.com/apache/mynewt-nimble/tree/master/nimble/controller))
- nimble/drivers: Contains drivers for supported radio transceivers (Nordic nRF51 and nRF52)
([drivers](https://github.com/apache/mynewt-nimble/tree/master/nimble/drivers))
- nimble/host: Contains code for host subsystem. This includes protocols like
L2CAP and ATT, support for HCI commands and events, Generic Access Profile (GAP),
Generic Attribute Profile (GATT) and Security Manager (SM).
([host](https://github.com/apache/mynewt-nimble/tree/master/nimble/host))
- nimble/host/mesh: Contains code for Bluetooth Mesh subsystem.
([mesh](https://github.com/apache/mynewt-nimble/tree/master/nimble/host/mesh))
- nimble/transport: Contains code for supported transport protocols between host
and controller. This includes UART, emSPI and RAM (used in combined build when
host and controller run on same CPU)
([transport](https://github.com/apache/mynewt-nimble/tree/master/nimble/transport))
- porting: Contains implementation of NimBLE Porting Layer (NPL) for supported
operating systems
([porting](https://github.com/apache/mynewt-nimble/tree/master/porting))
- ext: Contains external libraries used by NimBLE. Those are used if not
provided by OS
([ext](https://github.com/apache/mynewt-nimble/tree/master/ext))
- kernel: Contains the core of the RTOS ([kernel/os](https://github.com/apache/mynewt-core/tree/master/kernel/os))
## Sample Applications
There are also some sample applications that show how to Apache Mynewt NimBLE
stack. These sample applications are located in the `apps/` directory of
Apache Mynewt [repo](https://github.com/apache/mynewt-core). Some examples:
* [blecent](https://github.com/apache/mynewt-nimble/tree/master/apps/blecent):
A basic central device with no user interface. This application scans for
a peripheral that supports the alert notification service (ANS). Upon
discovering such a peripheral, blecent connects and performs a characteristic
read, characteristic write, and notification subscription.
* [blehci](https://github.com/apache/mynewt-nimble/tree/master/apps/blehci):
Implements a BLE controller-only application. A separate host-only
implementation, such as Linux's BlueZ, can interface with this application via
HCI over UART.
* [bleprph](https://github.com/apache/mynewt-nimble/tree/master/apps/bleprph): An
implementation of a minimal BLE peripheral.
* [btshell](https://github.com/apache/mynewt-nimble/tree/master/apps/btshell): A
shell-like application allowing to configure and use most of NimBLE
functionality from command line.
* [bleuart](https://github.com/apache/mynewt-core/tree/master/apps/bleuart):
Implements a simple BLE peripheral that supports the Nordic
UART / Serial Port Emulation service
(https://developer.nordicsemi.com/nRF5_SDK/nRF51_SDK_v8.x.x/doc/8.0.0/s110/html/a00072.html).
# Getting Help
If you are having trouble using or contributing to Apache Mynewt NimBLE, or just
want to talk to a human about what you're working on, you can contact us via the
[developers mailing list](mailto:dev@mynewt.apache.org).
Although not a formal channel, you can also find a number of core developers
on the #mynewt channel on Freenode IRC or #general channel on [Mynewt Slack](https://mynewt.slack.com/join/shared_invite/enQtNjA1MTg0NzgyNzg3LTcyMmZiOGQzOGMxM2U4ODFmMTIwNjNmYTE5Y2UwYjQwZWIxNTE0MTUzY2JmMTEzOWFjYWZkNGM0YmM4MzAxNWQ)
Also, be sure to checkout the [Frequently Asked Questions](https://mynewt.apache.org/faq/answers)
for some help troubleshooting first.
# Contributing
Anybody who works with Apache Mynewt can be a contributing member of the
community that develops and deploys it. The process of releasing an operating
system for microcontrollers is never done: and we welcome your contributions
to that effort.
More information can be found at the Community section of the Apache Mynewt
website, located [here](https://mynewt.apache.org/community).
## Pull Requests
Apache Mynewt welcomes pull request via Github. Discussions are done on Github,
but depending on the topic, can also be relayed to the official Apache Mynewt
developer mailing list dev@mynewt.apache.org.
If you are suggesting a new feature, please email the developer list directly,
with a description of the feature you are planning to work on.
## Filing Bugs
Bugs can be filed on the
[Apache Mynewt NimBLE Issues](https://github.com/apache/mynewt-nimble/issues).
Please label the issue as a "Bug".
Where possible, please include a self-contained reproduction case!
## Feature Requests
Feature requests should also be filed on the
[Apache Mynewt NimBLE Bug Tracker](https://github.com/apache/mynewt-nimble/issues).
Please label the issue as a "Feature" or "Enhancement" depending on the scope.
## Writing Tests
We love getting newt tests! Apache Mynewt is a huge undertaking, and improving
code coverage is a win for every Apache Mynewt user.
<!--
TODO
## Writing Documentation
Contributing to documentation (in addition to writing tests), is a great way
to get involved with the Apache Mynewt project.
The Mynewt NimBLE documentation is found in [/docs](/docs).
-->
# License
The code in this repository is all under either the Apache 2 license, or a
license compatible with the Apache 2 license. See the LICENSE file for more
information.

View file

@ -0,0 +1,33 @@
# RELEASE NOTES
18 March 2020 - Apache NimBLE v1.3.0
For full release notes, please visit the
[Apache Mynewt Wiki](https://cwiki.apache.org/confluence/display/MYNEWT/Release+Notes).
Apache NimBLE is an open-source Bluetooth 5.1 stack (both Host & Controller) that completely
replaces the proprietary SoftDevice on Nordic chipsets.
New features in this version of NimBLE include:
* Support for Bluetooth Core Specification 5.1
* New blestress test application
* Dialog DA1469x CMAC driver
* Support for LE Secure Connections out-of-band (OOB) association model
* Support for automated generation of syscfg for ports
* Qualification related bugfixes
* BLE Mesh improvements - fixes and resync with latest Zephyr code
* RIOT OS port fixes and improvements
* btshell sample application improvements
* improvements for bttester application
* Controller duplicates filtering improvements
* Multi PHY support improvements
* Memory and CPU usage optimizations
* Use of packed structs for HCI (code size reduction)
* Linux sample improvements
* PTS test instructions updates
* Clock managements improvements in controller
If working on next-generation RTOS and Bluetooth protocol stack
sounds exciting to you, get in touch, by sending a mail to the Apache Mynewt
Developer's list, dev@mynewt.apache.org.

View file

@ -0,0 +1,33 @@
#
# 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.
#
pkg.name: "apps/advertiser"
pkg.type: app
pkg.description: "Basic advertiser application"
pkg.author: "Krzysztof Kopyściński <krzysztof.kopyscinski@codecoup.pl>"
pkg.deps:
- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/sys/console/full"
- "@apache-mynewt-core/sys/log/full"
- "@apache-mynewt-core/sys/stats/full"
- "@apache-mynewt-core/sys/log/modlog"
- "@apache-mynewt-nimble/nimble/host/util"
- "@apache-mynewt-nimble/nimble/host/services/gap"
- "@apache-mynewt-nimble/nimble/transport"

View file

@ -0,0 +1,136 @@
/*
* 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 <stdlib.h>
#include "os/os.h"
#include "sysinit/sysinit.h"
#include "log/log.h"
#include "host/ble_hs.h"
#include "host/util/util.h"
#include "services/gap/ble_svc_gap.h"
static const char *device_name = "Apache Mynewt";
/* adv_event() calls advertise(), so forward declaration is required */
static void advertise(void);
static void
set_ble_addr(void)
{
int rc;
ble_addr_t addr;
/* generate new non-resolvable private address */
rc = ble_hs_id_gen_rnd(1, &addr);
assert(rc == 0);
/* set generated address */
rc = ble_hs_id_set_rnd(addr.val);
assert(rc == 0);
}
static int
adv_event(struct ble_gap_event *event, void *arg)
{
switch (event->type) {
case BLE_GAP_EVENT_ADV_COMPLETE:
MODLOG_DFLT(INFO, "Advertising completed, termination code: %d\n",
event->adv_complete.reason);
advertise();
return 0;
default:
MODLOG_DFLT(ERROR, "Advertising event not handled\n");
return 0;
}
}
static void
advertise(void)
{
int rc;
struct ble_gap_adv_params adv_params;
struct ble_hs_adv_fields fields;
/* set adv parameters */
memset(&adv_params, 0, sizeof(adv_params));
adv_params.conn_mode = BLE_GAP_CONN_MODE_NON;
adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
memset(&fields, 0, sizeof(fields));
/* Fill the fields with advertising data - flags, tx power level, name */
fields.flags = BLE_HS_ADV_F_DISC_GEN;
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;
rc = ble_gap_adv_set_fields(&fields);
assert(rc == 0);
MODLOG_DFLT(INFO, "Starting advertising...\n");
/* As own address type we use hard-coded value, because we generate
NRPA and by definition it's random */
rc = ble_gap_adv_start(BLE_OWN_ADDR_RANDOM, NULL, 10000,
&adv_params, adv_event, NULL);
assert(rc == 0);
}
static void
on_sync(void)
{
set_ble_addr();
/* begin advertising */
advertise();
}
static void
on_reset(int reason)
{
MODLOG_DFLT(INFO, "Resetting state; reason=%d\n", reason);
}
int
main(int argc, char **argv)
{
int rc;
/* Initialize all packages. */
sysinit();
ble_hs_cfg.sync_cb = on_sync;
ble_hs_cfg.reset_cb = on_reset;
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;
}

View file

@ -0,0 +1,36 @@
# 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.
#
pkg.name: apps/blecent
pkg.type: app
pkg.description: Simple BLE central application.
pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
pkg.deps:
- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/sys/console/full"
- "@apache-mynewt-core/sys/log/full"
- "@apache-mynewt-core/sys/log/modlog"
- "@apache-mynewt-core/sys/stats/full"
- nimble/host
- nimble/host/util
- nimble/host/services/gap
- nimble/host/services/gatt
- nimble/host/store/ram
- nimble/transport

View file

@ -0,0 +1,111 @@
/*
* 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.
*/
#ifndef H_BLECENT_
#define H_BLECENT_
#include "os/mynewt.h"
#include "modlog/modlog.h"
#ifdef __cplusplus
extern "C" {
#endif
struct ble_hs_adv_fields;
struct ble_gap_conn_desc;
struct ble_hs_cfg;
union ble_store_value;
union ble_store_key;
#define BLECENT_SVC_ALERT_UUID 0x1811
#define BLECENT_CHR_SUP_NEW_ALERT_CAT_UUID 0x2A47
#define BLECENT_CHR_NEW_ALERT 0x2A46
#define BLECENT_CHR_SUP_UNR_ALERT_CAT_UUID 0x2A48
#define BLECENT_CHR_UNR_ALERT_STAT_UUID 0x2A45
#define BLECENT_CHR_ALERT_NOT_CTRL_PT 0x2A44
/** Misc. */
void print_bytes(const uint8_t *bytes, int len);
void print_mbuf(const struct os_mbuf *om);
char *addr_str(const void *addr);
void print_uuid(const ble_uuid_t *uuid);
void print_conn_desc(const struct ble_gap_conn_desc *desc);
void print_adv_fields(const struct ble_hs_adv_fields *fields);
/** Peer. */
struct peer_dsc {
SLIST_ENTRY(peer_dsc) next;
struct ble_gatt_dsc dsc;
};
SLIST_HEAD(peer_dsc_list, peer_dsc);
struct peer_chr {
SLIST_ENTRY(peer_chr) next;
struct ble_gatt_chr chr;
struct peer_dsc_list dscs;
};
SLIST_HEAD(peer_chr_list, peer_chr);
struct peer_svc {
SLIST_ENTRY(peer_svc) next;
struct ble_gatt_svc svc;
struct peer_chr_list chrs;
};
SLIST_HEAD(peer_svc_list, peer_svc);
struct peer;
typedef void peer_disc_fn(const struct peer *peer, int status, void *arg);
struct peer {
SLIST_ENTRY(peer) next;
uint16_t conn_handle;
/** List of discovered GATT services. */
struct peer_svc_list svcs;
/** Keeps track of where we are in the service discovery process. */
uint16_t disc_prev_chr_val;
struct peer_svc *cur_svc;
/** Callback that gets executed when service discovery completes. */
peer_disc_fn *disc_cb;
void *disc_cb_arg;
};
int peer_disc_all(uint16_t conn_handle, peer_disc_fn *disc_cb,
void *disc_cb_arg);
const struct peer_dsc *
peer_dsc_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid,
const ble_uuid_t *chr_uuid, const ble_uuid_t *dsc_uuid);
const struct peer_chr *
peer_chr_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid,
const ble_uuid_t *chr_uuid);
const struct peer_svc *
peer_svc_find_uuid(const struct peer *peer, const ble_uuid_t *uuid);
int peer_delete(uint16_t conn_handle);
int peer_add(uint16_t conn_handle);
int peer_init(int max_peers, int max_svcs, int max_chrs, int max_dscs);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,525 @@
/*
* 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 "os/mynewt.h"
#include "bsp/bsp.h"
/* BLE */
#include "nimble/ble.h"
#include "host/ble_hs.h"
#include "host/util/util.h"
/* Mandatory services. */
#include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h"
/* Application-specified header. */
#include "blecent.h"
static int blecent_gap_event(struct ble_gap_event *event, void *arg);
/**
* Application callback. Called when the read of the ANS Supported New Alert
* Category characteristic has completed.
*/
static int
blecent_on_read(uint16_t conn_handle,
const struct ble_gatt_error *error,
struct ble_gatt_attr *attr,
void *arg)
{
MODLOG_DFLT(INFO, "Read complete; status=%d conn_handle=%d", error->status,
conn_handle);
if (error->status == 0) {
MODLOG_DFLT(INFO, " attr_handle=%d value=", attr->handle);
print_mbuf(attr->om);
}
MODLOG_DFLT(INFO, "\n");
return 0;
}
/**
* Application callback. Called when the write to the ANS Alert Notification
* Control Point characteristic has completed.
*/
static int
blecent_on_write(uint16_t conn_handle,
const struct ble_gatt_error *error,
struct ble_gatt_attr *attr,
void *arg)
{
MODLOG_DFLT(INFO,
"Write complete; status=%d conn_handle=%d attr_handle=%d\n",
error->status, conn_handle, attr->handle);
return 0;
}
/**
* Application callback. Called when the attempt to subscribe to notifications
* for the ANS Unread Alert Status characteristic has completed.
*/
static int
blecent_on_subscribe(uint16_t conn_handle,
const struct ble_gatt_error *error,
struct ble_gatt_attr *attr,
void *arg)
{
MODLOG_DFLT(INFO, "Subscribe complete; status=%d conn_handle=%d "
"attr_handle=%d\n",
error->status, conn_handle, attr->handle);
return 0;
}
/**
* Performs three concurrent GATT operations against the specified peer:
* 1. Reads the ANS Supported New Alert Category characteristic.
* 2. Writes the ANS Alert Notification Control Point characteristic.
* 3. Subscribes to notifications for the ANS Unread Alert Status
* characteristic.
*
* If the peer does not support a required service, characteristic, or
* descriptor, then the peer lied when it claimed support for the alert
* notification service! When this happens, or if a GATT procedure fails,
* this function immediately terminates the connection.
*/
static void
blecent_read_write_subscribe(const struct peer *peer)
{
const struct peer_chr *chr;
const struct peer_dsc *dsc;
uint8_t value[2];
int rc;
/* Read the supported-new-alert-category characteristic. */
chr = peer_chr_find_uuid(peer,
BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID),
BLE_UUID16_DECLARE(BLECENT_CHR_SUP_NEW_ALERT_CAT_UUID));
if (chr == NULL) {
MODLOG_DFLT(ERROR, "Error: Peer doesn't support the Supported New "
"Alert Category characteristic\n");
goto err;
}
rc = ble_gattc_read(peer->conn_handle, chr->chr.val_handle,
blecent_on_read, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Error: Failed to read characteristic; rc=%d\n",
rc);
goto err;
}
/* Write two bytes (99, 100) to the alert-notification-control-point
* characteristic.
*/
chr = peer_chr_find_uuid(peer,
BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID),
BLE_UUID16_DECLARE(BLECENT_CHR_ALERT_NOT_CTRL_PT));
if (chr == NULL) {
MODLOG_DFLT(ERROR, "Error: Peer doesn't support the Alert "
"Notification Control Point characteristic\n");
goto err;
}
value[0] = 99;
value[1] = 100;
rc = ble_gattc_write_flat(peer->conn_handle, chr->chr.val_handle,
value, sizeof value, blecent_on_write, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Error: Failed to write characteristic; rc=%d\n",
rc);
}
/* Subscribe to notifications for the Unread Alert Status characteristic.
* A central enables notifications by writing two bytes (1, 0) to the
* characteristic's client-characteristic-configuration-descriptor (CCCD).
*/
dsc = peer_dsc_find_uuid(peer,
BLE_UUID16_DECLARE(BLECENT_SVC_ALERT_UUID),
BLE_UUID16_DECLARE(BLECENT_CHR_UNR_ALERT_STAT_UUID),
BLE_UUID16_DECLARE(BLE_GATT_DSC_CLT_CFG_UUID16));
if (dsc == NULL) {
MODLOG_DFLT(ERROR, "Error: Peer lacks a CCCD for the Unread Alert "
"Status characteristic\n");
goto err;
}
value[0] = 1;
value[1] = 0;
rc = ble_gattc_write_flat(peer->conn_handle, dsc->dsc.handle,
value, sizeof value, blecent_on_subscribe, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Error: Failed to subscribe to characteristic; "
"rc=%d\n", rc);
goto err;
}
return;
err:
/* Terminate the connection. */
ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
}
/**
* Called when service discovery of the specified peer has completed.
*/
static void
blecent_on_disc_complete(const struct peer *peer, int status, void *arg)
{
if (status != 0) {
/* Service discovery failed. Terminate the connection. */
MODLOG_DFLT(ERROR, "Error: Service discovery failed; status=%d "
"conn_handle=%d\n", status, peer->conn_handle);
ble_gap_terminate(peer->conn_handle, BLE_ERR_REM_USER_CONN_TERM);
return;
}
/* Service discovery has completed successfully. Now we have a complete
* list of services, characteristics, and descriptors that the peer
* supports.
*/
MODLOG_DFLT(ERROR, "Service discovery complete; status=%d "
"conn_handle=%d\n", status, peer->conn_handle);
/* Now perform three concurrent GATT procedures against the peer: read,
* write, and subscribe to notifications.
*/
blecent_read_write_subscribe(peer);
}
/**
* Initiates the GAP general discovery procedure.
*/
static void
blecent_scan(void)
{
uint8_t own_addr_type;
struct ble_gap_disc_params disc_params;
int rc;
/* Figure out address to use while advertising (no privacy for now) */
rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
return;
}
/* Tell the controller to filter duplicates; we don't want to process
* repeated advertisements from the same device.
*/
disc_params.filter_duplicates = 1;
/**
* Perform a passive scan. I.e., don't send follow-up scan requests to
* each advertiser.
*/
disc_params.passive = 1;
/* Use defaults for the rest of the parameters. */
disc_params.itvl = 0;
disc_params.window = 0;
disc_params.filter_policy = 0;
disc_params.limited = 0;
rc = ble_gap_disc(own_addr_type, BLE_HS_FOREVER, &disc_params,
blecent_gap_event, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Error initiating GAP discovery procedure; rc=%d\n",
rc);
}
}
/**
* Indicates whether we should tre to connect to the sender of the specified
* advertisement. The function returns a positive result if the device
* advertises connectability and support for the Alert Notification service.
*/
static int
blecent_should_connect(const struct ble_gap_disc_desc *disc)
{
struct ble_hs_adv_fields fields;
int rc;
int i;
/* The device has to be advertising connectability. */
if (disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_ADV_IND &&
disc->event_type != BLE_HCI_ADV_RPT_EVTYPE_DIR_IND) {
return 0;
}
rc = ble_hs_adv_parse_fields(&fields, disc->data, disc->length_data);
if (rc != 0) {
return rc;
}
/* The device has to advertise support for the Alert Notification
* service (0x1811).
*/
for (i = 0; i < fields.num_uuids16; i++) {
if (ble_uuid_u16(&fields.uuids16[i].u) == BLECENT_SVC_ALERT_UUID) {
return 1;
}
}
return 0;
}
/**
* Connects to the sender of the specified advertisement of it looks
* interesting. A device is "interesting" if it advertises connectability and
* support for the Alert Notification service.
*/
static void
blecent_connect_if_interesting(const struct ble_gap_disc_desc *disc)
{
uint8_t own_addr_type;
int rc;
/* Don't do anything if we don't care about this advertiser. */
if (!blecent_should_connect(disc)) {
return;
}
/* Scanning must be stopped before a connection can be initiated. */
rc = ble_gap_disc_cancel();
if (rc != 0) {
MODLOG_DFLT(DEBUG, "Failed to cancel scan; rc=%d\n", rc);
return;
}
/* Figure out address to use for connect (no privacy for now) */
rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
return;
}
/* Try to connect the the advertiser. Allow 30 seconds (30000 ms) for
* timeout.
*/
rc = ble_gap_connect(own_addr_type, &disc->addr, 30000, NULL,
blecent_gap_event, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Error: Failed to connect to device; addr_type=%d "
"addr=%s\n; rc=%d",
disc->addr.type, addr_str(disc->addr.val), rc);
return;
}
}
/**
* The nimble host executes this callback when a GAP event occurs. The
* application associates a GAP event callback with each connection that is
* established. blecent uses the same callback for all connections.
*
* @param event The event being signalled.
* @param arg Application-specified argument; unused by
* blecent.
*
* @return 0 if the application successfully handled the
* event; nonzero on failure. The semantics
* of the return code is specific to the
* particular GAP event being signalled.
*/
static int
blecent_gap_event(struct ble_gap_event *event, void *arg)
{
struct ble_gap_conn_desc desc;
struct ble_hs_adv_fields fields;
int rc;
switch (event->type) {
case BLE_GAP_EVENT_DISC:
rc = ble_hs_adv_parse_fields(&fields, event->disc.data,
event->disc.length_data);
if (rc != 0) {
return 0;
}
/* An advertisment report was received during GAP discovery. */
print_adv_fields(&fields);
/* Try to connect to the advertiser if it looks interesting. */
blecent_connect_if_interesting(&event->disc);
return 0;
case BLE_GAP_EVENT_CONNECT:
/* A new connection was established or a connection attempt failed. */
if (event->connect.status == 0) {
/* Connection successfully established. */
MODLOG_DFLT(INFO, "Connection established ");
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
assert(rc == 0);
print_conn_desc(&desc);
MODLOG_DFLT(INFO, "\n");
/* Remember peer. */
rc = peer_add(event->connect.conn_handle);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Failed to add peer; rc=%d\n", rc);
return 0;
}
/* Perform service discovery. */
rc = peer_disc_all(event->connect.conn_handle,
blecent_on_disc_complete, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "Failed to discover services; rc=%d\n", rc);
return 0;
}
} else {
/* Connection attempt failed; resume scanning. */
MODLOG_DFLT(ERROR, "Error: Connection failed; status=%d\n",
event->connect.status);
blecent_scan();
}
return 0;
case BLE_GAP_EVENT_DISCONNECT:
/* Connection terminated. */
MODLOG_DFLT(INFO, "disconnect; reason=%d ", event->disconnect.reason);
print_conn_desc(&event->disconnect.conn);
MODLOG_DFLT(INFO, "\n");
/* Forget about peer. */
peer_delete(event->disconnect.conn.conn_handle);
/* Resume scanning. */
blecent_scan();
return 0;
case BLE_GAP_EVENT_DISC_COMPLETE:
MODLOG_DFLT(INFO, "discovery complete; reason=%d\n",
event->disc_complete.reason);
return 0;
case BLE_GAP_EVENT_ENC_CHANGE:
/* Encryption has been enabled or disabled for this connection. */
MODLOG_DFLT(INFO, "encryption change event; status=%d ",
event->enc_change.status);
rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc);
assert(rc == 0);
print_conn_desc(&desc);
return 0;
case BLE_GAP_EVENT_NOTIFY_RX:
/* Peer sent us a notification or indication. */
MODLOG_DFLT(INFO, "received %s; conn_handle=%d attr_handle=%d "
"attr_len=%d\n",
event->notify_rx.indication ?
"indication" :
"notification",
event->notify_rx.conn_handle,
event->notify_rx.attr_handle,
OS_MBUF_PKTLEN(event->notify_rx.om));
/* Attribute data is contained in event->notify_rx.attr_data. */
return 0;
case BLE_GAP_EVENT_MTU:
MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d cid=%d mtu=%d\n",
event->mtu.conn_handle,
event->mtu.channel_id,
event->mtu.value);
return 0;
case BLE_GAP_EVENT_REPEAT_PAIRING:
/* We already have a bond with the peer, but it is attempting to
* establish a new secure link. This app sacrifices security for
* convenience: just throw away the old bond and accept the new link.
*/
/* Delete the old bond. */
rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
assert(rc == 0);
ble_store_util_delete_peer(&desc.peer_id_addr);
/* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should
* continue with the pairing operation.
*/
return BLE_GAP_REPEAT_PAIRING_RETRY;
default:
return 0;
}
}
static void
blecent_on_reset(int reason)
{
MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
}
static void
blecent_on_sync(void)
{
int rc;
/* Make sure we have proper identity address set (public preferred) */
rc = ble_hs_util_ensure_addr(0);
assert(rc == 0);
/* Begin scanning for a peripheral to connect to. */
blecent_scan();
}
/**
* main
*
* All application logic and NimBLE host work is performed in default task.
*
* @return int NOTE: this function should never return!
*/
int
main(void)
{
int rc;
/* Initialize OS */
sysinit();
/* Configure the host. */
ble_hs_cfg.reset_cb = blecent_on_reset;
ble_hs_cfg.sync_cb = blecent_on_sync;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
/* Initialize data structures to track connected peers. */
rc = peer_init(MYNEWT_VAL(BLE_MAX_CONNECTIONS), 64, 64, 64);
assert(rc == 0);
/* Set the default device name. */
rc = ble_svc_gap_device_name_set("nimble-blecent");
assert(rc == 0);
/* os start should never return. If it does, this should be an error */
while (1) {
os_eventq_run(os_eventq_dflt_get());
}
return 0;
}

View file

@ -0,0 +1,209 @@
/*
* 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 <stdio.h>
#include <string.h>
#include "host/ble_hs.h"
#include "host/ble_uuid.h"
#include "blecent.h"
/**
* Utility function to log an array of bytes.
*/
void
print_bytes(const uint8_t *bytes, int len)
{
int i;
for (i = 0; i < len; i++) {
MODLOG_DFLT(DEBUG, "%s0x%02x", i != 0 ? ":" : "", bytes[i]);
}
}
void
print_mbuf(const struct os_mbuf *om)
{
int colon;
colon = 0;
while (om != NULL) {
if (colon) {
MODLOG_DFLT(DEBUG, ":");
} else {
colon = 1;
}
print_bytes(om->om_data, om->om_len);
om = SLIST_NEXT(om, om_next);
}
}
char *
addr_str(const void *addr)
{
static char buf[6 * 2 + 5 + 1];
const uint8_t *u8p;
u8p = addr;
sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]);
return buf;
}
void
print_uuid(const ble_uuid_t *uuid)
{
char buf[BLE_UUID_STR_LEN];
MODLOG_DFLT(DEBUG, "%s", ble_uuid_to_str(uuid, buf));
}
/**
* Logs information about a connection to the console.
*/
void
print_conn_desc(const struct ble_gap_conn_desc *desc)
{
MODLOG_DFLT(DEBUG, "handle=%d our_ota_addr_type=%d our_ota_addr=%s ",
desc->conn_handle, desc->our_ota_addr.type,
addr_str(desc->our_ota_addr.val));
MODLOG_DFLT(DEBUG, "our_id_addr_type=%d our_id_addr=%s ",
desc->our_id_addr.type, addr_str(desc->our_id_addr.val));
MODLOG_DFLT(DEBUG, "peer_ota_addr_type=%d peer_ota_addr=%s ",
desc->peer_ota_addr.type, addr_str(desc->peer_ota_addr.val));
MODLOG_DFLT(DEBUG, "peer_id_addr_type=%d peer_id_addr=%s ",
desc->peer_id_addr.type, addr_str(desc->peer_id_addr.val));
MODLOG_DFLT(DEBUG, "conn_itvl=%d conn_latency=%d supervision_timeout=%d "
"encrypted=%d authenticated=%d bonded=%d",
desc->conn_itvl, desc->conn_latency,
desc->supervision_timeout,
desc->sec_state.encrypted,
desc->sec_state.authenticated,
desc->sec_state.bonded);
}
void
print_adv_fields(const struct ble_hs_adv_fields *fields)
{
char s[BLE_HS_ADV_MAX_SZ];
const uint8_t *u8p;
int i;
if (fields->flags != 0) {
MODLOG_DFLT(DEBUG, " flags=0x%02x\n", fields->flags);
}
if (fields->uuids16 != NULL) {
MODLOG_DFLT(DEBUG, " uuids16(%scomplete)=",
fields->uuids16_is_complete ? "" : "in");
for (i = 0; i < fields->num_uuids16; i++) {
print_uuid(&fields->uuids16[i].u);
MODLOG_DFLT(DEBUG, " ");
}
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->uuids32 != NULL) {
MODLOG_DFLT(DEBUG, " uuids32(%scomplete)=",
fields->uuids32_is_complete ? "" : "in");
for (i = 0; i < fields->num_uuids32; i++) {
print_uuid(&fields->uuids32[i].u);
MODLOG_DFLT(DEBUG, " ");
}
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->uuids128 != NULL) {
MODLOG_DFLT(DEBUG, " uuids128(%scomplete)=",
fields->uuids128_is_complete ? "" : "in");
for (i = 0; i < fields->num_uuids128; i++) {
print_uuid(&fields->uuids128[i].u);
MODLOG_DFLT(DEBUG, " ");
}
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->name != NULL) {
assert(fields->name_len < sizeof s - 1);
memcpy(s, fields->name, fields->name_len);
s[fields->name_len] = '\0';
MODLOG_DFLT(DEBUG, " name(%scomplete)=%s\n",
fields->name_is_complete ? "" : "in", s);
}
if (fields->tx_pwr_lvl_is_present) {
MODLOG_DFLT(DEBUG, " tx_pwr_lvl=%d\n", fields->tx_pwr_lvl);
}
if (fields->slave_itvl_range != NULL) {
MODLOG_DFLT(DEBUG, " slave_itvl_range=");
print_bytes(fields->slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN);
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->svc_data_uuid16 != NULL) {
MODLOG_DFLT(DEBUG, " svc_data_uuid16=");
print_bytes(fields->svc_data_uuid16, fields->svc_data_uuid16_len);
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->public_tgt_addr != NULL) {
MODLOG_DFLT(DEBUG, " public_tgt_addr=");
u8p = fields->public_tgt_addr;
for (i = 0; i < fields->num_public_tgt_addrs; i++) {
MODLOG_DFLT(DEBUG, "public_tgt_addr=%s ", addr_str(u8p));
u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN;
}
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->appearance_is_present) {
MODLOG_DFLT(DEBUG, " appearance=0x%04x\n", fields->appearance);
}
if (fields->adv_itvl_is_present) {
MODLOG_DFLT(DEBUG, " adv_itvl=0x%04x\n", fields->adv_itvl);
}
if (fields->svc_data_uuid32 != NULL) {
MODLOG_DFLT(DEBUG, " svc_data_uuid32=");
print_bytes(fields->svc_data_uuid32, fields->svc_data_uuid32_len);
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->svc_data_uuid128 != NULL) {
MODLOG_DFLT(DEBUG, " svc_data_uuid128=");
print_bytes(fields->svc_data_uuid128, fields->svc_data_uuid128_len);
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->uri != NULL) {
MODLOG_DFLT(DEBUG, " uri=");
print_bytes(fields->uri, fields->uri_len);
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->mfg_data != NULL) {
MODLOG_DFLT(DEBUG, " mfg_data=");
print_bytes(fields->mfg_data, fields->mfg_data_len);
MODLOG_DFLT(DEBUG, "\n");
}
}

View file

@ -0,0 +1,807 @@
/*
* 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 "host/ble_hs.h"
#include "blecent.h"
static void *peer_svc_mem;
static struct os_mempool peer_svc_pool;
static void *peer_chr_mem;
static struct os_mempool peer_chr_pool;
static void *peer_dsc_mem;
static struct os_mempool peer_dsc_pool;
static void *peer_mem;
static struct os_mempool peer_pool;
static SLIST_HEAD(, peer) peers;
static struct peer_svc *
peer_svc_find_range(struct peer *peer, uint16_t attr_handle);
static struct peer_svc *
peer_svc_find(struct peer *peer, uint16_t svc_start_handle,
struct peer_svc **out_prev);
int
peer_svc_is_empty(const struct peer_svc *svc);
uint16_t
chr_end_handle(const struct peer_svc *svc, const struct peer_chr *chr);
int
chr_is_empty(const struct peer_svc *svc, const struct peer_chr *chr);
static struct peer_chr *
peer_chr_find(const struct peer_svc *svc, uint16_t chr_def_handle,
struct peer_chr **out_prev);
static void
peer_disc_chrs(struct peer *peer);
static int
peer_dsc_disced(uint16_t conn_handle, const struct ble_gatt_error *error,
uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc,
void *arg);
static struct peer *
peer_find(uint16_t conn_handle)
{
struct peer *peer;
SLIST_FOREACH(peer, &peers, next) {
if (peer->conn_handle == conn_handle) {
return peer;
}
}
return NULL;
}
static void
peer_disc_complete(struct peer *peer, int rc)
{
peer->disc_prev_chr_val = 0;
/* Notify caller that discovery has completed. */
if (peer->disc_cb != NULL) {
peer->disc_cb(peer, rc, peer->disc_cb_arg);
}
}
static struct peer_dsc *
peer_dsc_find_prev(const struct peer_chr *chr, uint16_t dsc_handle)
{
struct peer_dsc *prev;
struct peer_dsc *dsc;
prev = NULL;
SLIST_FOREACH(dsc, &chr->dscs, next) {
if (dsc->dsc.handle >= dsc_handle) {
break;
}
prev = dsc;
}
return prev;
}
static struct peer_dsc *
peer_dsc_find(const struct peer_chr *chr, uint16_t dsc_handle,
struct peer_dsc **out_prev)
{
struct peer_dsc *prev;
struct peer_dsc *dsc;
prev = peer_dsc_find_prev(chr, dsc_handle);
if (prev == NULL) {
dsc = SLIST_FIRST(&chr->dscs);
} else {
dsc = SLIST_NEXT(prev, next);
}
if (dsc != NULL && dsc->dsc.handle != dsc_handle) {
dsc = NULL;
}
if (out_prev != NULL) {
*out_prev = prev;
}
return dsc;
}
static int
peer_dsc_add(struct peer *peer, uint16_t chr_val_handle,
const struct ble_gatt_dsc *gatt_dsc)
{
struct peer_dsc *prev;
struct peer_dsc *dsc;
struct peer_svc *svc;
struct peer_chr *chr;
svc = peer_svc_find_range(peer, chr_val_handle);
if (svc == NULL) {
/* Can't find service for discovered descriptor; this shouldn't
* happen.
*/
assert(0);
return BLE_HS_EUNKNOWN;
}
chr = peer_chr_find(svc, chr_val_handle, NULL);
if (chr == NULL) {
/* Can't find characteristic for discovered descriptor; this shouldn't
* happen.
*/
assert(0);
return BLE_HS_EUNKNOWN;
}
dsc = peer_dsc_find(chr, gatt_dsc->handle, &prev);
if (dsc != NULL) {
/* Descriptor already discovered. */
return 0;
}
dsc = os_memblock_get(&peer_dsc_pool);
if (dsc == NULL) {
/* Out of memory. */
return BLE_HS_ENOMEM;
}
memset(dsc, 0, sizeof *dsc);
dsc->dsc = *gatt_dsc;
if (prev == NULL) {
SLIST_INSERT_HEAD(&chr->dscs, dsc, next);
} else {
SLIST_NEXT(prev, next) = dsc;
}
return 0;
}
static void
peer_disc_dscs(struct peer *peer)
{
struct peer_chr *chr;
struct peer_svc *svc;
int rc;
/* Search through the list of discovered characteristics for the first
* characteristic that contains undiscovered descriptors. Then, discover
* all descriptors belonging to that characteristic.
*/
SLIST_FOREACH(svc, &peer->svcs, next) {
SLIST_FOREACH(chr, &svc->chrs, next) {
if (!chr_is_empty(svc, chr) &&
SLIST_EMPTY(&chr->dscs) &&
peer->disc_prev_chr_val <= chr->chr.def_handle) {
rc = ble_gattc_disc_all_dscs(peer->conn_handle,
chr->chr.val_handle,
chr_end_handle(svc, chr),
peer_dsc_disced, peer);
if (rc != 0) {
peer_disc_complete(peer, rc);
}
peer->disc_prev_chr_val = chr->chr.val_handle;
return;
}
}
}
/* All descriptors discovered. */
peer_disc_complete(peer, 0);
}
static int
peer_dsc_disced(uint16_t conn_handle, const struct ble_gatt_error *error,
uint16_t chr_val_handle, const struct ble_gatt_dsc *dsc,
void *arg)
{
struct peer *peer;
int rc;
peer = arg;
assert(peer->conn_handle == conn_handle);
switch (error->status) {
case 0:
rc = peer_dsc_add(peer, chr_val_handle, dsc);
break;
case BLE_HS_EDONE:
/* All descriptors in this characteristic discovered; start discovering
* descriptors in the next characteristic.
*/
if (peer->disc_prev_chr_val > 0) {
peer_disc_dscs(peer);
}
rc = 0;
break;
default:
/* Error; abort discovery. */
rc = error->status;
break;
}
if (rc != 0) {
/* Error; abort discovery. */
peer_disc_complete(peer, rc);
}
return rc;
}
uint16_t
chr_end_handle(const struct peer_svc *svc, const struct peer_chr *chr)
{
const struct peer_chr *next_chr;
next_chr = SLIST_NEXT(chr, next);
if (next_chr != NULL) {
return next_chr->chr.def_handle - 1;
} else {
return svc->svc.end_handle;
}
}
int
chr_is_empty(const struct peer_svc *svc, const struct peer_chr *chr)
{
return chr_end_handle(svc, chr) <= chr->chr.val_handle;
}
static struct peer_chr *
peer_chr_find_prev(const struct peer_svc *svc, uint16_t chr_val_handle)
{
struct peer_chr *prev;
struct peer_chr *chr;
prev = NULL;
SLIST_FOREACH(chr, &svc->chrs, next) {
if (chr->chr.val_handle >= chr_val_handle) {
break;
}
prev = chr;
}
return prev;
}
static struct peer_chr *
peer_chr_find(const struct peer_svc *svc, uint16_t chr_val_handle,
struct peer_chr **out_prev)
{
struct peer_chr *prev;
struct peer_chr *chr;
prev = peer_chr_find_prev(svc, chr_val_handle);
if (prev == NULL) {
chr = SLIST_FIRST(&svc->chrs);
} else {
chr = SLIST_NEXT(prev, next);
}
if (chr != NULL && chr->chr.val_handle != chr_val_handle) {
chr = NULL;
}
if (out_prev != NULL) {
*out_prev = prev;
}
return chr;
}
static void
peer_chr_delete(struct peer_chr *chr)
{
struct peer_dsc *dsc;
while ((dsc = SLIST_FIRST(&chr->dscs)) != NULL) {
SLIST_REMOVE_HEAD(&chr->dscs, next);
os_memblock_put(&peer_dsc_pool, dsc);
}
os_memblock_put(&peer_chr_pool, chr);
}
static int
peer_chr_add(struct peer *peer, uint16_t svc_start_handle,
const struct ble_gatt_chr *gatt_chr)
{
struct peer_chr *prev;
struct peer_chr *chr;
struct peer_svc *svc;
svc = peer_svc_find(peer, svc_start_handle, NULL);
if (svc == NULL) {
/* Can't find service for discovered characteristic; this shouldn't
* happen.
*/
assert(0);
return BLE_HS_EUNKNOWN;
}
chr = peer_chr_find(svc, gatt_chr->def_handle, &prev);
if (chr != NULL) {
/* Characteristic already discovered. */
return 0;
}
chr = os_memblock_get(&peer_chr_pool);
if (chr == NULL) {
/* Out of memory. */
return BLE_HS_ENOMEM;
}
memset(chr, 0, sizeof *chr);
chr->chr = *gatt_chr;
if (prev == NULL) {
SLIST_INSERT_HEAD(&svc->chrs, chr, next);
} else {
SLIST_NEXT(prev, next) = chr;
}
return 0;
}
static int
peer_chr_disced(uint16_t conn_handle, const struct ble_gatt_error *error,
const struct ble_gatt_chr *chr, void *arg)
{
struct peer *peer;
int rc;
peer = arg;
assert(peer->conn_handle == conn_handle);
switch (error->status) {
case 0:
rc = peer_chr_add(peer, peer->cur_svc->svc.start_handle, chr);
break;
case BLE_HS_EDONE:
/* All characteristics in this service discovered; start discovering
* characteristics in the next service.
*/
if (peer->disc_prev_chr_val > 0) {
peer_disc_chrs(peer);
}
rc = 0;
break;
default:
rc = error->status;
break;
}
if (rc != 0) {
/* Error; abort discovery. */
peer_disc_complete(peer, rc);
}
return rc;
}
static void
peer_disc_chrs(struct peer *peer)
{
struct peer_svc *svc;
int rc;
/* Search through the list of discovered service for the first service that
* contains undiscovered characteristics. Then, discover all
* characteristics belonging to that service.
*/
SLIST_FOREACH(svc, &peer->svcs, next) {
if (!peer_svc_is_empty(svc) && SLIST_EMPTY(&svc->chrs)) {
peer->cur_svc = svc;
rc = ble_gattc_disc_all_chrs(peer->conn_handle,
svc->svc.start_handle,
svc->svc.end_handle,
peer_chr_disced, peer);
if (rc != 0) {
peer_disc_complete(peer, rc);
}
return;
}
}
/* All characteristics discovered. */
peer_disc_dscs(peer);
}
int
peer_svc_is_empty(const struct peer_svc *svc)
{
return svc->svc.end_handle <= svc->svc.start_handle;
}
static struct peer_svc *
peer_svc_find_prev(struct peer *peer, uint16_t svc_start_handle)
{
struct peer_svc *prev;
struct peer_svc *svc;
prev = NULL;
SLIST_FOREACH(svc, &peer->svcs, next) {
if (svc->svc.start_handle >= svc_start_handle) {
break;
}
prev = svc;
}
return prev;
}
static struct peer_svc *
peer_svc_find(struct peer *peer, uint16_t svc_start_handle,
struct peer_svc **out_prev)
{
struct peer_svc *prev;
struct peer_svc *svc;
prev = peer_svc_find_prev(peer, svc_start_handle);
if (prev == NULL) {
svc = SLIST_FIRST(&peer->svcs);
} else {
svc = SLIST_NEXT(prev, next);
}
if (svc != NULL && svc->svc.start_handle != svc_start_handle) {
svc = NULL;
}
if (out_prev != NULL) {
*out_prev = prev;
}
return svc;
}
static struct peer_svc *
peer_svc_find_range(struct peer *peer, uint16_t attr_handle)
{
struct peer_svc *svc;
SLIST_FOREACH(svc, &peer->svcs, next) {
if (svc->svc.start_handle <= attr_handle &&
svc->svc.end_handle >= attr_handle) {
return svc;
}
}
return NULL;
}
const struct peer_svc *
peer_svc_find_uuid(const struct peer *peer, const ble_uuid_t *uuid)
{
const struct peer_svc *svc;
SLIST_FOREACH(svc, &peer->svcs, next) {
if (ble_uuid_cmp(&svc->svc.uuid.u, uuid) == 0) {
return svc;
}
}
return NULL;
}
const struct peer_chr *
peer_chr_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid,
const ble_uuid_t *chr_uuid)
{
const struct peer_svc *svc;
const struct peer_chr *chr;
svc = peer_svc_find_uuid(peer, svc_uuid);
if (svc == NULL) {
return NULL;
}
SLIST_FOREACH(chr, &svc->chrs, next) {
if (ble_uuid_cmp(&chr->chr.uuid.u, chr_uuid) == 0) {
return chr;
}
}
return NULL;
}
const struct peer_dsc *
peer_dsc_find_uuid(const struct peer *peer, const ble_uuid_t *svc_uuid,
const ble_uuid_t *chr_uuid, const ble_uuid_t *dsc_uuid)
{
const struct peer_chr *chr;
const struct peer_dsc *dsc;
chr = peer_chr_find_uuid(peer, svc_uuid, chr_uuid);
if (chr == NULL) {
return NULL;
}
SLIST_FOREACH(dsc, &chr->dscs, next) {
if (ble_uuid_cmp(&dsc->dsc.uuid.u, dsc_uuid) == 0) {
return dsc;
}
}
return NULL;
}
static int
peer_svc_add(struct peer *peer, const struct ble_gatt_svc *gatt_svc)
{
struct peer_svc *prev;
struct peer_svc *svc;
svc = peer_svc_find(peer, gatt_svc->start_handle, &prev);
if (svc != NULL) {
/* Service already discovered. */
return 0;
}
svc = os_memblock_get(&peer_svc_pool);
if (svc == NULL) {
/* Out of memory. */
return BLE_HS_ENOMEM;
}
memset(svc, 0, sizeof *svc);
svc->svc = *gatt_svc;
SLIST_INIT(&svc->chrs);
if (prev == NULL) {
SLIST_INSERT_HEAD(&peer->svcs, svc, next);
} else {
SLIST_INSERT_AFTER(prev, svc, next);
}
return 0;
}
static void
peer_svc_delete(struct peer_svc *svc)
{
struct peer_chr *chr;
while ((chr = SLIST_FIRST(&svc->chrs)) != NULL) {
SLIST_REMOVE_HEAD(&svc->chrs, next);
peer_chr_delete(chr);
}
os_memblock_put(&peer_svc_pool, svc);
}
static int
peer_svc_disced(uint16_t conn_handle, const struct ble_gatt_error *error,
const struct ble_gatt_svc *service, void *arg)
{
struct peer *peer;
int rc;
peer = arg;
assert(peer->conn_handle == conn_handle);
switch (error->status) {
case 0:
rc = peer_svc_add(peer, service);
break;
case BLE_HS_EDONE:
/* All services discovered; start discovering characteristics. */
if (peer->disc_prev_chr_val > 0) {
peer_disc_chrs(peer);
}
rc = 0;
break;
default:
rc = error->status;
break;
}
if (rc != 0) {
/* Error; abort discovery. */
peer_disc_complete(peer, rc);
}
return rc;
}
int
peer_disc_all(uint16_t conn_handle, peer_disc_fn *disc_cb, void *disc_cb_arg)
{
struct peer_svc *svc;
struct peer *peer;
int rc;
peer = peer_find(conn_handle);
if (peer == NULL) {
return BLE_HS_ENOTCONN;
}
/* Undiscover everything first. */
while ((svc = SLIST_FIRST(&peer->svcs)) != NULL) {
SLIST_REMOVE_HEAD(&peer->svcs, next);
peer_svc_delete(svc);
}
peer->disc_prev_chr_val = 1;
peer->disc_cb = disc_cb;
peer->disc_cb_arg = disc_cb_arg;
rc = ble_gattc_disc_all_svcs(conn_handle, peer_svc_disced, peer);
if (rc != 0) {
return rc;
}
return 0;
}
int
peer_delete(uint16_t conn_handle)
{
struct peer_svc *svc;
struct peer *peer;
int rc;
peer = peer_find(conn_handle);
if (peer == NULL) {
return BLE_HS_ENOTCONN;
}
SLIST_REMOVE(&peers, peer, peer, next);
while ((svc = SLIST_FIRST(&peer->svcs)) != NULL) {
SLIST_REMOVE_HEAD(&peer->svcs, next);
peer_svc_delete(svc);
}
rc = os_memblock_put(&peer_pool, peer);
if (rc != 0) {
return BLE_HS_EOS;
}
return 0;
}
int
peer_add(uint16_t conn_handle)
{
struct peer *peer;
/* Make sure the connection handle is unique. */
peer = peer_find(conn_handle);
if (peer != NULL) {
return BLE_HS_EALREADY;
}
peer = os_memblock_get(&peer_pool);
if (peer == NULL) {
/* Out of memory. */
return BLE_HS_ENOMEM;
}
memset(peer, 0, sizeof *peer);
peer->conn_handle = conn_handle;
SLIST_INSERT_HEAD(&peers, peer, next);
return 0;
}
static void
peer_free_mem(void)
{
free(peer_mem);
peer_mem = NULL;
free(peer_svc_mem);
peer_svc_mem = NULL;
free(peer_chr_mem);
peer_chr_mem = NULL;
free(peer_dsc_mem);
peer_dsc_mem = NULL;
}
int
peer_init(int max_peers, int max_svcs, int max_chrs, int max_dscs)
{
int rc;
/* Free memory first in case this function gets called more than once. */
peer_free_mem();
peer_mem = malloc(
OS_MEMPOOL_BYTES(max_peers, sizeof (struct peer)));
if (peer_mem == NULL) {
rc = BLE_HS_ENOMEM;
goto err;
}
rc = os_mempool_init(&peer_pool, max_peers,
sizeof (struct peer), peer_mem,
"peer_pool");
if (rc != 0) {
rc = BLE_HS_EOS;
goto err;
}
peer_svc_mem = malloc(
OS_MEMPOOL_BYTES(max_svcs, sizeof (struct peer_svc)));
if (peer_svc_mem == NULL) {
rc = BLE_HS_ENOMEM;
goto err;
}
rc = os_mempool_init(&peer_svc_pool, max_svcs,
sizeof (struct peer_svc), peer_svc_mem,
"peer_svc_pool");
if (rc != 0) {
rc = BLE_HS_EOS;
goto err;
}
peer_chr_mem = malloc(
OS_MEMPOOL_BYTES(max_chrs, sizeof (struct peer_chr)));
if (peer_chr_mem == NULL) {
rc = BLE_HS_ENOMEM;
goto err;
}
rc = os_mempool_init(&peer_chr_pool, max_chrs,
sizeof (struct peer_chr), peer_chr_mem,
"peer_chr_pool");
if (rc != 0) {
rc = BLE_HS_EOS;
goto err;
}
peer_dsc_mem = malloc(
OS_MEMPOOL_BYTES(max_dscs, sizeof (struct peer_dsc)));
if (peer_dsc_mem == NULL) {
rc = BLE_HS_ENOMEM;
goto err;
}
rc = os_mempool_init(&peer_dsc_pool, max_dscs,
sizeof (struct peer_dsc), peer_dsc_mem,
"peer_dsc_pool");
if (rc != 0) {
rc = BLE_HS_EOS;
goto err;
}
return 0;
err:
peer_free_mem();
return rc;
}

View file

@ -0,0 +1,30 @@
# 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.
#
syscfg.vals:
# DEBUG logging is a bit noisy; use INFO.
LOG_LEVEL: 1
# Default task settings
OS_MAIN_STACK_SIZE: 336
# Disable peripheral and broadcaster roles.
BLE_ROLE_BROADCASTER: 0
BLE_ROLE_CENTRAL: 1
BLE_ROLE_OBSERVER: 1
BLE_ROLE_PERIPHERAL: 0

View file

@ -0,0 +1,9 @@
# BLE Cycling Speed and Cadence peripheral app.
The source files are located in the src/ directory.
pkg.yml contains the base definition of the app.
syscfg.yml contains setting definitions and overrides.

View file

@ -0,0 +1,40 @@
# 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.
#
pkg.name: apps/blecsc
pkg.type: app
pkg.description: BLE peripheral cycling speed and cadence sensor.
pkg.author: "Maciej Jurczak"
pkg.email: "mjurczak@gmail.com"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
pkg.deps:
- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/sys/console/full"
- "@apache-mynewt-core/sys/log/full"
- "@apache-mynewt-core/sys/log/modlog"
- "@apache-mynewt-core/sys/stats/full"
- "@apache-mynewt-core/sys/sysinit"
- "@apache-mynewt-core/sys/id"
- nimble/controller
- nimble/host
- nimble/host/services/gap
- nimble/host/services/gatt
- nimble/host/store/config
- nimble/transport

View file

@ -0,0 +1,105 @@
/*
* 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.
*/
#ifndef H_BLECSC_SENSOR_
#define H_BLECSC_SENSOR_
#include "modlog/modlog.h"
#include "nimble/ble.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Cycling Speed and Cadence configuration */
#define GATT_CSC_UUID 0x1816
#define GATT_CSC_MEASUREMENT_UUID 0x2A5B
#define GATT_CSC_FEATURE_UUID 0x2A5C
#define GATT_SENSOR_LOCATION_UUID 0x2A5D
#define GATT_SC_CONTROL_POINT_UUID 0x2A55
/* Device Information configuration */
#define GATT_DEVICE_INFO_UUID 0x180A
#define GATT_MANUFACTURER_NAME_UUID 0x2A29
#define GATT_MODEL_NUMBER_UUID 0x2A24
/*CSC Measurement flags*/
#define CSC_MEASUREMENT_WHEEL_REV_PRESENT 0x01
#define CSC_MEASUREMENT_CRANK_REV_PRESENT 0x02
/* CSC feature flags */
#define CSC_FEATURE_WHEEL_REV_DATA 0x01
#define CSC_FEATURE_CRANK_REV_DATA 0x02
#define CSC_FEATURE_MULTIPLE_SENSOR_LOC 0x04
/* Sensor location enum */
#define SENSOR_LOCATION_OTHER 0
#define SENSOR_LOCATION_TOP_OF_SHOE 1
#define SENSOR_LOCATION_IN_SHOE 2
#define SENSOR_LOCATION_HIP 3
#define SENSOR_LOCATION_FRONT_WHEEL 4
#define SENSOR_LOCATION_LEFT_CRANK 5
#define SENSOR_LOCATION_RIGHT_CRANK 6
#define SENSOR_LOCATION_LEFT_PEDAL 7
#define SENSOR_LOCATION_RIGHT_PEDAL 8
#define SENSOR_LOCATION_FROT_HUB 9
#define SENSOR_LOCATION_REAR_DROPOUT 10
#define SENSOR_LOCATION_CHAINSTAY 11
#define SENSOR_LOCATION_REAR_WHEEL 12
#define SENSOR_LOCATION_REAR_HUB 13
#define SENSOR_LOCATION_CHEST 14
#define SENSOR_LOCATION_SPIDER 15
#define SENSOR_LOCATION_CHAIN_RING 16
/* SC Control Point op codes */
#define SC_CP_OP_SET_CUMULATIVE_VALUE 1
#define SC_CP_OP_START_SENSOR_CALIBRATION 2
#define SC_CP_OP_UPDATE_SENSOR_LOCATION 3
#define SC_CP_OP_REQ_SUPPORTED_SENSOR_LOCATIONS 4
#define SC_CP_OP_RESPONSE 16
/*SC Control Point response values */
#define SC_CP_RESPONSE_SUCCESS 1
#define SC_CP_RESPONSE_OP_NOT_SUPPORTED 2
#define SC_CP_RESPONSE_INVALID_PARAM 3
#define SC_CP_RESPONSE_OP_FAILED 4
/* CSC simulation configuration */
#define CSC_FEATURES (CSC_FEATURE_WHEEL_REV_DATA | \
CSC_FEATURE_CRANK_REV_DATA |\
CSC_FEATURE_MULTIPLE_SENSOR_LOC)
struct ble_csc_measurement_state {
uint32_t cumulative_wheel_rev;
uint16_t last_wheel_evt_time;
uint16_t cumulative_crank_rev;
uint16_t last_crank_evt_time;
};
extern uint16_t csc_measurement_handle;
extern uint16_t csc_control_point_handle;
int gatt_svr_init(struct ble_csc_measurement_state * csc_measurement_state);
int gatt_svr_chr_notify_csc_measurement(uint16_t conn_handle);
void gatt_svr_set_cp_indicate(uint8_t indication_status);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,385 @@
/*
* 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 <stdio.h>
#include <string.h>
#include "os/mynewt.h"
#include "host/ble_hs.h"
#include "host/ble_uuid.h"
#include "blecsc_sens.h"
#define CSC_ERR_CCC_DESC_IMPROPERLY_CONFIGURED 0x81
static const char *manuf_name = "Apache Mynewt";
static const char *model_num = "Mynewt CSC Sensor";
static const uint8_t csc_supported_sensor_locations[] = {
SENSOR_LOCATION_FRONT_WHEEL,
SENSOR_LOCATION_REAR_DROPOUT,
SENSOR_LOCATION_CHAINSTAY,
SENSOR_LOCATION_REAR_WHEEL
};
static uint8_t sensor_location = SENSOR_LOCATION_REAR_DROPOUT;
static struct ble_csc_measurement_state * measurement_state;
uint16_t csc_measurement_handle;
uint16_t csc_control_point_handle;
uint8_t csc_cp_indication_status;
static int
gatt_svr_chr_access_csc_measurement(uint16_t conn_handle,
uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg);
static int
gatt_svr_chr_access_csc_feature(uint16_t conn_handle,
uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg);
static int
gatt_svr_chr_access_sensor_location(uint16_t conn_handle,
uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg);
static int
gatt_svr_chr_access_sc_control_point(uint16_t conn_handle,
uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg);
static int
gatt_svr_chr_access_device_info(uint16_t conn_handle,
uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg);
static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
{
/* Service: Cycling Speed and Cadence */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = BLE_UUID16_DECLARE(GATT_CSC_UUID),
.characteristics = (struct ble_gatt_chr_def[]) { {
/* Characteristic: Cycling Speed and Cadence Measurement */
.uuid = BLE_UUID16_DECLARE(GATT_CSC_MEASUREMENT_UUID),
.access_cb = gatt_svr_chr_access_csc_measurement,
.val_handle = &csc_measurement_handle,
.flags = BLE_GATT_CHR_F_NOTIFY,
}, {
/* Characteristic: Cycling Speed and Cadence features */
.uuid = BLE_UUID16_DECLARE(GATT_CSC_FEATURE_UUID),
.access_cb = gatt_svr_chr_access_csc_feature,
.flags = BLE_GATT_CHR_F_READ,
}, {
/* Characteristic: Sensor Location */
.uuid = BLE_UUID16_DECLARE(GATT_SENSOR_LOCATION_UUID),
.access_cb = gatt_svr_chr_access_sensor_location,
.flags = BLE_GATT_CHR_F_READ,
}, {
/* Characteristic: SC Control Point*/
.uuid = BLE_UUID16_DECLARE(GATT_SC_CONTROL_POINT_UUID),
.access_cb = gatt_svr_chr_access_sc_control_point,
.val_handle = &csc_control_point_handle,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_INDICATE,
}, {
0, /* No more characteristics in this service */
}, }
},
{
/* Service: Device Information */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = BLE_UUID16_DECLARE(GATT_DEVICE_INFO_UUID),
.characteristics = (struct ble_gatt_chr_def[]) { {
/* Characteristic: * Manufacturer name */
.uuid = BLE_UUID16_DECLARE(GATT_MANUFACTURER_NAME_UUID),
.access_cb = gatt_svr_chr_access_device_info,
.flags = BLE_GATT_CHR_F_READ,
}, {
/* Characteristic: Model number string */
.uuid = BLE_UUID16_DECLARE(GATT_MODEL_NUMBER_UUID),
.access_cb = gatt_svr_chr_access_device_info,
.flags = BLE_GATT_CHR_F_READ,
}, {
0, /* No more characteristics in this service */
}, }
},
{
0, /* No more services */
},
};
static int
gatt_svr_chr_access_csc_measurement(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
return BLE_ATT_ERR_READ_NOT_PERMITTED;
}
static int
gatt_svr_chr_access_csc_feature(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
static const uint16_t csc_feature = CSC_FEATURES;
int rc;
assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
rc = os_mbuf_append(ctxt->om, &csc_feature, sizeof(csc_feature));
return (rc == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
static int
gatt_svr_chr_access_sensor_location(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
int rc;
assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
rc = os_mbuf_append(ctxt->om, &sensor_location, sizeof(sensor_location));
return (rc == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
static int
gatt_svr_chr_access_sc_control_point(uint16_t conn_handle,
uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg)
{
uint8_t op_code;
uint8_t new_sensor_location;
uint8_t new_cumulative_wheel_rev_arr[4];
struct os_mbuf *om_indication;
uint8_t response = SC_CP_RESPONSE_OP_NOT_SUPPORTED;
int ii;
int rc;
assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR);
if (!csc_cp_indication_status) {
MODLOG_DFLT(INFO, "SC Control Point; CCC descriptor "
"improperly configured");
return CSC_ERR_CCC_DESC_IMPROPERLY_CONFIGURED;
}
/* Read control point op code*/
rc = os_mbuf_copydata(ctxt->om, 0, sizeof(op_code), &op_code);
if (rc != 0){
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
MODLOG_DFLT(INFO, "SC Control Point; opcode=%d\n", op_code);
/* Allocate response buffer */
om_indication = ble_hs_mbuf_att_pkt();
switch(op_code){
#if (CSC_FEATURES & CSC_FEATURE_WHEEL_REV_DATA)
case SC_CP_OP_SET_CUMULATIVE_VALUE:
/* Read new cumulative wheel revolutions value*/
rc = os_mbuf_copydata(ctxt->om, 1,
sizeof(new_cumulative_wheel_rev_arr),
new_cumulative_wheel_rev_arr);
if (rc != 0){
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
measurement_state->cumulative_wheel_rev =
get_le32(new_cumulative_wheel_rev_arr);
MODLOG_DFLT(INFO, "SC Control Point; Set cumulative value = %d\n",
measurement_state->cumulative_wheel_rev);
response = SC_CP_RESPONSE_SUCCESS;
break;
#endif
#if (CSC_FEATURES & CSC_FEATURE_MULTIPLE_SENSOR_LOC)
case SC_CP_OP_UPDATE_SENSOR_LOCATION:
/* Read new sensor location value*/
rc = os_mbuf_copydata(ctxt->om, 1, 1, &new_sensor_location);
if (rc != 0){
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
MODLOG_DFLT(INFO, "SC Control Point; Sensor location update = %d\n",
new_sensor_location);
/* Verify if requested new location is on supported locations list */
response = SC_CP_RESPONSE_INVALID_PARAM;
for (ii = 0; ii < sizeof(csc_supported_sensor_locations); ii++){
if (new_sensor_location == csc_supported_sensor_locations[ii]){
sensor_location = new_sensor_location;
response = SC_CP_RESPONSE_SUCCESS;
break;
}
}
break;
case SC_CP_OP_REQ_SUPPORTED_SENSOR_LOCATIONS:
response = SC_CP_RESPONSE_SUCCESS;
break;
#endif
default:
break;
}
/* Append response value */
rc = os_mbuf_append(om_indication, &response, sizeof(response));
if (rc != 0){
return BLE_ATT_ERR_INSUFFICIENT_RES;
}
#if (CSC_FEATURES & CSC_FEATURE_MULTIPLE_SENSOR_LOC)
/* In case of supported locations request append locations list */
if (op_code == SC_CP_OP_REQ_SUPPORTED_SENSOR_LOCATIONS){
rc = os_mbuf_append(om_indication, &csc_supported_sensor_locations,
sizeof(csc_supported_sensor_locations));
}
if (rc != 0){
return BLE_ATT_ERR_INSUFFICIENT_RES;
}
#endif
rc = ble_gattc_indicate_custom(conn_handle, csc_control_point_handle,
om_indication);
return rc;
}
static int
gatt_svr_chr_access_device_info(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
uint16_t uuid;
int rc;
uuid = ble_uuid_u16(ctxt->chr->uuid);
if (uuid == GATT_MODEL_NUMBER_UUID) {
rc = os_mbuf_append(ctxt->om, model_num, strlen(model_num));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
if (uuid == GATT_MANUFACTURER_NAME_UUID) {
rc = os_mbuf_append(ctxt->om, manuf_name, strlen(manuf_name));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
assert(0);
return BLE_ATT_ERR_UNLIKELY;
}
int
gatt_svr_chr_notify_csc_measurement(uint16_t conn_handle)
{
int rc;
struct os_mbuf *om;
uint8_t data_buf[11];
uint8_t data_offset = 1;
memset(data_buf, 0, sizeof(data_buf));
#if (CSC_FEATURES & CSC_FEATURE_WHEEL_REV_DATA)
data_buf[0] |= CSC_MEASUREMENT_WHEEL_REV_PRESENT;
put_le16(&(data_buf[5]), measurement_state->last_wheel_evt_time);
put_le32(&(data_buf[1]), measurement_state->cumulative_wheel_rev);
data_offset += 6;
#endif
#if (CSC_FEATURES & CSC_FEATURE_CRANK_REV_DATA)
data_buf[0] |= CSC_MEASUREMENT_CRANK_REV_PRESENT;
put_le16(&(data_buf[data_offset]),
measurement_state->cumulative_crank_rev);
put_le16(&(data_buf[data_offset + 2]),
measurement_state->last_crank_evt_time);
data_offset += 4;
#endif
om = ble_hs_mbuf_from_flat(data_buf, data_offset);
rc = ble_gattc_notify_custom(conn_handle, csc_measurement_handle, om);
return rc;
}
void
gatt_svr_set_cp_indicate(uint8_t indication_status)
{
csc_cp_indication_status = indication_status;
}
void
gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
{
char buf[BLE_UUID_STR_LEN];
switch (ctxt->op) {
case BLE_GATT_REGISTER_OP_SVC:
MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n",
ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf),
ctxt->svc.handle);
break;
case BLE_GATT_REGISTER_OP_CHR:
MODLOG_DFLT(DEBUG, "registering characteristic %s with "
"def_handle=%d val_handle=%d\n",
ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf),
ctxt->chr.def_handle,
ctxt->chr.val_handle);
break;
case BLE_GATT_REGISTER_OP_DSC:
MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n",
ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf),
ctxt->dsc.handle);
break;
default:
assert(0);
break;
}
}
int
gatt_svr_init(struct ble_csc_measurement_state * csc_measurement_state)
{
int rc;
rc = ble_gatts_count_cfg(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
rc = ble_gatts_add_svcs(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
measurement_state = csc_measurement_state;
return 0;
}

View file

@ -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;
}

View file

@ -0,0 +1,38 @@
# 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.
syscfg.vals:
# Disable central and observer roles.
BLE_ROLE_BROADCASTER: 1
BLE_ROLE_CENTRAL: 0
BLE_ROLE_OBSERVER: 0
BLE_ROLE_PERIPHERAL: 1
# Disable unused eddystone feature.
BLE_EDDYSTONE: 0
# Log reboot messages to a flash circular buffer.
REBOOT_LOG_FCB: 1
LOG_FCB: 1
CONFIG_FCB: 1
# Set public device address.
BLE_PUBLIC_DEV_ADDR: ((uint8_t[6]){0xcc, 0xbb, 0xaa, 0x33, 0x22, 0x11})
# Set device appearance to Cycling Speed and Cadence Sensor
BLE_SVC_GAP_APPEARANCE: BLE_SVC_GAP_APPEARANCE_CYC_SPEED_AND_CADENCE_SENSOR

View file

@ -0,0 +1,34 @@
# 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.
#
pkg.name: apps/blehci
pkg.type: app
pkg.description: BLE controller application exposing HCI over external interface
pkg.author: "Johan Hedberg <johan.hedberg@intel.com>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
pkg.deps:
- "@apache-mynewt-core/sys/console/stub"
- "@apache-mynewt-core/sys/log/stub"
- "@apache-mynewt-core/sys/stats/full"
- "@apache-mynewt-core/kernel/os"
- nimble/controller
- nimble/transport
pkg.req_apis:
- ble_transport

View file

@ -0,0 +1,33 @@
/*
* 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 "os/mynewt.h"
int
main(void)
{
/* Initialize OS */
sysinit();
while (1) {
os_eventq_run(os_eventq_dflt_get());
}
return 0;
}

View file

@ -0,0 +1,23 @@
# 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.
#
syscfg.vals:
# Default task settings
OS_MAIN_STACK_SIZE: 64
# Use UART transport by default
BLE_HCI_TRANSPORT: uart

View file

@ -0,0 +1,9 @@
# BLE Heart Rate peripheral app.
The source files are located in the src/ directory.
pkg.yml contains the base definition of the app.
syscfg.yml contains setting definitions and overrides.

View file

@ -0,0 +1,40 @@
# 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.
#
pkg.name: apps/blehr
pkg.type: app
pkg.description: BLE peripheral heartrate sensor.
pkg.author: "Szymon Czapracki"
pkg.email: "szymon.czapracki@codecoup.pl"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
pkg.deps:
- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/sys/console/full"
- "@apache-mynewt-core/sys/log/full"
- "@apache-mynewt-core/sys/log/modlog"
- "@apache-mynewt-core/sys/stats/full"
- "@apache-mynewt-core/sys/sysinit"
- "@apache-mynewt-core/sys/id"
- nimble/controller
- nimble/host
- nimble/host/services/gap
- nimble/host/services/gatt
- nimble/host/store/config
- nimble/transport/ram

View file

@ -0,0 +1,46 @@
/*
* 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.
*/
#ifndef H_BLEHR_SENSOR_
#define H_BLEHR_SENSOR_
#include "nimble/ble.h"
#include "modlog/modlog.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Heart-rate configuration */
#define GATT_HRS_UUID 0x180D
#define GATT_HRS_MEASUREMENT_UUID 0x2A37
#define GATT_HRS_BODY_SENSOR_LOC_UUID 0x2A38
#define GATT_DEVICE_INFO_UUID 0x180A
#define GATT_MANUFACTURER_NAME_UUID 0x2A29
#define GATT_MODEL_NUMBER_UUID 0x2A24
extern uint16_t hrs_hrm_handle;
int gatt_svr_init(void);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,177 @@
/*
* 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 <stdio.h>
#include <string.h>
#include "host/ble_hs.h"
#include "host/ble_uuid.h"
#include "blehr_sens.h"
static const char *manuf_name = "Apache Mynewt";
static const char *model_num = "Mynewt HR Sensor";
uint16_t hrs_hrm_handle;
static int
gatt_svr_chr_access_heart_rate(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);
static int
gatt_svr_chr_access_device_info(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);
static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
{
/* Service: Heart-rate */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = BLE_UUID16_DECLARE(GATT_HRS_UUID),
.characteristics = (struct ble_gatt_chr_def[]) { {
/* Characteristic: Heart-rate measurement */
.uuid = BLE_UUID16_DECLARE(GATT_HRS_MEASUREMENT_UUID),
.access_cb = gatt_svr_chr_access_heart_rate,
.val_handle = &hrs_hrm_handle,
.flags = BLE_GATT_CHR_F_NOTIFY,
}, {
/* Characteristic: Body sensor location */
.uuid = BLE_UUID16_DECLARE(GATT_HRS_BODY_SENSOR_LOC_UUID),
.access_cb = gatt_svr_chr_access_heart_rate,
.flags = BLE_GATT_CHR_F_READ,
}, {
0, /* No more characteristics in this service */
}, }
},
{
/* Service: Device Information */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = BLE_UUID16_DECLARE(GATT_DEVICE_INFO_UUID),
.characteristics = (struct ble_gatt_chr_def[]) { {
/* Characteristic: * Manufacturer name */
.uuid = BLE_UUID16_DECLARE(GATT_MANUFACTURER_NAME_UUID),
.access_cb = gatt_svr_chr_access_device_info,
.flags = BLE_GATT_CHR_F_READ,
}, {
/* Characteristic: Model number string */
.uuid = BLE_UUID16_DECLARE(GATT_MODEL_NUMBER_UUID),
.access_cb = gatt_svr_chr_access_device_info,
.flags = BLE_GATT_CHR_F_READ,
}, {
0, /* No more characteristics in this service */
}, }
},
{
0, /* No more services */
},
};
static int
gatt_svr_chr_access_heart_rate(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
/* Sensor location, set to "Chest" */
static uint8_t body_sens_loc = 0x01;
uint16_t uuid;
int rc;
uuid = ble_uuid_u16(ctxt->chr->uuid);
if (uuid == GATT_HRS_BODY_SENSOR_LOC_UUID) {
rc = os_mbuf_append(ctxt->om, &body_sens_loc, sizeof(body_sens_loc));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
assert(0);
return BLE_ATT_ERR_UNLIKELY;
}
static int
gatt_svr_chr_access_device_info(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
uint16_t uuid;
int rc;
uuid = ble_uuid_u16(ctxt->chr->uuid);
if (uuid == GATT_MODEL_NUMBER_UUID) {
rc = os_mbuf_append(ctxt->om, model_num, strlen(model_num));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
if (uuid == GATT_MANUFACTURER_NAME_UUID) {
rc = os_mbuf_append(ctxt->om, manuf_name, strlen(manuf_name));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
assert(0);
return BLE_ATT_ERR_UNLIKELY;
}
void
gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
{
char buf[BLE_UUID_STR_LEN];
switch (ctxt->op) {
case BLE_GATT_REGISTER_OP_SVC:
MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n",
ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf),
ctxt->svc.handle);
break;
case BLE_GATT_REGISTER_OP_CHR:
MODLOG_DFLT(DEBUG, "registering characteristic %s with "
"def_handle=%d val_handle=%d\n",
ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf),
ctxt->chr.def_handle,
ctxt->chr.val_handle);
break;
case BLE_GATT_REGISTER_OP_DSC:
MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n",
ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf),
ctxt->dsc.handle);
break;
default:
assert(0);
break;
}
}
int
gatt_svr_init(void)
{
int rc;
rc = ble_gatts_count_cfg(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
rc = ble_gatts_add_svcs(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
return 0;
}

View file

@ -0,0 +1,261 @@
/*
* 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 "blehr_sens.h"
static bool notify_state;
/* Connection handle */
static uint16_t conn_handle;
static const char *device_name = "blehr_sensor";
static int blehr_gap_event(struct ble_gap_event *event, void *arg);
static uint8_t blehr_addr_type;
/* Sending notify data timer */
static struct os_callout blehr_tx_timer;
/* Variable to simulate heart beats */
static uint8_t heartrate = 90;
/*
* Enables advertising with parameters:
* o General discoverable mode
* o Undirected connectable mode
*/
static void
blehr_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;
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(blehr_addr_type, NULL, BLE_HS_FOREVER,
&adv_params, blehr_gap_event, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc);
return;
}
}
static void
blehr_tx_hrate_stop(void)
{
os_callout_stop(&blehr_tx_timer);
}
/* Reset heartrate measurment */
static void
blehr_tx_hrate_reset(void)
{
int rc;
rc = os_callout_reset(&blehr_tx_timer, OS_TICKS_PER_SEC);
assert(rc == 0);
}
/* This functions simulates heart beat and notifies it to the client */
static void
blehr_tx_hrate(struct os_event *ev)
{
static uint8_t hrm[2];
int rc;
struct os_mbuf *om;
if (!notify_state) {
blehr_tx_hrate_stop();
heartrate = 90;
return;
}
hrm[0] = 0x06; /* contact of a sensor */
hrm[1] = heartrate; /* storing dummy data */
/* Simulation of heart beats */
heartrate++;
if (heartrate == 160) {
heartrate = 90;
}
om = ble_hs_mbuf_from_flat(hrm, sizeof(hrm));
rc = ble_gattc_notify_custom(conn_handle, hrs_hrm_handle, om);
assert(rc == 0);
blehr_tx_hrate_reset();
}
static int
blehr_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 */
blehr_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 = BLE_HS_CONN_HANDLE_NONE; /* reset conn_handle */
/* Connection terminated; resume advertising */
blehr_advertise();
break;
case BLE_GAP_EVENT_ADV_COMPLETE:
MODLOG_DFLT(INFO, "adv complete\n");
blehr_advertise();
break;
case BLE_GAP_EVENT_SUBSCRIBE:
MODLOG_DFLT(INFO, "subscribe event; cur_notify=%d\n value handle; "
"val_handle=%d\n",
event->subscribe.cur_notify, hrs_hrm_handle);
if (event->subscribe.attr_handle == hrs_hrm_handle) {
notify_state = event->subscribe.cur_notify;
blehr_tx_hrate_reset();
} else if (event->subscribe.attr_handle != hrs_hrm_handle) {
notify_state = event->subscribe.cur_notify;
blehr_tx_hrate_stop();
}
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
blehr_on_sync(void)
{
int rc;
/* Use privacy */
rc = ble_hs_id_infer_auto(0, &blehr_addr_type);
assert(rc == 0);
/* Begin advertising */
blehr_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 = blehr_on_sync;
os_callout_init(&blehr_tx_timer, os_eventq_dflt_get(),
blehr_tx_hrate, NULL);
rc = gatt_svr_init();
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;
}

View file

@ -0,0 +1,35 @@
# 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.
syscfg.vals:
# Disable central and observer roles.
BLE_ROLE_BROADCASTER: 1
BLE_ROLE_CENTRAL: 0
BLE_ROLE_OBSERVER: 0
BLE_ROLE_PERIPHERAL: 1
# Disable unused eddystone feature.
BLE_EDDYSTONE: 0
# Log reboot messages to a flash circular buffer.
REBOOT_LOG_FCB: 1
LOG_FCB: 1
CONFIG_FCB: 1
# Set public device address.
BLE_PUBLIC_DEV_ADDR: ((uint8_t[6]){0xcc, 0xbb, 0xaa, 0x33, 0x22, 0x11})

View file

@ -0,0 +1,37 @@
# 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.
#
pkg.name: apps/blemesh
pkg.type: app
pkg.description: Sample application for BLE Mesh node with on/off model
pkg.author: "Łukasz Rymanowski <lukasz.rymanowski@codecoup.pl>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
pkg.deps:
- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/sys/console/full"
- "@apache-mynewt-core/sys/log/full"
- "@apache-mynewt-core/sys/log/modlog"
- "@apache-mynewt-core/sys/stats/full"
- "@apache-mynewt-core/sys/shell"
- nimble/controller
- nimble/host
- nimble/host/services/gap
- nimble/host/services/gatt
- nimble/host/store/ram
- nimble/transport/ram

View file

@ -0,0 +1,468 @@
/*
* 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 "os/mynewt.h"
#include "mesh/mesh.h"
#include "console/console.h"
#include "hal/hal_system.h"
#include "hal/hal_gpio.h"
#include "bsp/bsp.h"
#include "shell/shell.h"
/* BLE */
#include "nimble/ble.h"
#include "host/ble_hs.h"
#include "services/gap/ble_svc_gap.h"
#include "mesh/glue.h"
/* Company ID */
#define CID_VENDOR 0x05C3
#define STANDARD_TEST_ID 0x00
#define TEST_ID 0x01
static int recent_test_id = STANDARD_TEST_ID;
#define FAULT_ARR_SIZE 2
static bool has_reg_fault = true;
static struct bt_mesh_cfg_srv cfg_srv = {
.relay = BT_MESH_RELAY_DISABLED,
.beacon = BT_MESH_BEACON_ENABLED,
#if MYNEWT_VAL(BLE_MESH_FRIEND)
.frnd = BT_MESH_FRIEND_ENABLED,
#else
.gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED,
#endif
#if MYNEWT_VAL(BLE_MESH_GATT_PROXY)
.gatt_proxy = BT_MESH_GATT_PROXY_ENABLED,
#else
.gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED,
#endif
.default_ttl = 7,
/* 3 transmissions with 20ms interval */
.net_transmit = BT_MESH_TRANSMIT(2, 20),
.relay_retransmit = BT_MESH_TRANSMIT(2, 20),
};
static int
fault_get_cur(struct bt_mesh_model *model,
uint8_t *test_id,
uint16_t *company_id,
uint8_t *faults,
uint8_t *fault_count)
{
uint8_t reg_faults[FAULT_ARR_SIZE] = { [0 ... FAULT_ARR_SIZE-1] = 0xff };
console_printf("fault_get_cur() has_reg_fault %u\n", has_reg_fault);
*test_id = recent_test_id;
*company_id = CID_VENDOR;
*fault_count = min(*fault_count, sizeof(reg_faults));
memcpy(faults, reg_faults, *fault_count);
return 0;
}
static int
fault_get_reg(struct bt_mesh_model *model,
uint16_t company_id,
uint8_t *test_id,
uint8_t *faults,
uint8_t *fault_count)
{
if (company_id != CID_VENDOR) {
return -BLE_HS_EINVAL;
}
console_printf("fault_get_reg() has_reg_fault %u\n", has_reg_fault);
*test_id = recent_test_id;
if (has_reg_fault) {
uint8_t reg_faults[FAULT_ARR_SIZE] = { [0 ... FAULT_ARR_SIZE-1] = 0xff };
*fault_count = min(*fault_count, sizeof(reg_faults));
memcpy(faults, reg_faults, *fault_count);
} else {
*fault_count = 0;
}
return 0;
}
static int
fault_clear(struct bt_mesh_model *model, uint16_t company_id)
{
if (company_id != CID_VENDOR) {
return -BLE_HS_EINVAL;
}
has_reg_fault = false;
return 0;
}
static int
fault_test(struct bt_mesh_model *model, uint8_t test_id, uint16_t company_id)
{
if (company_id != CID_VENDOR) {
return -BLE_HS_EINVAL;
}
if (test_id != STANDARD_TEST_ID && test_id != TEST_ID) {
return -BLE_HS_EINVAL;
}
recent_test_id = test_id;
has_reg_fault = true;
bt_mesh_fault_update(bt_mesh_model_elem(model));
return 0;
}
static const struct bt_mesh_health_srv_cb health_srv_cb = {
.fault_get_cur = &fault_get_cur,
.fault_get_reg = &fault_get_reg,
.fault_clear = &fault_clear,
.fault_test = &fault_test,
};
static struct bt_mesh_health_srv health_srv = {
.cb = &health_srv_cb,
};
static struct bt_mesh_model_pub health_pub;
static void
health_pub_init(void)
{
health_pub.msg = BT_MESH_HEALTH_FAULT_MSG(0);
}
static struct bt_mesh_model_pub gen_level_pub;
static struct bt_mesh_model_pub gen_onoff_pub;
static uint8_t gen_on_off_state;
static int16_t gen_level_state;
static void gen_onoff_status(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx)
{
struct os_mbuf *msg = NET_BUF_SIMPLE(3);
uint8_t *status;
console_printf("#mesh-onoff STATUS\n");
bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x04));
status = net_buf_simple_add(msg, 1);
*status = gen_on_off_state;
if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
console_printf("#mesh-onoff STATUS: send status failed\n");
}
os_mbuf_free_chain(msg);
}
static void gen_onoff_get(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
console_printf("#mesh-onoff GET\n");
gen_onoff_status(model, ctx);
}
static void gen_onoff_set(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
console_printf("#mesh-onoff SET\n");
gen_on_off_state = buf->om_data[0];
hal_gpio_write(LED_2, !gen_on_off_state);
gen_onoff_status(model, ctx);
}
static void gen_onoff_set_unack(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
console_printf("#mesh-onoff SET-UNACK\n");
gen_on_off_state = buf->om_data[0];
hal_gpio_write(LED_2, !gen_on_off_state);
}
static const struct bt_mesh_model_op gen_onoff_op[] = {
{ BT_MESH_MODEL_OP_2(0x82, 0x01), 0, gen_onoff_get },
{ BT_MESH_MODEL_OP_2(0x82, 0x02), 2, gen_onoff_set },
{ BT_MESH_MODEL_OP_2(0x82, 0x03), 2, gen_onoff_set_unack },
BT_MESH_MODEL_OP_END,
};
static void gen_level_status(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx)
{
struct os_mbuf *msg = NET_BUF_SIMPLE(4);
console_printf("#mesh-level STATUS\n");
bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x08));
net_buf_simple_add_le16(msg, gen_level_state);
if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
console_printf("#mesh-level STATUS: send status failed\n");
}
os_mbuf_free_chain(msg);
}
static void gen_level_get(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
console_printf("#mesh-level GET\n");
gen_level_status(model, ctx);
}
static void gen_level_set(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
int16_t level;
level = (int16_t) net_buf_simple_pull_le16(buf);
console_printf("#mesh-level SET: level=%d\n", level);
gen_level_status(model, ctx);
gen_level_state = level;
console_printf("#mesh-level: level=%d\n", gen_level_state);
}
static void gen_level_set_unack(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
int16_t level;
level = (int16_t) net_buf_simple_pull_le16(buf);
console_printf("#mesh-level SET-UNACK: level=%d\n", level);
gen_level_state = level;
console_printf("#mesh-level: level=%d\n", gen_level_state);
}
static void gen_delta_set(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
int16_t delta_level;
delta_level = (int16_t) net_buf_simple_pull_le16(buf);
console_printf("#mesh-level DELTA-SET: delta_level=%d\n", delta_level);
gen_level_status(model, ctx);
gen_level_state += delta_level;
console_printf("#mesh-level: level=%d\n", gen_level_state);
}
static void gen_delta_set_unack(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
int16_t delta_level;
delta_level = (int16_t) net_buf_simple_pull_le16(buf);
console_printf("#mesh-level DELTA-SET: delta_level=%d\n", delta_level);
gen_level_state += delta_level;
console_printf("#mesh-level: level=%d\n", gen_level_state);
}
static void gen_move_set(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
}
static void gen_move_set_unack(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
}
static const struct bt_mesh_model_op gen_level_op[] = {
{ BT_MESH_MODEL_OP_2(0x82, 0x05), 0, gen_level_get },
{ BT_MESH_MODEL_OP_2(0x82, 0x06), 3, gen_level_set },
{ BT_MESH_MODEL_OP_2(0x82, 0x07), 3, gen_level_set_unack },
{ BT_MESH_MODEL_OP_2(0x82, 0x09), 5, gen_delta_set },
{ BT_MESH_MODEL_OP_2(0x82, 0x0a), 5, gen_delta_set_unack },
{ BT_MESH_MODEL_OP_2(0x82, 0x0b), 3, gen_move_set },
{ BT_MESH_MODEL_OP_2(0x82, 0x0c), 3, gen_move_set_unack },
BT_MESH_MODEL_OP_END,
};
static struct bt_mesh_model root_models[] = {
BT_MESH_MODEL_CFG_SRV(&cfg_srv),
BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_op,
&gen_onoff_pub, NULL),
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_LEVEL_SRV, gen_level_op,
&gen_level_pub, NULL),
};
static struct bt_mesh_model_pub vnd_model_pub;
static void vnd_model_recv(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
struct os_mbuf *msg = NET_BUF_SIMPLE(3);
console_printf("#vendor-model-recv\n");
console_printf("data:%s len:%d\n", bt_hex(buf->om_data, buf->om_len),
buf->om_len);
bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_3(0x01, CID_VENDOR));
os_mbuf_append(msg, buf->om_data, buf->om_len);
if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
console_printf("#vendor-model-recv: send rsp failed\n");
}
os_mbuf_free_chain(msg);
}
static const struct bt_mesh_model_op vnd_model_op[] = {
{ BT_MESH_MODEL_OP_3(0x01, CID_VENDOR), 0, vnd_model_recv },
BT_MESH_MODEL_OP_END,
};
static struct bt_mesh_model vnd_models[] = {
BT_MESH_MODEL_VND(CID_VENDOR, BT_MESH_MODEL_ID_GEN_ONOFF_SRV, vnd_model_op,
&vnd_model_pub, NULL),
};
static struct bt_mesh_elem elements[] = {
BT_MESH_ELEM(0, root_models, vnd_models),
};
static const struct bt_mesh_comp comp = {
.cid = CID_VENDOR,
.elem = elements,
.elem_count = ARRAY_SIZE(elements),
};
static int output_number(bt_mesh_output_action_t action, uint32_t number)
{
console_printf("OOB Number: %lu\n", number);
return 0;
}
static void prov_complete(u16_t net_idx, u16_t addr)
{
console_printf("Local node provisioned, primary address 0x%04x\n", addr);
}
static const uint8_t dev_uuid[16] = MYNEWT_VAL(BLE_MESH_DEV_UUID);
static const struct bt_mesh_prov prov = {
.uuid = dev_uuid,
.output_size = 4,
.output_actions = BT_MESH_DISPLAY_NUMBER | BT_MESH_BEEP | BT_MESH_VIBRATE | BT_MESH_BLINK,
.output_number = output_number,
.complete = prov_complete,
};
static void
blemesh_on_reset(int reason)
{
BLE_HS_LOG(ERROR, "Resetting state; reason=%d\n", reason);
}
static void
blemesh_on_sync(void)
{
int err;
ble_addr_t addr;
console_printf("Bluetooth initialized\n");
/* Use NRPA */
err = ble_hs_id_gen_rnd(1, &addr);
assert(err == 0);
err = ble_hs_id_set_rnd(addr.val);
assert(err == 0);
err = bt_mesh_init(addr.type, &prov, &comp);
if (err) {
console_printf("Initializing mesh failed (err %d)\n", err);
return;
}
#if (MYNEWT_VAL(BLE_MESH_SHELL))
shell_register_default_module("mesh");
#endif
console_printf("Mesh initialized\n");
if (IS_ENABLED(CONFIG_SETTINGS)) {
settings_load();
}
if (bt_mesh_is_provisioned()) {
printk("Mesh network restored from flash\n");
}
}
int
main(int argc, char **argv)
{
#ifdef ARCH_sim
mcu_sim_parse_args(argc, argv);
#endif
/* Initialize OS */
sysinit();
/* Initialize the NimBLE host configuration. */
ble_hs_cfg.reset_cb = blemesh_on_reset;
ble_hs_cfg.sync_cb = blemesh_on_sync;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
hal_gpio_init_out(LED_2, 0);
health_pub_init();
while (1) {
os_eventq_run(os_eventq_dflt_get());
}
return 0;
}

View file

@ -0,0 +1,39 @@
# 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.
#
syscfg.vals:
# Enable the shell task.
SHELL_TASK: 1
# Set log level to info (disable debug logging).
LOG_LEVEL: 1
# Default task settings
OS_MAIN_STACK_SIZE: 768
# MCUmgr SMP is not supported in this app, so disable smp-over-shell.
SHELL_MGMT: 0
BLE_MESH: 1
MSYS_1_BLOCK_COUNT: 48
BLE_MESH_ADV_BUF_COUNT: 20
BLE_MESH_TX_SEG_MAX: 6
BLE_MESH_SETTINGS: 0
CONFIG_NFFS: 0

View file

@ -0,0 +1,37 @@
# 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.
#
pkg.name: apps/blemesh_light
pkg.type: app
pkg.description: Sample application for BLE Mesh node with Light model
pkg.author: "Michał Narajowski <michal.narajowski@codecoup.pl>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
pkg.deps:
- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/sys/console/full"
- "@apache-mynewt-core/sys/log/full"
- "@apache-mynewt-core/sys/log/modlog"
- "@apache-mynewt-core/sys/stats/full"
- "@apache-mynewt-core/sys/shell"
- nimble/controller
- nimble/host
- nimble/host/services/gap
- nimble/host/services/gatt
- nimble/host/store/ram
- nimble/transport/ram

View file

@ -0,0 +1,242 @@
/*
* 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 "syscfg/syscfg.h"
#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS)
#include "mesh/mesh.h"
#include "bsp.h"
#include "pwm/pwm.h"
#include "light_model.h"
#include "ws2812.h"
#if (!MYNEWT_VAL(USE_NEOPIXEL))
#if MYNEWT_VAL(PWM_0)
struct pwm_dev *pwm0;
#endif
#if MYNEWT_VAL(PWM_1)
struct pwm_dev *pwm1;
#endif
#if MYNEWT_VAL(PWM_2)
struct pwm_dev *pwm2;
#endif
#if MYNEWT_VAL(PWM_3)
struct pwm_dev *pwm3;
#endif
static uint16_t top_val;
#endif
#if (MYNEWT_VAL(USE_NEOPIXEL))
static uint32_t neopixel[WS2812_NUM_LED];
#endif
static u8_t gen_onoff_state;
static s16_t gen_level_state;
static void light_set_lightness(u8_t percentage)
{
#if (!MYNEWT_VAL(USE_NEOPIXEL))
int rc;
uint16_t pwm_val = (uint16_t) (percentage * top_val / 100);
#if MYNEWT_VAL(PWM_0)
rc = pwm_set_duty_cycle(pwm0, 0, pwm_val);
assert(rc == 0);
#endif
#if MYNEWT_VAL(PWM_1)
rc = pwm_set_duty_cycle(pwm1, 0, pwm_val);
assert(rc == 0);
#endif
#if MYNEWT_VAL(PWM_2)
rc = pwm_set_duty_cycle(pwm2, 0, pwm_val);
assert(rc == 0);
#endif
#if MYNEWT_VAL(PWM_3)
rc = pwm_set_duty_cycle(pwm3, 0, pwm_val);
assert(rc == 0);
#endif
#else
int i;
u32_t lightness;
u8_t max_lightness = 0x1f;
lightness = (u8_t) (percentage * max_lightness / 100);
for (i = 0; i < WS2812_NUM_LED; i++) {
neopixel[i] = (lightness | lightness << 8 | lightness << 16);
}
ws2812_write(neopixel);
#endif
}
static void update_light_state(void)
{
u16_t level = (u16_t)gen_level_state;
int percent = 100 * level / 0xffff;
if (gen_onoff_state == 0) {
percent = 0;
}
light_set_lightness((uint8_t) percent);
}
int light_model_gen_onoff_get(struct bt_mesh_model *model, u8_t *state)
{
*state = gen_onoff_state;
return 0;
}
int light_model_gen_onoff_set(struct bt_mesh_model *model, u8_t state)
{
gen_onoff_state = state;
update_light_state();
return 0;
}
int light_model_gen_level_get(struct bt_mesh_model *model, s16_t *level)
{
*level = gen_level_state;
return 0;
}
int light_model_gen_level_set(struct bt_mesh_model *model, s16_t level)
{
gen_level_state = level;
if ((u16_t)gen_level_state > 0x0000) {
gen_onoff_state = 1;
}
if ((u16_t)gen_level_state == 0x0000) {
gen_onoff_state = 0;
}
update_light_state();
return 0;
}
int light_model_light_lightness_get(struct bt_mesh_model *model, s16_t *lightness)
{
return light_model_gen_level_get(model, lightness);
}
int light_model_light_lightness_set(struct bt_mesh_model *model, s16_t lightness)
{
return light_model_gen_level_set(model, lightness);
}
#if (!MYNEWT_VAL(USE_NEOPIXEL))
struct pwm_dev_cfg dev_conf = {
.n_cycles = 0,
.int_prio = 3,
};
#if MYNEWT_VAL(PWM_0)
static struct pwm_chan_cfg led1_conf = {
.pin = LED_1,
.inverted = true,
};
#endif
#if MYNEWT_VAL(PWM_1)
static struct pwm_chan_cfg led2_conf = {
.pin = LED_2,
.inverted = true,
};
#endif
#if MYNEWT_VAL(PWM_2)
static struct pwm_chan_cfg led3_conf = {
.pin = LED_3,
.inverted = true,
};
#endif
#endif
#if MYNEWT_VAL(PWM_3)
static struct pwm_chan_cfg led4_conf = {
.pin = LED_4,
.inverted = true,
};
#endif
#if (!MYNEWT_VAL(USE_NEOPIXEL))
void init_pwm_dev(struct pwm_dev **pwm, char *dev_name, struct pwm_chan_cfg *chan_cfg)
{
int rc = 0;
*pwm = (struct pwm_dev *) os_dev_open(dev_name, 0, NULL);
assert(pwm);
rc = pwm_configure_device(*pwm, &dev_conf);
assert(rc == 0);
rc = pwm_configure_channel(*pwm, 0, chan_cfg);
assert(rc == 0);
rc = pwm_enable(*pwm);
assert(rc == 0);
}
int pwm_init(void)
{
#if MYNEWT_VAL(PWM_0)
init_pwm_dev(&pwm0, "pwm0", &led1_conf);
#endif
#if MYNEWT_VAL(PWM_1)
init_pwm_dev(&pwm1, "pwm1", &led2_conf);
#endif
#if MYNEWT_VAL(PWM_2)
init_pwm_dev(&pwm2, "pwm2", &led3_conf);
#endif
#if MYNEWT_VAL(PWM_3)
init_pwm_dev(&pwm3, "pwm3", &led4_conf);
#endif
if (!pwm0) {
return 0;
}
top_val = (uint16_t) pwm_get_top_value(pwm0);
update_light_state();
return 0;
}
#endif
#endif
int light_model_init(void)
{
#if MYNEWT_VAL(BLE_MESH_SHELL_MODELS)
int rc;
#if (!MYNEWT_VAL(USE_NEOPIXEL))
rc = pwm_init();
assert(rc == 0);
#else
rc = ws2812_init();
assert(rc == 0);
update_light_state();
#endif
return rc;
#else
return 0;
#endif
}

View file

@ -0,0 +1,37 @@
/*
* 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.
*
* Copyright (c) 2017 Intel Corporation
*
*/
#ifndef __BT_MESH_LIGHT_MODEL_H
#define __BT_MESH_LIGHT_MODEL_H
#include "syscfg/syscfg.h"
#include "mesh/mesh.h"
int light_model_gen_onoff_get(struct bt_mesh_model *model, u8_t *state);
int light_model_gen_onoff_set(struct bt_mesh_model *model, u8_t state);
int light_model_gen_level_get(struct bt_mesh_model *model, s16_t *level);
int light_model_gen_level_set(struct bt_mesh_model *model, s16_t level);
int light_model_light_lightness_get(struct bt_mesh_model *model, s16_t *lightness);
int light_model_light_lightness_set(struct bt_mesh_model *model, s16_t lightness);
int light_model_init(void);
#endif

View file

@ -0,0 +1,113 @@
/*
* 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 "sysinit/sysinit.h"
#include "os/os.h"
#include "mesh/mesh.h"
#include "console/console.h"
#include "bsp/bsp.h"
#include "shell/shell.h"
#include "host/ble_hs.h"
#include "mesh/glue.h"
#include "mesh/testing.h"
#include "mesh/model_srv.h"
#include "light_model.h"
static void model_bound_cb(u16_t addr, struct bt_mesh_model *model,
u16_t key_idx)
{
int rc;
console_printf("Model bound: remote addr 0x%04x key_idx 0x%04x model %p\n",
addr, key_idx, model);
if (model->id != BT_MESH_MODEL_ID_GEN_LEVEL_SRV) {
return;
}
/* Hack for demo purposes */
rc = bt_test_bind_app_key_to_model(model, key_idx,
BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV);
if (rc) {
console_printf("Failed to bind light lightness srv model to app_key");
} else {
console_printf("Successfuly bound light lightness srv model to app_key");
}
}
static struct bt_test_cb bt_test_cb = {
.mesh_model_bound = model_bound_cb,
};
static void
blemesh_on_reset(int reason)
{
BLE_HS_LOG(ERROR, "Resetting state; reason=%d\n", reason);
}
static void
blemesh_on_sync(void)
{
console_printf("Bluetooth initialized\n");
shell_register_default_module("mesh");
bt_test_cb_register(&bt_test_cb);
light_model_init();
bt_mesh_set_gen_onoff_srv_cb(light_model_gen_onoff_get,
light_model_gen_onoff_set);
bt_mesh_set_gen_level_srv_cb(light_model_gen_level_get,
light_model_gen_level_set);
bt_mesh_set_light_lightness_srv_cb(light_model_light_lightness_get,
light_model_light_lightness_set);
console_printf("Mesh initialized\n");
if (IS_ENABLED(CONFIG_SETTINGS)) {
settings_load();
}
if (bt_mesh_is_provisioned()) {
printk("Mesh network restored from flash\n");
}
/* Hack for demo purposes */
bt_test_shell_init();
}
int
main(void)
{
/* Initialize OS */
sysinit();
/* Initialize the NimBLE host configuration. */
ble_hs_cfg.reset_cb = blemesh_on_reset;
ble_hs_cfg.sync_cb = blemesh_on_sync;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
while (1) {
os_eventq_run(os_eventq_dflt_get());
}
return 0;
}

View file

@ -0,0 +1,138 @@
/**
* 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 "syscfg/syscfg.h"
#if (MYNEWT_VAL(USE_NEOPIXEL))
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include "sysinit/sysinit.h"
#include "os/os.h"
#include "bsp/bsp.h"
#include "pwm/pwm.h"
#include "nrfx.h"
#include "nrfx_pwm.h"
#include "ws2812.h"
#define BITS_PER_SEQ (24)
#define BIT0 (0x8000 | 6)
#define BIT1 (0x8000 | 11)
static const nrfx_pwm_t pwm = NRFX_PWM_INSTANCE(WS2812_PWM);
static const nrfx_pwm_config_t pwm_config = {
.output_pins = { WS2812_GPIO, NRFX_PWM_PIN_NOT_USED, NRFX_PWM_PIN_NOT_USED, NRFX_PWM_PIN_NOT_USED },
.irq_priority = 3,
.base_clock = NRF_PWM_CLK_16MHz,
.count_mode = NRF_PWM_MODE_UP,
.top_value = 20,
.load_mode = NRF_PWM_LOAD_COMMON,
.step_mode = NRF_PWM_STEP_AUTO,
};
static uint16_t pwm_seq_values[2][BITS_PER_SEQ];
static const nrf_pwm_sequence_t pwm_seq[2] = {
{
.values.p_raw = pwm_seq_values[0],
.length = BITS_PER_SEQ,
.repeats = 0,
.end_delay = 0,
}, {
.values.p_raw = pwm_seq_values[1],
.length = BITS_PER_SEQ,
.repeats = 0,
.end_delay = 0,
},
};
static uint32_t led_color[WS2812_NUM_LED];
static int led_idx;
static void
load_pixel(void)
{
uint16_t *seq_values;
uint32_t grb;
int i;
seq_values = pwm_seq_values[led_idx & 1];
grb = led_color[led_idx];
for (i = 0; i < BITS_PER_SEQ; i++) {
*seq_values = grb & 0x800000 ? BIT1 : BIT0;
grb <<= 1;
seq_values++;
}
led_idx++;
}
static void
pwm_handler_func(nrfx_pwm_evt_type_t event_type)
{
switch (event_type) {
case NRFX_PWM_EVT_END_SEQ0:
case NRFX_PWM_EVT_END_SEQ1:
load_pixel();
break;
default:
break;
}
}
int
ws2812_init(void)
{
nrfx_err_t err;
err = nrfx_pwm_init(&pwm, &pwm_config, pwm_handler_func);
return err != NRFX_SUCCESS;
}
int
ws2812_write(const uint32_t *rgb)
{
uint32_t grb;
int i;
for (i = 0; i < WS2812_NUM_LED; i++) {
grb = 0;
grb |= (rgb[i] & 0x00FF00) << 8;
grb |= (rgb[i] & 0xFF0000) >> 8;
grb |= (rgb[i] & 0x0000FF);
led_color[i] = grb;
}
led_idx = 0;
load_pixel();
load_pixel();
nrfx_pwm_complex_playback(&pwm, &pwm_seq[0], &pwm_seq[1], WS2812_NUM_LED,
NRFX_PWM_FLAG_SIGNAL_END_SEQ0 |
NRFX_PWM_FLAG_SIGNAL_END_SEQ1 |
NRFX_PWM_FLAG_STOP);
return 0;
}
#endif

View file

@ -0,0 +1,42 @@
/**
* 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.
*/
#ifndef __WS2812_H__
#define __WS2812_H__
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define WS2812_PWM 0
#define WS2812_GPIO 30
#define WS2812_NUM_LED 32
int ws2812_init(void);
int ws2812_write(const uint32_t *rgb);
#ifdef __cplusplus
}
#endif
#endif /* __WS2812_H__ */

View file

@ -0,0 +1,63 @@
# 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.
#
syscfg.defs:
USE_NEOPIXEL:
value: 0
syscfg.vals:
# Enable the shell task.
SHELL_TASK: 1
# Set log level to info (disable debug logging).
LOG_LEVEL: 1
# Default task settings
OS_MAIN_STACK_SIZE: 768
# SMP is not supported in this app, so disable smp-over-shell.
SHELL_MGMT: 0
MSYS_1_BLOCK_COUNT: 80
BLE_MESH_ADV_BUF_COUNT: 20
BLE_MESH_TX_SEG_MAX: 6
BLE_MESH: 1
BLE_MESH_SHELL: 1
BLE_MESH_PROV: 1
BLE_MESH_PB_ADV: 1
BLE_MESH_PB_GATT: 1
BLE_MESH_GATT_PROXY: 1
BLE_MESH_TESTING: 1
BLE_MESH_FRIEND: 0
BLE_MESH_CFG_CLI: 1
BLE_MESH_HEALTH_CLI: 0
BLE_MESH_SHELL_MODELS: 1
BLE_MESH_OOB_OUTPUT_ACTIONS: 0
BLE_MESH_SETTINGS: 0
CONFIG_NFFS: 0
USE_NEOPIXEL: 0
syscfg.vals.BLE_MESH_SHELL_MODELS:
PWM_0: 1
PWM_1: 1
PWM_2: 1
PWM_3: 1

View file

@ -0,0 +1,79 @@
### Bluetooth: Mesh OnOff Model
#### Overview
This is a simple application demonstrating a Bluetooth mesh multi-element node.
Each element has a mesh onoff client and server
model which controls one of the 4 sets of buttons and LEDs .
Prior to provisioning, an unprovisioned beacon is broadcast that contains
a UUID. Each button controls the state of its
corresponding LED and does not initiate any mesh activity.
The models for button 1 and LED 1 are in the node's root element.
The 3 remaining button/LED pairs are in elements 1 through 3.
Assuming the provisioner assigns 0x100 to the root element,
the secondary elements will appear at 0x101, 0x102 and 0x103.
After provisioning, the button clients must
be configured to publish and the LED servers to subscribe.
If a LED server is provided with a publish address, it will
also publish its status on an onoff state change.
If a button is pressed only once within a 1 second interval, it sends an
"on" message. If it is pressed more than once, it
sends an "off" message. The buttons are quite noisy and are debounced in
the button_pressed() interrupt handler. An interrupt within 250ms of the
previous is discarded. This might seem a little clumsy, but the alternative of
using one button for "on" and another for "off" would reduce the number
of onoff clients from 4 to 2.
#### Requirements
************
This sample has been tested on the Nordic nRF52840-PDK board, but would
likely also run on the nrf52_pca10040 board.
#### Building and Running
********************
Prior to provisioning, each button controls its corresponding LED as one
would expect with an actual switch.
Provisioning is done using the BlueZ meshctl utility. Below is an example that
binds button 2 and LED 1 to application key 1. It then configures button 2
to publish to group 0xc000 and LED 1 to subscribe to that group.
```
discover-unprovisioned on
provision <discovered UUID>
menu config
target 0100
appkey-add 1
bind 0 1 1000 # bind appkey 1 to LED server on element 0 (unicast 0100)
sub-add 0100 c000 1000 # add subscription to group address c000 to the LED server
bind 1 1 1001 # bind appkey 1 to button 2 on element 1 (unicast 0101)
pub-set 0101 c000 1 0 0 1001 # publish button 2 to group address c000
```
The meshctl utility maintains a persistent JSON database containing
the mesh configuration. As additional nodes (boards) are provisioned, it
assigns sequential unicast addresses based on the number of elements
supported by the node. This example supports 4 elements per node.
The first or root element of the node contains models for configuration,
health, and onoff. The secondary elements only
have models for onoff. The meshctl target for configuration must be the
root element's unicast address as it is the only one that has a
configuration server model.
If meshctl is gracefully exited, it can be restarted and reconnected to
network 0x0.
The meshctl utility also supports a onoff model client that can be used to
change the state of any LED that is bound to application key 0x1.
This is done by setting the target to the unicast address of the element
that has that LED's model and issuing the onoff command.
Group addresses are not supported.

View file

@ -0,0 +1,34 @@
# 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.
#
pkg.name: apps/blemesh_models_example_1
pkg.type: app
pkg.description: Sample application for BLE Mesh node with on/off model on nRF52840pdk
pkg.author: "Michał Narajowski <michal.narajowski@codecoup.pl>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
pkg.deps:
- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/sys/console/full"
- "@apache-mynewt-core/sys/log/full"
- "@apache-mynewt-core/sys/stats/full"
- nimble/controller
- nimble/host
- nimble/host/services/gap
- nimble/host/services/gatt
- nimble/transport/ram

View file

@ -0,0 +1,709 @@
/* main.c - Application main entry point */
/*
* 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.
*/
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* This application is specific to the Nordic nRF52840-PDK board.
*
* It supports the 4 buttons and 4 LEDs as mesh clients and servers.
*
* Prior to provisioning, a button inverts the state of the
* corresponding LED.
*
* Button and LED 1 are in the root node.
* The 3 remaining button/LED pairs are in element 1 through 3.
* Assuming the provisioner assigns 0x100 to the root node,
* the secondary elements will appear at 0x101, 0x102 and 0x103.
*
* It's anticipated that after provisioning, the button clients would
* be configured to publish and the LED servers to subscribe.
*
* If a LED server is provided with a publish address, it will
* also publish its status on a state change.
*
* Messages from a button to its corresponding LED are ignored as
* the LED's state has already been changed locally by the button client.
*
* The buttons are debounced at a nominal 250ms. That value can be
* changed as needed.
*
*/
#include "os/mynewt.h"
#include "bsp/bsp.h"
#include "console/console.h"
#include "hal/hal_gpio.h"
#include "host/ble_hs.h"
#include "mesh/glue.h"
#include "mesh/mesh.h"
#define CID_RUNTIME 0x05C3
/* Model Operation Codes */
#define BT_MESH_MODEL_OP_GEN_ONOFF_GET BT_MESH_MODEL_OP_2(0x82, 0x01)
#define BT_MESH_MODEL_OP_GEN_ONOFF_SET BT_MESH_MODEL_OP_2(0x82, 0x02)
#define BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x03)
#define BT_MESH_MODEL_OP_GEN_ONOFF_STATUS BT_MESH_MODEL_OP_2(0x82, 0x04)
static void gen_onoff_set(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf);
static void gen_onoff_set_unack(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf);
static void gen_onoff_get(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf);
static void gen_onoff_status(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf);
/*
* Server Configuration Declaration
*/
static struct bt_mesh_cfg_srv cfg_srv = {
.relay = BT_MESH_RELAY_DISABLED,
.beacon = BT_MESH_BEACON_ENABLED,
#if defined(CONFIG_BT_MESH_FRIEND)
.frnd = BT_MESH_FRIEND_ENABLED,
#else
.frnd = BT_MESH_FRIEND_NOT_SUPPORTED,
#endif
#if defined(CONFIG_BT_MESH_GATT_PROXY)
.gatt_proxy = BT_MESH_GATT_PROXY_ENABLED,
#else
.gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED,
#endif
.default_ttl = 7,
/* 3 transmissions with 20ms interval */
.net_transmit = BT_MESH_TRANSMIT(2, 20),
.relay_retransmit = BT_MESH_TRANSMIT(2, 20),
};
/*
* Client Configuration Declaration
*/
static struct bt_mesh_cfg_cli cfg_cli = {
};
/*
* Health Server Declaration
*/
static struct bt_mesh_health_srv health_srv = {
};
/*
* Publication Declarations
*
* The publication messages are initialized to the
* the size of the opcode + content
*
* For publication, the message must be in static or global as
* it is re-transmitted several times. This occurs
* after the function that called bt_mesh_model_publish() has
* exited and the stack is no longer valid.
*
* Note that the additional 4 bytes for the AppMIC is not needed
* because it is added to a stack variable at the time a
* transmission occurs.
*
*/
static struct bt_mesh_model_pub health_pub;
static struct bt_mesh_model_pub gen_onoff_pub_srv;
static struct bt_mesh_model_pub gen_onoff_pub_cli;
static struct bt_mesh_model_pub gen_onoff_pub_srv_s_0;
static struct bt_mesh_model_pub gen_onoff_pub_cli_s_0;
static struct bt_mesh_model_pub gen_onoff_pub_srv_s_1;
static struct bt_mesh_model_pub gen_onoff_pub_cli_s_1;
static struct bt_mesh_model_pub gen_onoff_pub_srv_s_2;
static struct bt_mesh_model_pub gen_onoff_pub_cli_s_2;
static struct os_mbuf *bt_mesh_pub_msg_health_pub;
static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_srv;
static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_cli;
static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_srv_s_0;
static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_cli_s_0;
static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_srv_s_1;
static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_cli_s_1;
static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_srv_s_2;
static struct os_mbuf *bt_mesh_pub_msg_gen_onoff_pub_cli_s_2;
void init_pub(void)
{
bt_mesh_pub_msg_health_pub = NET_BUF_SIMPLE(1 + 3 + 0);
bt_mesh_pub_msg_gen_onoff_pub_srv = NET_BUF_SIMPLE(2 + 2);
bt_mesh_pub_msg_gen_onoff_pub_cli = NET_BUF_SIMPLE(2 + 2);
bt_mesh_pub_msg_gen_onoff_pub_srv_s_0 = NET_BUF_SIMPLE(2 + 2);
bt_mesh_pub_msg_gen_onoff_pub_cli_s_0 = NET_BUF_SIMPLE(2 + 2);
bt_mesh_pub_msg_gen_onoff_pub_srv_s_1 = NET_BUF_SIMPLE(2 + 2);
bt_mesh_pub_msg_gen_onoff_pub_cli_s_1 = NET_BUF_SIMPLE(2 + 2);
bt_mesh_pub_msg_gen_onoff_pub_srv_s_2 = NET_BUF_SIMPLE(2 + 2);
bt_mesh_pub_msg_gen_onoff_pub_cli_s_2 = NET_BUF_SIMPLE(2 + 2);
health_pub.msg = bt_mesh_pub_msg_health_pub;
gen_onoff_pub_srv.msg = bt_mesh_pub_msg_gen_onoff_pub_srv;
gen_onoff_pub_cli.msg = bt_mesh_pub_msg_gen_onoff_pub_cli;
gen_onoff_pub_srv_s_0.msg = bt_mesh_pub_msg_gen_onoff_pub_srv_s_0;
gen_onoff_pub_cli_s_0.msg = bt_mesh_pub_msg_gen_onoff_pub_cli_s_0;
gen_onoff_pub_srv_s_1.msg = bt_mesh_pub_msg_gen_onoff_pub_srv_s_1;
gen_onoff_pub_cli_s_1.msg = bt_mesh_pub_msg_gen_onoff_pub_cli_s_1;
gen_onoff_pub_srv_s_2.msg = bt_mesh_pub_msg_gen_onoff_pub_srv_s_2;
gen_onoff_pub_cli_s_2.msg = bt_mesh_pub_msg_gen_onoff_pub_cli_s_2;
}
/*
* Models in an element must have unique op codes.
*
* The mesh stack dispatches a message to the first model in an element
* that is also bound to an app key and supports the op code in the
* received message.
*
*/
/*
* OnOff Model Server Op Dispatch Table
*
*/
static const struct bt_mesh_model_op gen_onoff_srv_op[] = {
{ BT_MESH_MODEL_OP_GEN_ONOFF_GET, 0, gen_onoff_get },
{ BT_MESH_MODEL_OP_GEN_ONOFF_SET, 2, gen_onoff_set },
{ BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK, 2, gen_onoff_set_unack },
BT_MESH_MODEL_OP_END,
};
/*
* OnOff Model Client Op Dispatch Table
*/
static const struct bt_mesh_model_op gen_onoff_cli_op[] = {
{ BT_MESH_MODEL_OP_GEN_ONOFF_STATUS, 1, gen_onoff_status },
BT_MESH_MODEL_OP_END,
};
struct onoff_state {
u8_t current;
u8_t previous;
u8_t led_gpio_pin;
};
/*
* Declare and Initialize Element Contexts
* Change to select different GPIO output pins
*/
static struct onoff_state onoff_state_arr[] = {
{ .led_gpio_pin = LED_1 },
{ .led_gpio_pin = LED_2 },
{ .led_gpio_pin = LED_3 },
{ .led_gpio_pin = LED_4 },
};
/*
*
* Element Model Declarations
*
* Element 0 Root Models
*/
static struct bt_mesh_model root_models[] = {
BT_MESH_MODEL_CFG_SRV(&cfg_srv),
BT_MESH_MODEL_CFG_CLI(&cfg_cli),
BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op,
&gen_onoff_pub_srv, &onoff_state_arr[0]),
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op,
&gen_onoff_pub_cli, &onoff_state_arr[0]),
};
/*
* Element 1 Models
*/
static struct bt_mesh_model secondary_0_models[] = {
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op,
&gen_onoff_pub_srv_s_0, &onoff_state_arr[1]),
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op,
&gen_onoff_pub_cli_s_0, &onoff_state_arr[1]),
};
/*
* Element 2 Models
*/
static struct bt_mesh_model secondary_1_models[] = {
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op,
&gen_onoff_pub_srv_s_1, &onoff_state_arr[2]),
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op,
&gen_onoff_pub_cli_s_1, &onoff_state_arr[2]),
};
/*
* Element 3 Models
*/
static struct bt_mesh_model secondary_2_models[] = {
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op,
&gen_onoff_pub_srv_s_2, &onoff_state_arr[3]),
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_CLI, gen_onoff_cli_op,
&gen_onoff_pub_cli_s_2, &onoff_state_arr[3]),
};
/*
* Button to Client Model Assignments
*/
struct bt_mesh_model *mod_cli_sw[] = {
&root_models[4],
&secondary_0_models[1],
&secondary_1_models[1],
&secondary_2_models[1],
};
/*
* LED to Server Model Assigmnents
*/
struct bt_mesh_model *mod_srv_sw[] = {
&root_models[3],
&secondary_0_models[0],
&secondary_1_models[0],
&secondary_2_models[0],
};
/*
* Root and Secondary Element Declarations
*/
static struct bt_mesh_elem elements[] = {
BT_MESH_ELEM(0, root_models, BT_MESH_MODEL_NONE),
BT_MESH_ELEM(0, secondary_0_models, BT_MESH_MODEL_NONE),
BT_MESH_ELEM(0, secondary_1_models, BT_MESH_MODEL_NONE),
BT_MESH_ELEM(0, secondary_2_models, BT_MESH_MODEL_NONE),
};
static const struct bt_mesh_comp comp = {
.cid = CID_RUNTIME,
.elem = elements,
.elem_count = ARRAY_SIZE(elements),
};
struct sw {
u8_t sw_num;
u8_t onoff_state;
struct os_callout button_work;
struct os_callout button_timer;
};
static u8_t button_press_cnt;
static struct sw sw;
static u8_t trans_id;
static u32_t time, last_time;
static u16_t primary_addr;
static u16_t primary_net_idx;
/*
* Generic OnOff Model Server Message Handlers
*
* Mesh Model Specification 3.1.1
*
*/
static void gen_onoff_get(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
struct os_mbuf *msg = NET_BUF_SIMPLE(2 + 1 + 4);
struct onoff_state *state = model->user_data;
BT_INFO("addr 0x%04x onoff 0x%02x",
bt_mesh_model_elem(model)->addr, state->current);
bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_GEN_ONOFF_STATUS);
net_buf_simple_add_u8(msg, state->current);
if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
BT_ERR("Unable to send On Off Status response");
}
os_mbuf_free_chain(msg);
}
static void gen_onoff_set_unack(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
struct os_mbuf *msg = model->pub->msg;
struct onoff_state *state = model->user_data;
int err;
state->current = net_buf_simple_pull_u8(buf);
BT_INFO("addr 0x%02x state 0x%02x",
bt_mesh_model_elem(model)->addr, state->current);
/* Pin set low turns on LED's on the nrf52840-pca10056 board */
hal_gpio_write(state->led_gpio_pin,
state->current ? 0 : 1);
/*
* If a server has a publish address, it is required to
* publish status on a state change
*
* See Mesh Profile Specification 3.7.6.1.2
*
* Only publish if there is an assigned address
*/
if (state->previous != state->current &&
model->pub->addr != BT_MESH_ADDR_UNASSIGNED) {
BT_INFO("publish last 0x%02x cur 0x%02x",
state->previous,
state->current);
state->previous = state->current;
bt_mesh_model_msg_init(msg,
BT_MESH_MODEL_OP_GEN_ONOFF_STATUS);
net_buf_simple_add_u8(msg, state->current);
err = bt_mesh_model_publish(model);
if (err) {
BT_ERR("bt_mesh_model_publish err %d", err);
}
}
}
static void gen_onoff_set(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
BT_INFO("");
gen_onoff_set_unack(model, ctx, buf);
gen_onoff_get(model, ctx, buf);
}
static void gen_onoff_status(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *buf)
{
u8_t state;
state = net_buf_simple_pull_u8(buf);
BT_INFO("Node 0x%04x OnOff status from 0x%04x with state 0x%02x",
bt_mesh_model_elem(model)->addr, ctx->addr, state);
}
static int output_number(bt_mesh_output_action_t action, u32_t number)
{
BT_INFO("OOB Number %u", number);
return 0;
}
static int output_string(const char *str)
{
BT_INFO("OOB String %s", str);
return 0;
}
static void prov_complete(u16_t net_idx, u16_t addr)
{
BT_INFO("provisioning complete for net_idx 0x%04x addr 0x%04x",
net_idx, addr);
primary_addr = addr;
primary_net_idx = net_idx;
}
static void prov_reset(void)
{
bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);
}
static u8_t dev_uuid[16] = MYNEWT_VAL(BLE_MESH_DEV_UUID);
#define BUTTON_DEBOUNCE_DELAY_MS 250
/*
* Map GPIO pins to button number
* Change to select different GPIO input pins
*/
static uint8_t pin_to_sw(int pin_pos)
{
switch (pin_pos) {
case BUTTON_1: return 0;
case BUTTON_2: return 1;
case BUTTON_3: return 2;
case BUTTON_4: return 3;
default:break;
}
BT_ERR("No match for GPIO pin 0x%08x", pin_pos);
return 0;
}
static void button_pressed(struct os_event *ev)
{
int pin_pos = (int ) ev->ev_arg;
/*
* One button press within a 1 second interval sends an on message
* More than one button press sends an off message
*/
time = k_uptime_get_32();
/* debounce the switch */
if (time < last_time + BUTTON_DEBOUNCE_DELAY_MS) {
last_time = time;
return;
}
if (button_press_cnt == 0) {
os_callout_reset(&sw.button_timer, os_time_ms_to_ticks32(K_SECONDS(1)));
}
BT_INFO("button_press_cnt 0x%02x", button_press_cnt);
button_press_cnt++;
/* The variable pin_pos is the pin position in the GPIO register,
* not the pin number. It's assumed that only one bit is set.
*/
sw.sw_num = pin_to_sw(pin_pos);
last_time = time;
}
/*
* Button Count Timer Worker
*/
static void button_cnt_timer(struct os_event *work)
{
struct sw *button_sw = work->ev_arg;
button_sw->onoff_state = button_press_cnt == 1 ? 1 : 0;
BT_INFO("button_press_cnt 0x%02x onoff_state 0x%02x",
button_press_cnt, button_sw->onoff_state);
button_press_cnt = 0;
os_callout_reset(&sw.button_work, 0);
}
/*
* Button Pressed Worker Task
*/
static void button_pressed_worker(struct os_event *work)
{
struct os_mbuf *msg = NET_BUF_SIMPLE(1);
struct bt_mesh_model *mod_cli, *mod_srv;
struct bt_mesh_model_pub *pub_cli, *pub_srv;
struct sw *sw = work->ev_arg;
u8_t sw_idx = sw->sw_num;
int err;
mod_cli = mod_cli_sw[sw_idx];
pub_cli = mod_cli->pub;
mod_srv = mod_srv_sw[sw_idx];
pub_srv = mod_srv->pub;
(void)pub_srv;
/* If unprovisioned, just call the set function.
* The intent is to have switch-like behavior
* prior to provisioning. Once provisioned,
* the button and its corresponding led are no longer
* associated and act independently. So, if a button is to
* control its associated led after provisioning, the button
* must be configured to either publish to the led's unicast
* address or a group to which the led is subscribed.
*/
if (primary_addr == BT_MESH_ADDR_UNASSIGNED) {
struct bt_mesh_msg_ctx ctx = {
.addr = sw_idx + primary_addr,
};
/* This is a dummy message sufficient
* for the led server
*/
net_buf_simple_add_u8(msg, sw->onoff_state);
gen_onoff_set_unack(mod_srv, &ctx, msg);
goto done;
}
if (pub_cli->addr == BT_MESH_ADDR_UNASSIGNED) {
goto done;
}
BT_INFO("publish to 0x%04x onoff 0x%04x sw_idx 0x%04x",
pub_cli->addr, sw->onoff_state, sw_idx);
bt_mesh_model_msg_init(pub_cli->msg,
BT_MESH_MODEL_OP_GEN_ONOFF_SET);
net_buf_simple_add_u8(pub_cli->msg, sw->onoff_state);
net_buf_simple_add_u8(pub_cli->msg, trans_id++);
err = bt_mesh_model_publish(mod_cli);
if (err) {
BT_ERR("bt_mesh_model_publish err %d", err);
}
done:
os_mbuf_free_chain(msg);
}
/* Disable OOB security for SILabs Android app */
static const struct bt_mesh_prov prov = {
.uuid = dev_uuid,
#if 1
.output_size = 6,
.output_actions = (BT_MESH_DISPLAY_NUMBER | BT_MESH_DISPLAY_STRING),
.output_number = output_number,
.output_string = output_string,
#else
.output_size = 0,
.output_actions = 0,
.output_number = 0,
#endif
.complete = prov_complete,
.reset = prov_reset,
};
void init_led(u8_t dev)
{
hal_gpio_init_out(onoff_state_arr[dev].led_gpio_pin, 1);
}
static struct os_event button_event;
static void
gpio_irq_handler(void *arg)
{
button_event.ev_arg = arg;
os_eventq_put(os_eventq_dflt_get(), &button_event);
}
void init_button(int button)
{
button_event.ev_cb = button_pressed;
hal_gpio_irq_init(button, gpio_irq_handler, (void *)button,
HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP);
hal_gpio_irq_enable(button);
}
static void
blemesh_on_reset(int reason)
{
BLE_HS_LOG(ERROR, "Resetting state; reason=%d\n", reason);
}
static void
blemesh_on_sync(void)
{
int err;
ble_addr_t addr;
console_printf("Bluetooth initialized\n");
/* Use NRPA */
err = ble_hs_id_gen_rnd(1, &addr);
assert(err == 0);
err = ble_hs_id_set_rnd(addr.val);
assert(err == 0);
err = bt_mesh_init(addr.type, &prov, &comp);
if (err) {
console_printf("Initializing mesh failed (err %d)\n", err);
return;
}
if (IS_ENABLED(CONFIG_SETTINGS)) {
settings_load();
}
if (bt_mesh_is_provisioned()) {
console_printf("Mesh network restored from flash\n");
}
bt_mesh_prov_enable(BT_MESH_PROV_GATT | BT_MESH_PROV_ADV);
console_printf("Mesh initialized\n");
}
int main(void)
{
#ifdef ARCH_sim
mcu_sim_parse_args(argc, argv);
#endif
/* Initialize OS */
sysinit();
BT_INFO("Initializing...");
/* Initialize the button debouncer */
last_time = k_uptime_get_32();
/* Initialize button worker task*/
os_callout_init(&sw.button_work, os_eventq_dflt_get(),
button_pressed_worker, &sw);
/* Initialize button count timer */
os_callout_init(&sw.button_timer, os_eventq_dflt_get(),
button_cnt_timer, &sw);
/* Initialize LED's */
init_led(0);
init_led(1);
init_led(2);
init_led(3);
init_button(BUTTON_1);
init_button(BUTTON_2);
init_button(BUTTON_3);
init_button(BUTTON_4);
init_pub();
/* Initialize the NimBLE host configuration. */
ble_hs_cfg.reset_cb = blemesh_on_reset;
ble_hs_cfg.sync_cb = blemesh_on_sync;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
while (1) {
os_eventq_run(os_eventq_dflt_get());
}
return 0;
}

View file

@ -0,0 +1,38 @@
# 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.
#
syscfg.vals:
# Set log level to info (disable debug logging).
LOG_LEVEL: 1
# Default task settings
OS_MAIN_STACK_SIZE: 768
# SMP is not supported in this app, so disable smp-over-shell.
SHELL_MGMT: 0
MSYS_1_BLOCK_COUNT: 48
BLE_MESH: 1
BLE_MESH_CFG_CLI: 1
BLE_MESH_DEV_UUID: "((uint8_t[16]){0xdd, 0xdd, 0})"
BLE_MESH_SETTINGS: 0
CONFIG_NFFS: 0
BLE_MESH_ADV_BUF_COUNT: 20
BLE_MESH_TX_SEG_MAX: 6

View file

@ -0,0 +1,101 @@
#### Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
##### Overview
********
This is a application demonstrating a Bluetooth mesh node in
which Root element has following models
- Generic OnOff Server
- Generic OnOff Client
- Generic Level Server
- Generic Level Client
- Generic Default Transition Time Server
- Generic Default Transition Time Client
- Generic Power OnOff Server
- Generic Power OnOff Setup Server
- Generic Power OnOff Client
- Light Lightness Server
- Light Lightness Setup Server
- Light Lightness Client
- Light CTL Server
- Light CTL Setup Server
- Light CTL Client
- Vendor Model
And Secondary element has following models
- Generic Level Server
- Generic Level Client
- Light CTL Temperature Server
Prior to provisioning, an unprovisioned beacon is broadcast that contains
a unique UUID. Each button controls the state of its
corresponding LED and does not initiate any mesh activity
##### Associations of Models with hardware
************************************
For the nRF52840-PDK board, these are the model associations:
* LED1 is associated with generic OnOff Server's state which is part of Root element
* LED2 is associated with Vendor Model which is part of Root element
* LED3 is associated with generic Level (ROOT) / Light Lightness Actual value
* LED4 is associated with generic Level (Secondary) / Light CTL Temperature value
* Button1 and Button2 are associated with gen. OnOff Client or Vendor Model which is part of Root element
* Button3 and Button4 are associated with gen. Level Client / Light Lightness Client / Light CTL Client which is part of Root element
States of Servers are bounded as per Bluetooth SIG Mesh Model Specification v1.0
After provisioning, the button clients must
be configured to publish and the LED servers to subscribe.
If a server is provided with a publish address, it will
also publish its relevant status.
##### Requirements
************
This sample has been tested on the Nordic nRF52840-PDK board, but would
likely also run on the nrf52_pca10040 board.
##### Running
************
Provisioning is done using the BlueZ meshctl utility. In this example, we'll use meshctl commands to bind:
- Button1, Button2, and LED1 to application key 1. It then configures Button1 and Button2
to publish to group 0xC000 and LED1 to subscribe to that group.
- Button3, Button4, and LED3 to application key 1. It then configures Button3 and Button4
to publish to group 0xC000 and LED3 to subscribe to that group.
```
discover-unprovisioned on
provision <discovered UUID>
menu config
target 0100
appkey-add 1
bind 0 1 1000
bind 0 1 1001
bind 0 1 1002
bind 0 1 1003
sub-add 0100 c000 1000
sub-add 0100 c000 1002
pub-set 0100 c000 1 0 5 1001
pub-set 0100 c000 1 0 5 1003
```
The meshctl utility maintains a persistent JSON database containing
the mesh configuration. As additional nodes (boards) are provisioned, it
assigns sequential unicast addresses based on the number of elements
supported by the node. This example supports 2 elements per node.
The meshctl target for configuration must be the root element's unicast
address as it is the only one that has a configuration server model. If
meshctl is gracefully exited, it can be restarted and reconnected to
network 0x0.
The meshctl utility also supports a onoff model client that can be used to
change the state of any LED that is bound to application key 0x1.
This is done by setting the target to the unicast address of the element
that has that LED's model and issuing the onoff command.
Group addresses are not supported.

View file

@ -0,0 +1,40 @@
# 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.
#
pkg.name: apps/blemesh_models_example_2
pkg.type: app
pkg.description: Sample application for BLE Mesh node with on/off, level, light and vendor models on nRF52840pdk
pkg.author: "Michał Narajowski <michal.narajowski@codecoup.pl>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
pkg.deps:
- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/sys/console/full"
- "@apache-mynewt-core/sys/log/full"
- "@apache-mynewt-core/sys/stats/full"
- "@apache-mynewt-core/encoding/base64"
- "@apache-mynewt-core/sys/config"
- nimble/controller
- nimble/host
- nimble/host/services/gap
- nimble/host/services/gatt
- nimble/transport/ram
pkg.lflags:
- -DFLOAT_SUPPORT
- -lm

View file

@ -0,0 +1,95 @@
/*
* 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "bsp/bsp.h"
#include "console/console.h"
#include "hal/hal_gpio.h"
#include "mesh/mesh.h"
#include "app_gpio.h"
#include "publisher.h"
int button_device[] = {
BUTTON_1,
BUTTON_2,
BUTTON_3,
BUTTON_4,
};
int led_device[] = {
LED_1,
LED_2,
LED_3,
LED_4,
};
static struct os_callout button_work;
static void button_pressed(struct os_event *ev)
{
os_callout_reset(&button_work, 0);
}
static struct os_event button_event;
static void gpio_irq_handler(void *arg)
{
button_event.ev_arg = arg;
os_eventq_put(os_eventq_dflt_get(), &button_event);
}
void app_gpio_init(void)
{
/* LEDs configiuratin & setting */
hal_gpio_init_out(led_device[0], 1);
hal_gpio_init_out(led_device[1], 1);
hal_gpio_init_out(led_device[2], 1);
hal_gpio_init_out(led_device[3], 1);
/* Buttons configiuratin & setting */
os_callout_init(&button_work, os_eventq_dflt_get(), publish, NULL);
button_event.ev_cb = button_pressed;
hal_gpio_irq_init(button_device[0], gpio_irq_handler, NULL,
HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP);
hal_gpio_irq_enable(button_device[0]);
hal_gpio_irq_init(button_device[1], gpio_irq_handler, NULL,
HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP);
hal_gpio_irq_enable(button_device[1]);
hal_gpio_irq_init(button_device[2], gpio_irq_handler, NULL,
HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP);
hal_gpio_irq_enable(button_device[2]);
hal_gpio_irq_init(button_device[3], gpio_irq_handler, NULL,
HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP);
hal_gpio_irq_enable(button_device[3]);
}

View file

@ -0,0 +1,36 @@
/*
* 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _APP_GPIO_H
#define _APP_GPIO_H
/* GPIO */
extern int button_device[];
extern int led_device[];
void app_gpio_init(void);
#endif

View file

@ -0,0 +1,116 @@
/*
* 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "console/console.h"
#include "common.h"
#include "ble_mesh.h"
#include "device_composition.h"
#define OOB_AUTH_ENABLE 1
#ifdef OOB_AUTH_ENABLE
static int output_number(bt_mesh_output_action_t action, u32_t number)
{
printk("OOB Number: %lu\n", number);
return 0;
}
static int output_string(const char *str)
{
printk("OOB String: %s\n", str);
return 0;
}
#endif
static void prov_complete(u16_t net_idx, u16_t addr)
{
printk("Local node provisioned, primary address 0x%04x\n", addr);
}
static void prov_reset(void)
{
bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);
}
static u8_t dev_uuid[16] = MYNEWT_VAL(BLE_MESH_DEV_UUID);
static const struct bt_mesh_prov prov = {
.uuid = dev_uuid,
#ifdef OOB_AUTH_ENABLE
.output_size = 6,
.output_actions = BT_MESH_DISPLAY_NUMBER | BT_MESH_DISPLAY_STRING,
.output_number = output_number,
.output_string = output_string,
#endif
.complete = prov_complete,
.reset = prov_reset,
};
void blemesh_on_reset(int reason)
{
BLE_HS_LOG(ERROR, "Resetting state; reason=%d\n", reason);
}
void blemesh_on_sync(void)
{
int err;
ble_addr_t addr;
console_printf("Bluetooth initialized\n");
/* Use NRPA */
err = ble_hs_id_gen_rnd(1, &addr);
assert(err == 0);
err = ble_hs_id_set_rnd(addr.val);
assert(err == 0);
err = bt_mesh_init(addr.type, &prov, &comp);
if (err) {
console_printf("Initializing mesh failed (err %d)\n", err);
return;
}
if (IS_ENABLED(CONFIG_SETTINGS)) {
settings_load();
}
if (bt_mesh_is_provisioned()) {
console_printf("Mesh network restored from flash\n");
}
bt_mesh_prov_enable(BT_MESH_PROV_GATT | BT_MESH_PROV_ADV);
console_printf("Mesh initialized\n");
bt_initialized();
}

View file

@ -0,0 +1,73 @@
/*
* 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _BLE_MESH_H
#define _BLE_MESH_H
#include "mesh/mesh.h"
#include "mesh/glue.h"
/* Model Operation Codes */
#define BT_MESH_MODEL_OP_GEN_ONOFF_GET BT_MESH_MODEL_OP_2(0x82, 0x01)
#define BT_MESH_MODEL_OP_GEN_ONOFF_SET BT_MESH_MODEL_OP_2(0x82, 0x02)
#define BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x03)
#define BT_MESH_MODEL_OP_GEN_ONOFF_STATUS BT_MESH_MODEL_OP_2(0x82, 0x04)
#define BT_MESH_MODEL_OP_GEN_LEVEL_GET BT_MESH_MODEL_OP_2(0x82, 0x05)
#define BT_MESH_MODEL_OP_GEN_LEVEL_SET BT_MESH_MODEL_OP_2(0x82, 0x06)
#define BT_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x07)
#define BT_MESH_MODEL_OP_GEN_LEVEL_STATUS BT_MESH_MODEL_OP_2(0x82, 0x08)
#define BT_MESH_MODEL_OP_GEN_DELTA_SET BT_MESH_MODEL_OP_2(0x82, 0x09)
#define BT_MESH_MODEL_OP_GEN_DELTA_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x0A)
#define BT_MESH_MODEL_OP_GEN_MOVE_SET BT_MESH_MODEL_OP_2(0x82, 0x0B)
#define BT_MESH_MODEL_OP_GEN_MOVE_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x0C)
#define BT_MESH_MODEL_GEN_DEF_TRANS_TIME_STATUS BT_MESH_MODEL_OP_2(0x82, 0x10)
#define BT_MESH_MODEL_GEN_ONPOWERUP_STATUS BT_MESH_MODEL_OP_2(0x82, 0x12)
#define BT_MESH_MODEL_LIGHT_LIGHTNESS_STATUS BT_MESH_MODEL_OP_2(0x82, 0x4E)
#define BT_MESH_MODEL_LIGHT_LIGHTNESS_LINEAR_STATUS \
BT_MESH_MODEL_OP_2(0x82, 0x52)
#define BT_MESH_MODEL_LIGHT_LIGHTNESS_LAST_STATUS \
BT_MESH_MODEL_OP_2(0x82, 0x54)
#define BT_MESH_MODEL_LIGHT_LIGHTNESS_DEFAULT_STATUS \
BT_MESH_MODEL_OP_2(0x82, 0x56)
#define BT_MESH_MODEL_LIGHT_LIGHTNESS_RANGE_STATUS \
BT_MESH_MODEL_OP_2(0x82, 0x58)
#define BT_MESH_MODEL_LIGHT_CTL_STATUS BT_MESH_MODEL_OP_2(0x82, 0x60)
#define BT_MESH_MODEL_LIGHT_CTL_TEMP_RANGE_STATUS \
BT_MESH_MODEL_OP_2(0x82, 0x63)
#define BT_MESH_MODEL_LIGHT_CTL_TEMP_STATUS BT_MESH_MODEL_OP_2(0x82, 0x66)
#define BT_MESH_MODEL_LIGHT_CTL_DEFAULT_STATUS BT_MESH_MODEL_OP_2(0x82, 0x68)
void blemesh_on_reset(int reason);
void blemesh_on_sync(void);
void init_pub(void);
#endif

View file

@ -0,0 +1,33 @@
/*
* 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _COMMON_H
#define _COMMON_H
void update_light_state(void);
void bt_initialized(void);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,177 @@
/*
* 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _DEVICE_COMPOSITION_H
#define _DEVICE_COMPOSITION_H
#define CID_RUNTIME 0x05C3
#define STATE_OFF 0x00
#define STATE_ON 0x01
#define STATE_DEFAULT 0x01
#define STATE_RESTORE 0x02
/* Following 4 values are as per Mesh Model specification */
#define LIGHTNESS_MIN 0x0001
#define LIGHTNESS_MAX 0xFFFF
#define TEMP_MIN 0x0320
#define TEMP_MAX 0x4E20
/* Refer 7.2 of Mesh Model Specification */
#define RANGE_SUCCESSFULLY_UPDATED 0x00
#define CANNOT_SET_RANGE_MIN 0x01
#define CANNOT_SET_RANGE_MAX 0x02
struct generic_onoff_state {
u8_t onoff;
u8_t target_onoff;
u8_t last_tid;
u16_t last_src_addr;
u16_t last_dst_addr;
s64_t last_msg_timestamp;
s32_t tt_delta;
struct transition *transition;
};
struct generic_level_state {
s16_t level;
s16_t target_level;
s16_t last_level;
s32_t last_delta;
u8_t last_tid;
u16_t last_src_addr;
u16_t last_dst_addr;
s64_t last_msg_timestamp;
s32_t tt_delta;
struct transition *transition;
};
struct generic_onpowerup_state {
u8_t onpowerup;
};
struct gen_def_trans_time_state {
u8_t tt;
};
struct vendor_state {
int current;
u32_t response;
u8_t last_tid;
u16_t last_src_addr;
u16_t last_dst_addr;
s64_t last_msg_timestamp;
};
struct light_lightness_state {
u16_t linear;
u16_t target_linear;
u16_t actual;
u16_t target_actual;
u16_t last;
u16_t def;
u8_t status_code;
u16_t light_range_min;
u16_t light_range_max;
u32_t lightness_range;
u8_t last_tid;
u16_t last_src_addr;
u16_t last_dst_addr;
s64_t last_msg_timestamp;
s32_t tt_delta_actual;
s32_t tt_delta_linear;
struct transition *transition;
};
struct light_ctl_state {
u16_t lightness;
u16_t target_lightness;
u16_t temp;
u16_t target_temp;
s16_t delta_uv;
s16_t target_delta_uv;
u8_t status_code;
u16_t temp_range_min;
u16_t temp_range_max;
u32_t temperature_range;
u16_t lightness_def;
u16_t temp_def;
u32_t lightness_temp_def;
s16_t delta_uv_def;
u32_t lightness_temp_last;
u8_t last_tid;
u16_t last_src_addr;
u16_t last_dst_addr;
s64_t last_msg_timestamp;
s32_t tt_delta_lightness;
s32_t tt_delta_temp;
s32_t tt_delta_duv;
struct transition *transition;
};
extern struct generic_onoff_state gen_onoff_srv_root_user_data;
extern struct generic_level_state gen_level_srv_root_user_data;
extern struct gen_def_trans_time_state gen_def_trans_time_srv_user_data;
extern struct generic_onpowerup_state gen_power_onoff_srv_user_data;
extern struct light_lightness_state light_lightness_srv_user_data;
extern struct light_ctl_state light_ctl_srv_user_data;
extern struct generic_level_state gen_level_srv_s0_user_data;
extern struct bt_mesh_model root_models[];
extern struct bt_mesh_model vnd_models[];
extern struct bt_mesh_model s0_models[];
extern const struct bt_mesh_comp comp;
void gen_onoff_publish(struct bt_mesh_model *model);
void gen_level_publish(struct bt_mesh_model *model);
void light_lightness_publish(struct bt_mesh_model *model);
void light_lightness_linear_publish(struct bt_mesh_model *model);
void light_ctl_publish(struct bt_mesh_model *model);
void light_ctl_temp_publish(struct bt_mesh_model *model);
#endif

View file

@ -0,0 +1,250 @@
/*
* 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "console/console.h"
#include "hal/hal_gpio.h"
#include "mesh/mesh.h"
#include "app_gpio.h"
#include "storage.h"
#include "ble_mesh.h"
#include "device_composition.h"
#include "no_transition_work_handler.h"
#include "publisher.h"
#include "state_binding.h"
#include "transition.h"
static bool reset;
static void light_default_var_init(void)
{
gen_def_trans_time_srv_user_data.tt = 0x00;
gen_power_onoff_srv_user_data.onpowerup = STATE_DEFAULT;
light_lightness_srv_user_data.light_range_min = LIGHTNESS_MIN;
light_lightness_srv_user_data.light_range_max = LIGHTNESS_MAX;
light_lightness_srv_user_data.last = LIGHTNESS_MAX;
light_lightness_srv_user_data.def = LIGHTNESS_MAX;
/* Following 2 values are as per specification */
light_ctl_srv_user_data.temp_range_min = TEMP_MIN;
light_ctl_srv_user_data.temp_range_max = TEMP_MAX;
light_ctl_srv_user_data.temp_def = TEMP_MIN;
light_ctl_srv_user_data.lightness_temp_last =
(u32_t) ((LIGHTNESS_MAX << 16) | TEMP_MIN);
}
static void light_default_status_init(void)
{
u16_t lightness;
lightness = (u16_t) (light_ctl_srv_user_data.lightness_temp_last >> 16);
if (lightness) {
gen_onoff_srv_root_user_data.onoff = STATE_ON;
} else {
gen_onoff_srv_root_user_data.onoff = STATE_OFF;
}
/* Retrieve Default Lightness & Temperature Values */
if (light_ctl_srv_user_data.lightness_temp_def) {
light_ctl_srv_user_data.lightness_def = (u16_t)
(light_ctl_srv_user_data.lightness_temp_def >> 16);
light_ctl_srv_user_data.temp_def = (u16_t)
(light_ctl_srv_user_data.lightness_temp_def);
}
light_lightness_srv_user_data.def =
light_ctl_srv_user_data.lightness_def;
light_ctl_srv_user_data.temp = light_ctl_srv_user_data.temp_def;
/* Retrieve Range of Lightness & Temperature */
if (light_lightness_srv_user_data.lightness_range) {
light_lightness_srv_user_data.light_range_max = (u16_t)
(light_lightness_srv_user_data.lightness_range >> 16);
light_lightness_srv_user_data.light_range_min = (u16_t)
(light_lightness_srv_user_data.lightness_range);
}
if (light_ctl_srv_user_data.temperature_range) {
light_ctl_srv_user_data.temp_range_max = (u16_t)
(light_ctl_srv_user_data.temperature_range >> 16);
light_ctl_srv_user_data.temp_range_min = (u16_t)
(light_ctl_srv_user_data.temperature_range);
}
switch (gen_power_onoff_srv_user_data.onpowerup) {
case STATE_OFF:
gen_onoff_srv_root_user_data.onoff = STATE_OFF;
state_binding(ONOFF, ONOFF_TEMP);
break;
case STATE_DEFAULT:
gen_onoff_srv_root_user_data.onoff = STATE_ON;
state_binding(ONOFF, ONOFF_TEMP);
break;
case STATE_RESTORE:
light_lightness_srv_user_data.last = (u16_t)
(light_ctl_srv_user_data.lightness_temp_last >> 16);
light_ctl_srv_user_data.temp =
(u16_t) (light_ctl_srv_user_data.lightness_temp_last);
state_binding(ONPOWERUP, ONOFF_TEMP);
break;
}
default_tt = gen_def_trans_time_srv_user_data.tt;
}
void update_light_state(void)
{
u8_t power, color;
power = 100 * ((float) lightness / 65535);
color = 100 * ((float) (temperature + 32768) / 65535);
printk("power-> %d, color-> %d\n", power, color);
if (lightness) {
/* LED1 On */
hal_gpio_write(led_device[0], 0);
} else {
/* LED1 Off */
hal_gpio_write(led_device[0], 1);
}
if (power < 50) {
/* LED3 On */
hal_gpio_write(led_device[2], 0);
} else {
/* LED3 Off */
hal_gpio_write(led_device[2], 1);
}
if (color < 50) {
/* LED4 On */
hal_gpio_write(led_device[3], 0);
} else {
/* LED4 Off */
hal_gpio_write(led_device[3], 1);
}
if (*ptr_counter == 0 || reset == false) {
reset = true;
os_callout_reset(&no_transition_work, 0);
}
}
static void short_time_multireset_bt_mesh_unprovisioning(void)
{
if (reset_counter >= 4) {
reset_counter = 0;
printk("BT Mesh reset\n");
bt_mesh_reset();
} else {
printk("Reset Counter -> %d\n", reset_counter);
reset_counter++;
}
save_on_flash(RESET_COUNTER);
}
static void reset_counter_timer_handler(struct os_event *dummy)
{
reset_counter = 0;
save_on_flash(RESET_COUNTER);
printk("Reset Counter set to Zero\n");
}
struct os_callout reset_counter_timer;
static void init_timers(void)
{
os_callout_init(&reset_counter_timer, os_eventq_dflt_get(),
reset_counter_timer_handler, NULL);
os_callout_reset(&reset_counter_timer,
os_time_ms_to_ticks32(K_MSEC(7000)));
no_transition_work_init();
}
void bt_initialized(void)
{
light_default_status_init();
update_light_state();
randomize_publishers_TID();
short_time_multireset_bt_mesh_unprovisioning();
}
int main(void)
{
#ifdef ARCH_sim
mcu_sim_parse_args(argc, argv);
#endif
/* Initialize OS */
sysinit();
light_default_var_init();
app_gpio_init();
init_timers();
transition_timers_init();
init_pub();
ps_settings_init();
printk("Initializing...\n");
/* Initialize the NimBLE host configuration. */
ble_hs_cfg.reset_cb = blemesh_on_reset;
ble_hs_cfg.sync_cb = blemesh_on_sync;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
while (1) {
os_eventq_run(os_eventq_dflt_get());
}
return 0;
}

View file

@ -0,0 +1,89 @@
/*
* 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "ble_mesh.h"
#include "device_composition.h"
#include "storage.h"
static void unsolicitedly_publish_states_work_handler(struct os_event *work)
{
gen_onoff_publish(&root_models[2]);
gen_level_publish(&root_models[4]);
light_lightness_publish(&root_models[11]);
light_lightness_linear_publish(&root_models[11]);
light_ctl_publish(&root_models[14]);
gen_level_publish(&s0_models[0]);
light_ctl_temp_publish(&s0_models[2]);
}
struct os_callout unsolicitedly_publish_states_work;
static void unsolicitedly_publish_states_timer_handler(struct os_event *dummy)
{
os_callout_reset(&unsolicitedly_publish_states_work, 0);
}
struct os_callout unsolicitedly_publish_states_timer;
static void save_lightness_temp_last_values_timer_handler(struct os_event *dummy)
{
save_on_flash(LIGHTNESS_TEMP_LAST_STATE);
}
struct os_callout save_lightness_temp_last_values_timer;
static void no_transition_work_handler(struct os_event *work)
{
os_callout_reset(&unsolicitedly_publish_states_timer,
os_time_ms_to_ticks32(K_MSEC(5000)));
/* If Lightness & Temperature values remains stable for
* 10 Seconds then & then only get stored on SoC flash.
*/
if (gen_power_onoff_srv_user_data.onpowerup == STATE_RESTORE) {
os_callout_reset(&save_lightness_temp_last_values_timer,
os_time_ms_to_ticks32(
K_MSEC(10000)));
}
}
struct os_callout no_transition_work;
void no_transition_work_init(void)
{
os_callout_init(&no_transition_work, os_eventq_dflt_get(),
no_transition_work_handler, NULL);
os_callout_init(&save_lightness_temp_last_values_timer,
os_eventq_dflt_get(),
save_lightness_temp_last_values_timer_handler,
NULL);
os_callout_init(&unsolicitedly_publish_states_work, os_eventq_dflt_get(),
unsolicitedly_publish_states_work_handler, NULL);
os_callout_init(&unsolicitedly_publish_states_timer, os_eventq_dflt_get(),
unsolicitedly_publish_states_timer_handler, NULL);
}

View file

@ -0,0 +1,34 @@
/*
* 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _NO_TRANSITION_WORK_HANDLER_H
#define _NO_TRANSITION_WORK_HANDLER_H
extern struct os_callout no_transition_work;
void no_transition_work_init(void);
#endif

View file

@ -0,0 +1,266 @@
/*
* 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "console/console.h"
#include "hal/hal_gpio.h"
#include "app_gpio.h"
#include "ble_mesh.h"
#include "device_composition.h"
#include "publisher.h"
#define ONOFF
#define GENERIC_LEVEL
/* #define LIGHT_CTL */
/* #define LIGHT_CTL_TEMP */
static bool is_randomization_of_TIDs_done;
#if (defined(ONOFF) || defined(ONOFF_TT))
static u8_t tid_onoff;
#elif defined(VND_MODEL_TEST)
static u8_t tid_vnd;
#endif
static u8_t tid_level;
void randomize_publishers_TID(void)
{
#if (defined(ONOFF) || defined(ONOFF_TT))
bt_rand(&tid_onoff, sizeof(tid_onoff));
#elif defined(VND_MODEL_TEST)
bt_rand(&tid_vnd, sizeof(tid_vnd));
#endif
bt_rand(&tid_level, sizeof(tid_level));
is_randomization_of_TIDs_done = true;
}
static u32_t button_read(int button)
{
return (uint32_t) hal_gpio_read(button);
}
void publish(struct os_event *work)
{
int err = 0;
if (is_randomization_of_TIDs_done == false) {
return;
}
if (button_read(button_device[0]) == 0) {
#if defined(ONOFF)
bt_mesh_model_msg_init(root_models[3].pub->msg,
BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK);
net_buf_simple_add_u8(root_models[3].pub->msg, 0x01);
net_buf_simple_add_u8(root_models[3].pub->msg, tid_onoff++);
err = bt_mesh_model_publish(&root_models[3]);
#elif defined(ONOFF_TT)
bt_mesh_model_msg_init(root_models[3].pub->msg,
BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK);
net_buf_simple_add_u8(root_models[3].pub->msg, 0x01);
net_buf_simple_add_u8(root_models[3].pub->msg, tid_onoff++);
net_buf_simple_add_u8(root_models[3].pub->msg, 0x45);
net_buf_simple_add_u8(root_models[3].pub->msg, 0x28);
err = bt_mesh_model_publish(&root_models[3]);
#elif defined(VND_MODEL_TEST)
bt_mesh_model_msg_init(vnd_models[0].pub->msg,
BT_MESH_MODEL_OP_3(0x03, CID_RUNTIME));
net_buf_simple_add_le16(vnd_models[0].pub->msg, 0x0001);
net_buf_simple_add_u8(vnd_models[0].pub->msg, tid_vnd++);
err = bt_mesh_model_publish(&vnd_models[0]);
#endif
} else if (button_read(button_device[1]) == 0) {
#if defined(ONOFF)
bt_mesh_model_msg_init(root_models[3].pub->msg,
BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK);
net_buf_simple_add_u8(root_models[3].pub->msg, 0x00);
net_buf_simple_add_u8(root_models[3].pub->msg, tid_onoff++);
err = bt_mesh_model_publish(&root_models[3]);
#elif defined(ONOFF_TT)
bt_mesh_model_msg_init(root_models[3].pub->msg,
BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK);
net_buf_simple_add_u8(root_models[3].pub->msg, 0x00);
net_buf_simple_add_u8(root_models[3].pub->msg, tid_onoff++);
net_buf_simple_add_u8(root_models[3].pub->msg, 0x45);
net_buf_simple_add_u8(root_models[3].pub->msg, 0x28);
err = bt_mesh_model_publish(&root_models[3]);
#elif defined(VND_MODEL_TEST)
bt_mesh_model_msg_init(vnd_models[0].pub->msg,
BT_MESH_MODEL_OP_3(0x03, CID_RUNTIME));
net_buf_simple_add_le16(vnd_models[0].pub->msg, 0x0000);
net_buf_simple_add_u8(vnd_models[0].pub->msg, tid_vnd++);
err = bt_mesh_model_publish(&vnd_models[0]);
#endif
} else if (button_read(button_device[2]) == 0) {
#if defined(GENERIC_LEVEL)
bt_mesh_model_msg_init(root_models[5].pub->msg,
BT_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK);
net_buf_simple_add_le16(root_models[5].pub->msg, LEVEL_S25);
net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++);
err = bt_mesh_model_publish(&root_models[5]);
#elif defined(ONOFF_GET)
bt_mesh_model_msg_init(root_models[3].pub->msg,
BT_MESH_MODEL_OP_GEN_ONOFF_GET);
err = bt_mesh_model_publish(&root_models[3]);
#elif defined(GENERIC_DELTA_LEVEL)
bt_mesh_model_msg_init(root_models[5].pub->msg,
BT_MESH_MODEL_OP_GEN_DELTA_SET_UNACK);
net_buf_simple_add_le32(root_models[5].pub->msg, 100);
net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++);
err = bt_mesh_model_publish(&root_models[5]);
#elif defined(GENERIC_MOVE_LEVEL_TT)
bt_mesh_model_msg_init(root_models[5].pub->msg,
BT_MESH_MODEL_OP_GEN_MOVE_SET_UNACK);
net_buf_simple_add_le16(root_models[5].pub->msg, 13100);
net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++);
net_buf_simple_add_u8(root_models[5].pub->msg, 0x45);
net_buf_simple_add_u8(root_models[5].pub->msg, 0x00);
err = bt_mesh_model_publish(&root_models[5]);
#elif defined(LIGHT_LIGHTNESS_TT)
bt_mesh_model_msg_init(root_models[13].pub->msg,
BT_MESH_MODEL_OP_2(0x82, 0x4D));
net_buf_simple_add_le16(root_models[13].pub->msg, LEVEL_U25);
net_buf_simple_add_u8(root_models[13].pub->msg, tid_level++);
net_buf_simple_add_u8(root_models[13].pub->msg, 0x45);
net_buf_simple_add_u8(root_models[13].pub->msg, 0x28);
err = bt_mesh_model_publish(&root_models[13]);
#elif defined(LIGHT_CTL)
bt_mesh_model_msg_init(root_models[16].pub->msg,
BT_MESH_MODEL_OP_2(0x82, 0x5F));
/* Lightness */
net_buf_simple_add_le16(root_models[16].pub->msg, LEVEL_U25);
/* Temperature (value should be from 0x0320 to 0x4E20 */
/* This is as per 6.1.3.1 in Mesh Model Specification */
net_buf_simple_add_le16(root_models[16].pub->msg, 0x0320);
/* Delta UV */
net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000);
net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++);
err = bt_mesh_model_publish(&root_models[16]);
#elif defined(LIGHT_CTL_TT)
bt_mesh_model_msg_init(root_models[16].pub->msg,
BT_MESH_MODEL_OP_2(0x82, 0x5F));
/* Lightness */
net_buf_simple_add_le16(root_models[16].pub->msg, LEVEL_U25);
/* Temperature (value should be from 0x0320 to 0x4E20 */
/* This is as per 6.1.3.1 in Mesh Model Specification */
net_buf_simple_add_le16(root_models[16].pub->msg, 0x0320);
/* Delta UV */
net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000);
net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++);
net_buf_simple_add_u8(root_models[16].pub->msg, 0x45);
net_buf_simple_add_u8(root_models[16].pub->msg, 0x00);
err = bt_mesh_model_publish(&root_models[16]);
#elif defined(LIGHT_CTL_TEMP)
bt_mesh_model_msg_init(root_models[16].pub->msg,
BT_MESH_MODEL_OP_2(0x82, 0x65));
/* Temperature (value should be from 0x0320 to 0x4E20 */
/* This is as per 6.1.3.1 in Mesh Model Specification */
net_buf_simple_add_le16(root_models[16].pub->msg, 0x0320);
/* Delta UV */
net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000);
net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++);
err = bt_mesh_model_publish(&root_models[16]);
#endif
} else if (button_read(button_device[3]) == 0) {
#if defined(GENERIC_LEVEL)
bt_mesh_model_msg_init(root_models[5].pub->msg,
BT_MESH_MODEL_OP_GEN_LEVEL_SET_UNACK);
net_buf_simple_add_le16(root_models[5].pub->msg, LEVEL_S100);
net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++);
err = bt_mesh_model_publish(&root_models[5]);
#elif defined(GENERIC_DELTA_LEVEL)
bt_mesh_model_msg_init(root_models[5].pub->msg,
BT_MESH_MODEL_OP_GEN_DELTA_SET_UNACK);
net_buf_simple_add_le32(root_models[5].pub->msg, -100);
net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++);
err = bt_mesh_model_publish(&root_models[5]);
#elif defined(GENERIC_MOVE_LEVEL_TT)
bt_mesh_model_msg_init(root_models[5].pub->msg,
BT_MESH_MODEL_OP_GEN_MOVE_SET_UNACK);
net_buf_simple_add_le16(root_models[5].pub->msg, -13100);
net_buf_simple_add_u8(root_models[5].pub->msg, tid_level++);
net_buf_simple_add_u8(root_models[5].pub->msg, 0x45);
net_buf_simple_add_u8(root_models[5].pub->msg, 0x00);
err = bt_mesh_model_publish(&root_models[5]);
#elif defined(LIGHT_LIGHTNESS_TT)
bt_mesh_model_msg_init(root_models[13].pub->msg,
BT_MESH_MODEL_OP_2(0x82, 0x4D));
net_buf_simple_add_le16(root_models[13].pub->msg, LEVEL_U100);
net_buf_simple_add_u8(root_models[13].pub->msg, tid_level++);
net_buf_simple_add_u8(root_models[13].pub->msg, 0x45);
net_buf_simple_add_u8(root_models[13].pub->msg, 0x28);
err = bt_mesh_model_publish(&root_models[13]);
#elif defined(LIGHT_CTL)
bt_mesh_model_msg_init(root_models[16].pub->msg,
BT_MESH_MODEL_OP_2(0x82, 0x5F));
/* Lightness */
net_buf_simple_add_le16(root_models[16].pub->msg, LEVEL_U100);
/* Temperature (value should be from 0x0320 to 0x4E20 */
/* This is as per 6.1.3.1 in Mesh Model Specification */
net_buf_simple_add_le16(root_models[16].pub->msg, 0x4E20);
/* Delta UV */
net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000);
net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++);
err = bt_mesh_model_publish(&root_models[16]);
#elif defined(LIGHT_CTL_TT)
bt_mesh_model_msg_init(root_models[16].pub->msg,
BT_MESH_MODEL_OP_2(0x82, 0x5F));
/* Lightness */
net_buf_simple_add_le16(root_models[16].pub->msg, LEVEL_U100);
/* Temperature (value should be from 0x0320 to 0x4E20 */
/* This is as per 6.1.3.1 in Mesh Model Specification */
net_buf_simple_add_le16(root_models[16].pub->msg, 0x4E20);
/* Delta UV */
net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000);
net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++);
net_buf_simple_add_u8(root_models[16].pub->msg, 0x45);
net_buf_simple_add_u8(root_models[16].pub->msg, 0x00);
err = bt_mesh_model_publish(&root_models[16]);
#elif defined(LIGHT_CTL_TEMP)
bt_mesh_model_msg_init(root_models[16].pub->msg,
BT_MESH_MODEL_OP_2(0x82, 0x65));
/* Temperature (value should be from 0x0320 to 0x4E20 */
/* This is as per 6.1.3.1 in Mesh Model Specification */
net_buf_simple_add_le16(root_models[16].pub->msg, 0x4E20);
/* Delta UV */
net_buf_simple_add_le16(root_models[16].pub->msg, 0x0000);
net_buf_simple_add_u8(root_models[16].pub->msg, tid_level++);
err = bt_mesh_model_publish(&root_models[16]);
#endif
}
if (err) {
printk("bt_mesh_model_publish: err: %d\n", err);
}
}

View file

@ -0,0 +1,46 @@
/*
* 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _PUBLISHER_H
#define _PUBLISHER_H
/* Others */
#define LEVEL_S0 -32768
#define LEVEL_S25 -16384
#define LEVEL_S50 0
#define LEVEL_S75 16384
#define LEVEL_S100 32767
#define LEVEL_U0 0
#define LEVEL_U25 16384
#define LEVEL_U50 32768
#define LEVEL_U75 49152
#define LEVEL_U100 65535
void randomize_publishers_TID(void);
void publish(struct os_event *work);
#endif

View file

@ -0,0 +1,308 @@
/*
* 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <math.h>
#include "ble_mesh.h"
#include "device_composition.h"
#include "state_binding.h"
#include "transition.h"
u16_t lightness, target_lightness;
s16_t temperature, target_temperature;
static s32_t ceiling(float num)
{
s32_t inum;
inum = (s32_t) num;
if (num == (float) inum) {
return inum;
}
return inum + 1;
}
u16_t actual_to_linear(u16_t val)
{
float tmp;
tmp = ((float) val / 65535);
return (u16_t) ceiling(65535 * tmp * tmp);
}
u16_t linear_to_actual(u16_t val)
{
return (u16_t) (65535 * sqrt(((float) val / 65535)));
}
static void constrain_lightness(u16_t var)
{
if (var > 0 && var < light_lightness_srv_user_data.light_range_min) {
var = light_lightness_srv_user_data.light_range_min;
} else if (var > light_lightness_srv_user_data.light_range_max) {
var = light_lightness_srv_user_data.light_range_max;
}
lightness = var;
}
static void constrain_lightness2(u16_t var)
{
/* This is as per Mesh Model Specification 3.3.2.2.3 */
if (var > 0 && var < light_lightness_srv_user_data.light_range_min) {
if (gen_level_srv_root_user_data.last_delta < 0) {
var = 0U;
} else {
var = light_lightness_srv_user_data.light_range_min;
}
} else if (var > light_lightness_srv_user_data.light_range_max) {
var = light_lightness_srv_user_data.light_range_max;
}
lightness = var;
}
static void constrain_target_lightness(u16_t var)
{
if (var > 0 &&
var < light_lightness_srv_user_data.light_range_min) {
var = light_lightness_srv_user_data.light_range_min;
} else if (var > light_lightness_srv_user_data.light_range_max) {
var = light_lightness_srv_user_data.light_range_max;
}
target_lightness = var;
}
static s16_t light_ctl_temp_to_level(u16_t temp)
{
float tmp;
/* Mesh Model Specification 6.1.3.1.1 2nd formula start */
tmp = (temp - light_ctl_srv_user_data.temp_range_min) * 65535;
tmp = tmp / (light_ctl_srv_user_data.temp_range_max -
light_ctl_srv_user_data.temp_range_min);
return (s16_t) (tmp - 32768);
/* 6.1.3.1.1 2nd formula end */
}
static u16_t level_to_light_ctl_temp(s16_t level)
{
u16_t tmp;
float diff;
/* Mesh Model Specification 6.1.3.1.1 1st formula start */
diff = (float) (light_ctl_srv_user_data.temp_range_max -
light_ctl_srv_user_data.temp_range_min) / 65535;
tmp = (u16_t) ((level + 32768) * diff);
return (light_ctl_srv_user_data.temp_range_min + tmp);
/* 6.1.3.1.1 1st formula end */
}
void state_binding(u8_t light, u8_t temp)
{
switch (temp) {
case ONOFF_TEMP:
case CTL_TEMP:
temperature =
light_ctl_temp_to_level(light_ctl_srv_user_data.temp);
gen_level_srv_s0_user_data.level = temperature;
break;
case LEVEL_TEMP:
temperature = gen_level_srv_s0_user_data.level;
light_ctl_srv_user_data.temp =
level_to_light_ctl_temp(temperature);
break;
default:
break;
}
switch (light) {
case ONPOWERUP:
if (gen_onoff_srv_root_user_data.onoff == STATE_OFF) {
lightness = 0U;
} else if (gen_onoff_srv_root_user_data.onoff == STATE_ON) {
lightness = light_lightness_srv_user_data.last;
}
break;
case ONOFF:
if (gen_onoff_srv_root_user_data.onoff == STATE_OFF) {
lightness = 0U;
} else if (gen_onoff_srv_root_user_data.onoff == STATE_ON) {
if (light_lightness_srv_user_data.def == 0) {
lightness = light_lightness_srv_user_data.last;
} else {
lightness = light_lightness_srv_user_data.def;
}
}
break;
case LEVEL:
lightness = gen_level_srv_root_user_data.level + 32768;
break;
case DELTA_LEVEL:
lightness = gen_level_srv_root_user_data.level + 32768;
constrain_lightness2(lightness);
goto jump;
case ACTUAL:
lightness = light_lightness_srv_user_data.actual;
break;
case LINEAR:
lightness =
linear_to_actual(light_lightness_srv_user_data.linear);
break;
case CTL:
lightness = light_ctl_srv_user_data.lightness;
break;
default:
break;
}
constrain_lightness(lightness);
jump:
if (lightness != 0) {
light_lightness_srv_user_data.last = lightness;
}
if (lightness) {
gen_onoff_srv_root_user_data.onoff = STATE_ON;
} else {
gen_onoff_srv_root_user_data.onoff = STATE_OFF;
}
gen_level_srv_root_user_data.level = lightness - 32768;
light_lightness_srv_user_data.actual = lightness;
light_lightness_srv_user_data.linear = actual_to_linear(lightness);
light_ctl_srv_user_data.lightness = lightness;
}
void calculate_lightness_target_values(u8_t type)
{
bool set_light_ctl_temp_target_value;
u16_t tmp;
set_light_ctl_temp_target_value = true;
switch (type) {
case ONOFF:
if (gen_onoff_srv_root_user_data.target_onoff == 0) {
tmp = 0U;
} else {
if (light_lightness_srv_user_data.def == 0) {
tmp = light_lightness_srv_user_data.last;
} else {
tmp = light_lightness_srv_user_data.def;
}
}
break;
case LEVEL:
tmp = gen_level_srv_root_user_data.target_level + 32768;
break;
case ACTUAL:
tmp = light_lightness_srv_user_data.target_actual;
break;
case LINEAR:
tmp = linear_to_actual(light_lightness_srv_user_data.target_linear);
break;
case CTL:
set_light_ctl_temp_target_value = false;
tmp = light_ctl_srv_user_data.target_lightness;
target_temperature = light_ctl_temp_to_level(light_ctl_srv_user_data.target_temp);
gen_level_srv_s0_user_data.target_level = target_temperature;
break;
default:
return;
}
constrain_target_lightness(tmp);
if (target_lightness) {
gen_onoff_srv_root_user_data.target_onoff = STATE_ON;
} else {
gen_onoff_srv_root_user_data.target_onoff = STATE_OFF;
}
gen_level_srv_root_user_data.target_level = target_lightness - 32768;
light_lightness_srv_user_data.target_actual = target_lightness;
light_lightness_srv_user_data.target_linear =
actual_to_linear(target_lightness);
light_ctl_srv_user_data.target_lightness = target_lightness;
if (set_light_ctl_temp_target_value) {
target_temperature = light_ctl_srv_user_data.temp;
light_ctl_srv_user_data.target_temp = target_temperature;
}
}
void calculate_temp_target_values(u8_t type)
{
bool set_light_ctl_delta_uv_target_value;
set_light_ctl_delta_uv_target_value = true;
switch (type) {
case LEVEL_TEMP:
target_temperature = gen_level_srv_s0_user_data.target_level;
light_ctl_srv_user_data.target_temp =
level_to_light_ctl_temp(target_temperature);
break;
case CTL_TEMP:
set_light_ctl_delta_uv_target_value = false;
target_temperature = light_ctl_temp_to_level(light_ctl_srv_user_data.target_temp);
gen_level_srv_s0_user_data.target_level = target_temperature;
break;
default:
return;
}
target_lightness = light_ctl_srv_user_data.lightness;
light_ctl_srv_user_data.target_lightness = target_lightness;
if (set_light_ctl_delta_uv_target_value) {
light_ctl_srv_user_data.target_delta_uv =
light_ctl_srv_user_data.delta_uv;
}
}

View file

@ -0,0 +1,53 @@
/*
* 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _STATE_BINDING_H
#define _STATE_BINDING_H
enum state_binding {
ONPOWERUP = 0x01,
ONOFF,
LEVEL,
DELTA_LEVEL,
ACTUAL,
LINEAR,
CTL,
IGNORE,
ONOFF_TEMP,
LEVEL_TEMP,
CTL_TEMP,
IGNORE_TEMP
};
extern u16_t lightness, target_lightness;
extern s16_t temperature, target_temperature;
void state_binding(u8_t lightness, u8_t temperature);
void calculate_lightness_target_values(u8_t type);
void calculate_temp_target_values(u8_t type);
#endif

View file

@ -0,0 +1,255 @@
/*
* 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "base64/base64.h"
#include "console/console.h"
#include "mesh/mesh.h"
#include "ble_mesh.h"
#include "device_composition.h"
#include "storage.h"
static u8_t storage_id;
u8_t reset_counter;
static void save_reset_counter(void)
{
char buf[5];
settings_str_from_bytes(&reset_counter, sizeof(reset_counter), buf,
sizeof(buf));
settings_save_one("ps/rc", buf);
}
static void save_gen_def_trans_time_state(void)
{
char buf[5];
settings_str_from_bytes(&gen_def_trans_time_srv_user_data.tt,
sizeof(gen_def_trans_time_srv_user_data.tt),
buf, sizeof(buf));
settings_save_one("ps/gdtt", buf);
}
static void save_gen_onpowerup_state(void)
{
char buf[5];
settings_str_from_bytes(&gen_power_onoff_srv_user_data.onpowerup,
sizeof(gen_power_onoff_srv_user_data.onpowerup),
buf, sizeof(buf));
settings_save_one("ps/gpo", buf);
if (gen_power_onoff_srv_user_data.onpowerup == 0x02) {
save_on_flash(LIGHTNESS_TEMP_LAST_STATE);
}
}
static void save_lightness_temp_def_state(void)
{
char buf[12];
light_ctl_srv_user_data.lightness_temp_def =
(u32_t) ((light_ctl_srv_user_data.lightness_def << 16) |
light_ctl_srv_user_data.temp_def);
settings_str_from_bytes(&light_ctl_srv_user_data.lightness_temp_def,
sizeof(light_ctl_srv_user_data.lightness_temp_def),
buf, sizeof(buf));
settings_save_one("ps/ltd", buf);
}
static void save_lightness_temp_last_state(void)
{
char buf[12];
light_ctl_srv_user_data.lightness_temp_last =
(u32_t) ((light_ctl_srv_user_data.lightness << 16) |
light_ctl_srv_user_data.temp);
settings_str_from_bytes(&light_ctl_srv_user_data.lightness_temp_last,
sizeof(light_ctl_srv_user_data.lightness_temp_last),
buf, sizeof(buf));
settings_save_one("ps/ltl", buf);
printk("Light CTL Last values have beed saved !!\n");
}
static void save_lightness_range(void)
{
char buf[12];
light_lightness_srv_user_data.lightness_range =
(u32_t) ((light_lightness_srv_user_data.light_range_max << 16) |
light_lightness_srv_user_data.light_range_min);
settings_str_from_bytes(&light_lightness_srv_user_data.lightness_range,
sizeof(light_lightness_srv_user_data.lightness_range),
buf, sizeof(buf));
settings_save_one("ps/lr", buf);
}
static void save_temperature_range(void)
{
char buf[12];
light_ctl_srv_user_data.temperature_range =
(u32_t) ((light_ctl_srv_user_data.temp_range_max << 16) |
light_ctl_srv_user_data.temp_range_min);
settings_str_from_bytes(&light_ctl_srv_user_data.temperature_range,
sizeof(light_ctl_srv_user_data.temperature_range),
buf, sizeof(buf));
settings_save_one("ps/tr", buf);
}
static void storage_work_handler(struct os_event *work)
{
switch (storage_id) {
case RESET_COUNTER:
save_reset_counter();
break;
case GEN_DEF_TRANS_TIME_STATE:
save_gen_def_trans_time_state();
break;
case GEN_ONPOWERUP_STATE:
save_gen_onpowerup_state();
break;
case LIGHTNESS_TEMP_DEF_STATE:
save_lightness_temp_def_state();
break;
case LIGHTNESS_TEMP_LAST_STATE:
save_lightness_temp_last_state();
break;
case LIGHTNESS_RANGE:
save_lightness_range();
break;
case TEMPERATURE_RANGE:
save_temperature_range();
break;
}
}
struct os_callout storage_work;
void save_on_flash(u8_t id)
{
storage_id = id;
os_callout_reset(&storage_work, 0);
}
static int ps_set(int argc, char **argv, char *val)
{
int len;
if (argc == 1) {
if (!strcmp(argv[0], "rc")) {
len = sizeof(reset_counter);
return settings_bytes_from_str(val, &reset_counter,
&len);
}
if (!strcmp(argv[0], "gdtt")) {
len = sizeof(gen_def_trans_time_srv_user_data.tt);
return settings_bytes_from_str(val,
&gen_def_trans_time_srv_user_data.tt, &len);
}
if (!strcmp(argv[0], "gpo")) {
len = sizeof(gen_power_onoff_srv_user_data.onpowerup);
return settings_bytes_from_str(val,
&gen_power_onoff_srv_user_data.onpowerup, &len);
}
if (!strcmp(argv[0], "ltd")) {
len = sizeof(light_ctl_srv_user_data.lightness_temp_def);
return settings_bytes_from_str(val,
&light_ctl_srv_user_data.lightness_temp_def,
&len);
}
if (!strcmp(argv[0], "ltl")) {
len = sizeof(light_ctl_srv_user_data.
lightness_temp_last);
return settings_bytes_from_str(val,
&light_ctl_srv_user_data.lightness_temp_last,
&len);
}
if (!strcmp(argv[0], "lr")) {
len = sizeof(light_lightness_srv_user_data.
lightness_range);
return settings_bytes_from_str(val,
&light_lightness_srv_user_data.lightness_range,
&len);
}
if (!strcmp(argv[0], "tr")) {
len = sizeof(light_ctl_srv_user_data.
temperature_range);
return settings_bytes_from_str(val,
&light_ctl_srv_user_data. temperature_range,
&len);
}
}
return -ENOENT;
}
static struct conf_handler ps_settings = {
.ch_name = "ps",
.ch_set = ps_set,
};
int ps_settings_init(void)
{
int err;
os_callout_init(&storage_work, os_eventq_dflt_get(),
storage_work_handler, NULL);
err = conf_register(&ps_settings);
if (err) {
printk("ps_settings_register failed (err %d)", err);
return err;
}
return 0;
}

View file

@ -0,0 +1,47 @@
/*
* 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _STORAGE_H
#define _STORAGE_H
enum ps_variables_id {
RESET_COUNTER = 0x01,
GEN_DEF_TRANS_TIME_STATE,
GEN_ONPOWERUP_STATE,
LIGHTNESS_TEMP_DEF_STATE,
LIGHTNESS_TEMP_LAST_STATE,
LIGHTNESS_RANGE,
TEMPERATURE_RANGE
};
extern u8_t reset_counter;
extern struct os_callout storage_work;
int ps_settings_init(void);
void save_on_flash(u8_t id);
#endif

View file

@ -0,0 +1,792 @@
/*
* 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "ble_mesh.h"
#include "common.h"
#include "device_composition.h"
#include "state_binding.h"
#include "transition.h"
struct os_callout onoff_work;
struct os_callout level_lightness_work;
struct os_callout level_temp_work;
struct os_callout light_lightness_actual_work;
struct os_callout light_lightness_linear_work;
struct os_callout light_ctl_work;
struct os_callout light_ctl_temp_work;
struct os_callout dummy_timer;
u8_t transition_type, default_tt;
u32_t *ptr_counter;
struct os_callout *ptr_timer = &dummy_timer;
struct transition lightness_transition, temp_transition;
/* Function to calculate Remaining Time (Start) */
void calculate_rt(struct transition *transition)
{
u8_t steps, resolution;
s32_t duration_remainder;
s64_t now;
if (transition->just_started) {
transition->rt = transition->tt;
} else {
now = k_uptime_get();
duration_remainder = transition->total_duration -
(now - transition->start_timestamp);
if (duration_remainder > 620000) {
/* > 620 seconds -> resolution = 0b11 [10 minutes] */
resolution = 0x03;
steps = duration_remainder / 600000;
} else if (duration_remainder > 62000) {
/* > 62 seconds -> resolution = 0b10 [10 seconds] */
resolution = 0x02;
steps = duration_remainder / 10000;
} else if (duration_remainder > 6200) {
/* > 6.2 seconds -> resolution = 0b01 [1 seconds] */
resolution = 0x01;
steps = duration_remainder / 1000;
} else if (duration_remainder > 0) {
/* <= 6.2 seconds -> resolution = 0b00 [100 ms] */
resolution = 0x00;
steps = duration_remainder / 100;
} else {
resolution = 0x00;
steps = 0x00;
}
transition->rt = (resolution << 6) | steps;
}
}
/* Function to calculate Remaining Time (End) */
static void bound_states_transition_type_reassignment(u8_t type)
{
switch (type) {
case ONOFF:
case LEVEL:
case ACTUAL:
case LINEAR:
light_ctl_srv_user_data.transition = &lightness_transition;
break;
case CTL:
light_ctl_srv_user_data.transition = &lightness_transition;
gen_level_srv_s0_user_data.transition = &lightness_transition;
break;
case LEVEL_TEMP:
case CTL_TEMP:
gen_level_srv_s0_user_data.transition = &temp_transition;
light_ctl_srv_user_data.transition = &temp_transition;
break;
default:
break;
}
}
static void tt_values_calculator(struct transition *transition)
{
u8_t steps_multiplier, resolution;
resolution = (transition->tt >> 6);
steps_multiplier = (transition->tt & 0x3F);
switch (resolution) {
case 0: /* 100ms */
transition->total_duration = steps_multiplier * 100;
break;
case 1: /* 1 second */
transition->total_duration = steps_multiplier * 1000;
break;
case 2: /* 10 seconds */
transition->total_duration = steps_multiplier * 10000;
break;
case 3: /* 10 minutes */
transition->total_duration = steps_multiplier * 600000;
break;
}
transition->counter = ((float) transition->total_duration / 100);
if (transition->counter > DEVICE_SPECIFIC_RESOLUTION) {
transition->counter = DEVICE_SPECIFIC_RESOLUTION;
}
ptr_counter = &transition->counter;
}
void onoff_tt_values(struct generic_onoff_state *state, u8_t tt, u8_t delay)
{
bound_states_transition_type_reassignment(ONOFF);
calculate_lightness_target_values(ONOFF);
state->transition->tt = tt;
state->transition->delay = delay;
if (tt != 0) {
tt_values_calculator(state->transition);
} else {
return;
}
state->transition->quo_tt = state->transition->total_duration /
state->transition->counter;
state->tt_delta = ((float) (lightness - target_lightness) /
state->transition->counter);
}
void level_tt_values(struct generic_level_state *state, u8_t tt, u8_t delay)
{
if (state == &gen_level_srv_root_user_data) {
bound_states_transition_type_reassignment(LEVEL);
calculate_lightness_target_values(LEVEL);
} else if (state == &gen_level_srv_s0_user_data) {
bound_states_transition_type_reassignment(LEVEL_TEMP);
calculate_temp_target_values(LEVEL_TEMP);
}
state->transition->tt = tt;
state->transition->delay = delay;
if (tt != 0) {
tt_values_calculator(state->transition);
} else {
return;
}
state->transition->quo_tt = state->transition->total_duration /
state->transition->counter;
state->tt_delta = ((float) (state->level - state->target_level) /
state->transition->counter);
}
void light_lightness_actual_tt_values(struct light_lightness_state *state,
u8_t tt, u8_t delay)
{
bound_states_transition_type_reassignment(ACTUAL);
calculate_lightness_target_values(ACTUAL);
state->transition->tt = tt;
state->transition->delay = delay;
if (tt != 0) {
tt_values_calculator(state->transition);
} else {
return;
}
state->transition->quo_tt = state->transition->total_duration /
state->transition->counter;
state->tt_delta_actual =
((float) (state->actual - state->target_actual) /
state->transition->counter);
}
void light_lightness_linear_tt_values(struct light_lightness_state *state,
u8_t tt, u8_t delay)
{
bound_states_transition_type_reassignment(LINEAR);
calculate_lightness_target_values(LINEAR);
state->transition->tt = tt;
state->transition->delay = delay;
if (tt != 0) {
tt_values_calculator(state->transition);
} else {
return;
}
state->transition->quo_tt = state->transition->total_duration /
state->transition->counter;
state->tt_delta_linear =
((float) (state->linear - state->target_linear) /
state->transition->counter);
}
void light_ctl_tt_values(struct light_ctl_state *state, u8_t tt, u8_t delay)
{
bound_states_transition_type_reassignment(CTL);
calculate_lightness_target_values(CTL);
state->transition->tt = tt;
state->transition->delay = delay;
if (tt != 0) {
tt_values_calculator(state->transition);
} else {
return;
}
state->transition->quo_tt = state->transition->total_duration /
state->transition->counter;
state->tt_delta_lightness =
((float) (state->lightness - state->target_lightness) /
state->transition->counter);
state->tt_delta_temp =
((float) (state->temp - state->target_temp) /
state->transition->counter);
state->tt_delta_duv =
((float) (state->delta_uv - state->target_delta_uv) /
state->transition->counter);
}
void light_ctl_temp_tt_values(struct light_ctl_state *state,
u8_t tt, u8_t delay)
{
bound_states_transition_type_reassignment(CTL_TEMP);
calculate_temp_target_values(CTL_TEMP);
state->transition->tt = tt;
state->transition->delay = delay;
if (tt != 0) {
tt_values_calculator(state->transition);
} else {
return;
}
state->transition->quo_tt = state->transition->total_duration /
state->transition->counter;
state->tt_delta_temp = ((float) (state->temp - state->target_temp) /
state->transition->counter);
state->tt_delta_duv =
((float) (state->delta_uv - state->target_delta_uv) /
state->transition->counter);
}
/* Timers related handlers & threads (Start) */
static void onoff_work_handler(struct os_event *work)
{
struct generic_onoff_state *state = &gen_onoff_srv_root_user_data;
if (state->transition->just_started) {
state->transition->just_started = false;
if (state->transition->counter == 0) {
state_binding(ONOFF, IGNORE_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
} else {
state->transition->start_timestamp = k_uptime_get();
if (state->target_onoff == STATE_ON) {
state->onoff = STATE_ON;
}
}
return;
}
if (state->transition->counter != 0) {
state->transition->counter--;
lightness -= state->tt_delta;
state_binding(IGNORE, IGNORE_TEMP);
update_light_state();
}
if (state->transition->counter == 0) {
state->onoff = state->target_onoff;
lightness = target_lightness;
state_binding(IGNORE, IGNORE_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
}
}
static void level_lightness_work_handler(struct os_event *work)
{
u8_t level;
struct generic_level_state *state = &gen_level_srv_root_user_data;
switch (transition_type) {
case LEVEL_TT:
level = LEVEL;
break;
case LEVEL_TT_DELTA:
level = DELTA_LEVEL;
break;
case LEVEL_TT_MOVE:
level = LEVEL;
break;
default:
return;
}
if (state->transition->just_started) {
state->transition->just_started = false;
if (state->transition->counter == 0) {
state_binding(level, IGNORE_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
} else {
state->transition->start_timestamp = k_uptime_get();
}
return;
}
if (state->transition->counter != 0) {
state->transition->counter--;
state->level -= state->tt_delta;
state_binding(level, IGNORE_TEMP);
update_light_state();
}
if (state->transition->counter == 0) {
state->level = state->target_level;
state_binding(level, IGNORE_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
}
}
static void level_temp_work_handler(struct os_event *work)
{
struct generic_level_state *state = &gen_level_srv_s0_user_data;
switch (transition_type) {
case LEVEL_TEMP_TT:
break;
case LEVEL_TEMP_TT_DELTA:
break;
case LEVEL_TEMP_TT_MOVE:
break;
default:
return;
}
if (state->transition->just_started) {
state->transition->just_started = false;
if (state->transition->counter == 0) {
state_binding(IGNORE, LEVEL_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
} else {
state->transition->start_timestamp = k_uptime_get();
}
return;
}
if (state->transition->counter != 0) {
state->transition->counter--;
state->level -= state->tt_delta;
state_binding(IGNORE, LEVEL_TEMP);
update_light_state();
}
if (state->transition->counter == 0) {
state->level = state->target_level;
state_binding(IGNORE, LEVEL_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
}
}
static void light_lightness_actual_work_handler(struct os_event *work)
{
struct light_lightness_state *state = &light_lightness_srv_user_data;
if (state->transition->just_started) {
state->transition->just_started = false;
if (state->transition->counter == 0) {
state_binding(ACTUAL, IGNORE_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
} else {
state->transition->start_timestamp = k_uptime_get();
}
return;
}
if (state->transition->counter != 0) {
state->transition->counter--;
state->actual -= state->tt_delta_actual;
state_binding(ACTUAL, IGNORE_TEMP);
update_light_state();
}
if (state->transition->counter == 0) {
state->actual = state->target_actual;
state_binding(ACTUAL, IGNORE_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
}
}
static void light_lightness_linear_work_handler(struct os_event *work)
{
struct light_lightness_state *state = &light_lightness_srv_user_data;
if (state->transition->just_started) {
state->transition->just_started = false;
if (state->transition->counter == 0) {
state_binding(LINEAR, IGNORE_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
} else {
state->transition->start_timestamp = k_uptime_get();
}
return;
}
if (state->transition->counter != 0) {
state->transition->counter--;
state->linear -= state->tt_delta_linear;
state_binding(LINEAR, IGNORE_TEMP);
update_light_state();
}
if (state->transition->counter == 0) {
state->linear = state->target_linear;
state_binding(LINEAR, IGNORE_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
}
}
static void light_ctl_work_handler(struct os_event *work)
{
struct light_ctl_state *state = &light_ctl_srv_user_data;
if (state->transition->just_started) {
state->transition->just_started = false;
if (state->transition->counter == 0) {
state_binding(CTL, CTL_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
} else {
state->transition->start_timestamp = k_uptime_get();
}
return;
}
if (state->transition->counter != 0) {
state->transition->counter--;
/* Lightness */
state->lightness -= state->tt_delta_lightness;
/* Temperature */
state->temp -= state->tt_delta_temp;
/* Delta_UV */
state->delta_uv -= state->tt_delta_duv;
state_binding(CTL, CTL_TEMP);
update_light_state();
}
if (state->transition->counter == 0) {
state->lightness = state->target_lightness;
state->temp = state->target_temp;
state->delta_uv = state->target_delta_uv;
state_binding(CTL, CTL_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
}
}
static void light_ctl_temp_work_handler(struct os_event *work)
{
struct light_ctl_state *state = &light_ctl_srv_user_data;
if (state->transition->just_started) {
state->transition->just_started = false;
if (state->transition->counter == 0) {
state_binding(IGNORE, CTL_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
} else {
state->transition->start_timestamp = k_uptime_get();
}
return;
}
if (state->transition->counter != 0) {
state->transition->counter--;
/* Temperature */
state->temp -= state->tt_delta_temp;
/* Delta UV */
state->delta_uv -= state->tt_delta_duv;
state_binding(IGNORE, CTL_TEMP);
update_light_state();
}
if (state->transition->counter == 0) {
state->temp = state->target_temp;
state->delta_uv = state->target_delta_uv;
state_binding(IGNORE, CTL_TEMP);
update_light_state();
os_callout_stop(ptr_timer);
}
}
static void dummy_timer_handler(struct os_event *ev)
{ }
static void onoff_tt_handler(struct os_event *ev)
{
struct generic_onoff_state *state = ev->ev_arg;
assert(state != NULL);
os_callout_reset(&onoff_work, 0);
os_callout_reset(&state->transition->timer,
os_time_ms_to_ticks32(
K_MSEC(state->transition->quo_tt)));
}
static void level_lightness_tt_handler(struct os_event *ev)
{
struct generic_level_state *state = ev->ev_arg;
assert(state != NULL);
os_callout_reset(&level_lightness_work, 0);
os_callout_reset(&state->transition->timer,
os_time_ms_to_ticks32(
K_MSEC(state->transition->quo_tt)));
}
static void level_temp_tt_handler(struct os_event *ev)
{
struct generic_level_state *state = ev->ev_arg;
assert(state != NULL);
os_callout_reset(&level_temp_work, 0);
os_callout_reset(&state->transition->timer,
os_time_ms_to_ticks32(
K_MSEC(state->transition->quo_tt)));
}
static void light_lightness_actual_tt_handler(struct os_event *ev)
{
struct light_lightness_state *state = ev->ev_arg;
assert(state != NULL);
os_callout_reset(&light_lightness_actual_work, 0);
os_callout_reset(&state->transition->timer,
os_time_ms_to_ticks32(
K_MSEC(state->transition->quo_tt)));
}
static void light_lightness_linear_tt_handler(struct os_event *ev)
{
struct light_lightness_state *state = ev->ev_arg;
assert(state != NULL);
os_callout_reset(&light_lightness_linear_work, 0);
os_callout_reset(&state->transition->timer,
os_time_ms_to_ticks32(
K_MSEC(state->transition->quo_tt)));
}
static void light_ctl_tt_handler(struct os_event *ev)
{
struct light_ctl_state *state = ev->ev_arg;
assert(state != NULL);
os_callout_reset(&light_ctl_work, 0);
os_callout_reset(&state->transition->timer,
os_time_ms_to_ticks32(
K_MSEC(state->transition->quo_tt)));
}
static void light_ctl_temp_tt_handler(struct os_event *ev)
{
struct light_ctl_state *state = ev->ev_arg;
assert(state != NULL);
os_callout_reset(&light_ctl_temp_work, 0);
os_callout_reset(&state->transition->timer,
os_time_ms_to_ticks32(
K_MSEC(state->transition->quo_tt)));
}
/* Timers related handlers & threads (End) */
/* Messages handlers (Start) */
void onoff_handler(struct generic_onoff_state *state)
{
ptr_timer = &state->transition->timer;
os_callout_init(ptr_timer, os_eventq_dflt_get(),
onoff_tt_handler, NULL);
ptr_timer->c_ev.ev_arg = state;
os_callout_reset(ptr_timer,
os_time_ms_to_ticks32(
K_MSEC(5 * state->transition->delay)));
}
void level_lightness_handler(struct generic_level_state *state)
{
ptr_timer = &state->transition->timer;
os_callout_init(ptr_timer, os_eventq_dflt_get(),
level_lightness_tt_handler, NULL);
ptr_timer->c_ev.ev_arg = state;
os_callout_reset(ptr_timer,
os_time_ms_to_ticks32(
K_MSEC(5 * state->transition->delay)));
}
void level_temp_handler(struct generic_level_state *state)
{
ptr_timer = &state->transition->timer;
os_callout_init(ptr_timer, os_eventq_dflt_get(),
level_temp_tt_handler, NULL);
ptr_timer->c_ev.ev_arg = state;
os_callout_reset(ptr_timer,
os_time_ms_to_ticks32(
K_MSEC(5 * state->transition->delay)));
}
void light_lightness_actual_handler(struct light_lightness_state *state)
{
ptr_timer = &state->transition->timer;
os_callout_init(ptr_timer, os_eventq_dflt_get(),
light_lightness_actual_tt_handler, NULL);
ptr_timer->c_ev.ev_arg = state;
os_callout_reset(ptr_timer,
os_time_ms_to_ticks32(
K_MSEC(5 * state->transition->delay)));
}
void light_lightness_linear_handler(struct light_lightness_state *state)
{
ptr_timer = &state->transition->timer;
os_callout_init(ptr_timer, os_eventq_dflt_get(),
light_lightness_linear_tt_handler, NULL);
ptr_timer->c_ev.ev_arg = state;
os_callout_reset(ptr_timer,
os_time_ms_to_ticks32(
K_MSEC(5 * state->transition->delay)));
}
void light_ctl_handler(struct light_ctl_state *state)
{
ptr_timer = &state->transition->timer;
os_callout_init(ptr_timer, os_eventq_dflt_get(),
light_ctl_tt_handler, NULL);
ptr_timer->c_ev.ev_arg = state;
os_callout_reset(ptr_timer,
os_time_ms_to_ticks32(
K_MSEC(5 * state->transition->delay)));
}
void light_ctl_temp_handler(struct light_ctl_state *state)
{
ptr_timer = &state->transition->timer;
os_callout_init(ptr_timer, os_eventq_dflt_get(),
light_ctl_temp_tt_handler, NULL);
ptr_timer->c_ev.ev_arg = state;
os_callout_reset(ptr_timer,
os_time_ms_to_ticks32(
K_MSEC(5 * state->transition->delay)));
}
/* Messages handlers (End) */
void transition_timers_init(void)
{
os_callout_init(&onoff_work, os_eventq_dflt_get(),
onoff_work_handler, NULL);
os_callout_init(&level_lightness_work, os_eventq_dflt_get(),
level_lightness_work_handler, NULL);
os_callout_init(&level_temp_work, os_eventq_dflt_get(),
level_temp_work_handler, NULL);
os_callout_init(&light_lightness_actual_work,
os_eventq_dflt_get(),
light_lightness_actual_work_handler, NULL);
os_callout_init(&light_lightness_linear_work,
os_eventq_dflt_get(),
light_lightness_linear_work_handler, NULL);
os_callout_init(&light_ctl_work, os_eventq_dflt_get(),
light_ctl_work_handler, NULL);
os_callout_init(&light_ctl_temp_work, os_eventq_dflt_get(),
light_ctl_temp_work_handler, NULL);
os_callout_init(&dummy_timer, os_eventq_dflt_get(),
dummy_timer_handler, NULL);
}

View file

@ -0,0 +1,87 @@
/*
* 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.
*/
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _TRANSITION_H
#define _TRANSITION_H
#define UNKNOWN_VALUE 0x3F
#define DEVICE_SPECIFIC_RESOLUTION 10
enum level_transition_types {
LEVEL_TT,
LEVEL_TT_DELTA,
LEVEL_TT_MOVE,
LEVEL_TEMP_TT,
LEVEL_TEMP_TT_DELTA,
LEVEL_TEMP_TT_MOVE,
};
struct transition {
bool just_started;
u8_t tt;
u8_t rt;
u8_t delay;
u32_t quo_tt;
u32_t counter;
u32_t total_duration;
s64_t start_timestamp;
struct os_callout timer;
};
extern u8_t transition_type, default_tt;
extern u32_t *ptr_counter;
extern struct os_callout *ptr_timer;
extern struct transition lightness_transition, temp_transition;
extern struct os_callout dummy_timer;
void calculate_rt(struct transition *transition);
void onoff_tt_values(struct generic_onoff_state *state, u8_t tt, u8_t delay);
void level_tt_values(struct generic_level_state *state, u8_t tt, u8_t delay);
void light_lightness_actual_tt_values(struct light_lightness_state *state,
u8_t tt, u8_t delay);
void light_lightness_linear_tt_values(struct light_lightness_state *state,
u8_t tt, u8_t delay);
void light_ctl_tt_values(struct light_ctl_state *state, u8_t tt, u8_t delay);
void light_ctl_temp_tt_values(struct light_ctl_state *state,
u8_t tt, u8_t delay);
void onoff_handler(struct generic_onoff_state *state);
void level_lightness_handler(struct generic_level_state *state);
void level_temp_handler(struct generic_level_state *state);
void light_lightness_actual_handler(struct light_lightness_state *state);
void light_lightness_linear_handler(struct light_lightness_state *state);
void light_ctl_handler(struct light_ctl_state *state);
void light_ctl_temp_handler(struct light_ctl_state *state);
void transition_timers_init(void);
#endif

View file

@ -0,0 +1,60 @@
# 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.
#
syscfg.vals:
# Set log level to info (disable debug logging).
LOG_LEVEL: 1
# Default task settings
OS_MAIN_STACK_SIZE: 4096
# SMP is not supported in this app, so disable smp-over-shell.
SHELL_MGMT: 0
MSYS_1_BLOCK_COUNT: 48
FLOAT_USER: 1
HARD_FLOAT: 1
BLE_MESH_DEV_UUID: "((uint8_t[16]){0xdd, 0xdd, 0})"
BLE_MESH_ADV_BUF_COUNT: 60
BLE_MESH_TX_SEG_MAX: 6
BLE_MESH_TX_SEG_MSG_COUNT: 3
BLE_MESH_RX_SEG_MSG_COUNT: 3
BLE_MESH_CRPL: 128
BLE_MESH_RPL_STORE_TIMEOUT: 120
BLE_MESH_MSG_CACHE_SIZE: 100
BLE_MESH_SETTINGS: 1
CONFIG_FCB: 1
BLE_MESH: 1
BLE_MESH_RELAY: 1
BLE_MESH_LOW_POWER: 0
BLE_MESH_LPN_AUTO: 0
BLE_MESH_FRIEND: 0
BLE_MESH_PROV: 1
BLE_MESH_PB_ADV: 1
BLE_MESH_PB_GATT: 1
BLE_MESH_GATT_PROXY: 1
BLE_MESH_SUBNET_COUNT: 2
BLE_MESH_APP_KEY_COUNT: 2
BLE_MESH_MODEL_GROUP_COUNT: 2
BLE_MESH_LABEL_COUNT: 3

View file

@ -0,0 +1,37 @@
# 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.
#
pkg.name: apps/blemesh_shell
pkg.type: app
pkg.description: Sample application for BLE Mesh node with shell support
pkg.author: "Michał Narajowski <michal.narajowski@codecoup.pl>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
pkg.deps:
- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/sys/console/full"
- "@apache-mynewt-core/sys/log/full"
- "@apache-mynewt-core/sys/log/modlog"
- "@apache-mynewt-core/sys/stats/full"
- "@apache-mynewt-core/sys/shell"
- nimble/controller
- nimble/host
- nimble/host/services/gap
- nimble/host/services/gatt
- nimble/host/store/ram
- nimble/transport/ram

View file

@ -0,0 +1,114 @@
/*
* 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.
*/
#define MESH_LOG_MODULE BLE_MESH_LOG
#include <assert.h>
#include "os/mynewt.h"
#include "mesh/mesh.h"
#include "console/console.h"
#include "hal/hal_system.h"
#include "hal/hal_gpio.h"
#include "bsp/bsp.h"
#include "shell/shell.h"
/* BLE */
#include "nimble/ble.h"
#include "host/ble_hs.h"
#include "services/gap/ble_svc_gap.h"
#include "mesh/glue.h"
#include "mesh/testing.h"
void net_recv_ev(uint8_t ttl, uint8_t ctl, uint16_t src, uint16_t dst,
const void *payload, size_t payload_len)
{
console_printf("Received net packet: ttl 0x%02x ctl 0x%02x src 0x%04x "
"dst 0x%04x " "payload_len %d\n", ttl, ctl, src, dst,
payload_len);
}
static void model_bound_cb(u16_t addr, struct bt_mesh_model *model,
u16_t key_idx)
{
console_printf("Model bound: remote addr 0x%04x key_idx 0x%04x model %p\n",
addr, key_idx, model);
}
static void model_unbound_cb(u16_t addr, struct bt_mesh_model *model,
u16_t key_idx)
{
console_printf("Model unbound: remote addr 0x%04x key_idx 0x%04x "
"model %p\n", addr, key_idx, model);
}
static void invalid_bearer_cb(u8_t opcode)
{
console_printf("Invalid bearer: opcode 0x%02x\n", opcode);
}
static void incomp_timer_exp_cb(void)
{
console_printf("Incomplete timer expired\n");
}
static struct bt_test_cb bt_test_cb = {
.mesh_net_recv = net_recv_ev,
.mesh_model_bound = model_bound_cb,
.mesh_model_unbound = model_unbound_cb,
.mesh_prov_invalid_bearer = invalid_bearer_cb,
.mesh_trans_incomp_timer_exp = incomp_timer_exp_cb,
};
static void
blemesh_on_reset(int reason)
{
BLE_HS_LOG(ERROR, "Resetting state; reason=%d\n", reason);
}
static void
blemesh_on_sync(void)
{
console_printf("Bluetooth initialized\n");
shell_register_default_module("mesh");
if (IS_ENABLED(CONFIG_BT_TESTING)) {
bt_test_cb_register(&bt_test_cb);
}
}
int
main(void)
{
/* Initialize OS */
sysinit();
/* Initialize the NimBLE host configuration. */
ble_hs_cfg.reset_cb = blemesh_on_reset;
ble_hs_cfg.sync_cb = blemesh_on_sync;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
hal_gpio_init_out(LED_2, 0);
while (1) {
os_eventq_run(os_eventq_dflt_get());
}
return 0;
}

View file

@ -0,0 +1,57 @@
# 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.
#
syscfg.vals:
# Enable the shell task.
SHELL_TASK: 1
# Set log level to info (disable debug logging).
LOG_LEVEL: 0
# Default task settings
OS_MAIN_STACK_SIZE: 768
# SMP is not supported in this app, so disable smp-over-shell.
SHELL_MGMT: 0
MSYS_1_BLOCK_COUNT: 80
BLE_MESH_ADV_BUF_COUNT: 20
BLE_MESH_TX_SEG_MAX: 6
BLE_MESH: 1
BLE_MESH_SHELL: 1
BLE_MESH_PROV: 1
BLE_MESH_PROVISIONER: 1
BLE_MESH_RELAY: 1
BLE_MESH_PB_ADV: 1
BLE_MESH_PB_GATT: 1
BLE_MESH_LOW_POWER: 1
BLE_MESH_LPN_AUTO: 0
BLE_MESH_GATT_PROXY: 1
BLE_MESH_LABEL_COUNT: 2
BLE_MESH_SUBNET_COUNT: 2
BLE_MESH_MODEL_GROUP_COUNT: 2
BLE_MESH_MODEL_EXTENSIONS: 1
BLE_MESH_APP_KEY_COUNT: 4
BLE_MESH_IV_UPDATE_TEST: 1
BLE_MESH_TESTING: 1
BLE_MESH_FRIEND: 1
BLE_MESH_CFG_CLI: 1
BLE_MESH_SETTINGS: 0
CONFIG_NFFS: 0

View file

@ -0,0 +1,45 @@
# 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.
#
pkg.name: apps/bleprph
pkg.type: app
pkg.description: Simple BLE peripheral application.
pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
pkg.deps:
- "@apache-mynewt-core/boot/split"
- "@mcuboot/boot/bootutil"
- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/mgmt/imgmgr"
- "@apache-mynewt-core/mgmt/smp"
- "@apache-mynewt-core/mgmt/smp/transport/ble"
- "@apache-mynewt-core/sys/console/full"
- "@apache-mynewt-core/sys/log/full"
- "@apache-mynewt-core/sys/log/modlog"
- "@apache-mynewt-core/sys/stats/full"
- "@apache-mynewt-core/sys/sysinit"
- "@apache-mynewt-core/sys/id"
- nimble/host
- nimble/host/services/ans
- nimble/host/services/dis
- nimble/host/services/gap
- nimble/host/services/gatt
- nimble/host/store/config
- nimble/host/util
- nimble/transport

View file

@ -0,0 +1,61 @@
/*
* 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.
*/
#ifndef H_BLEPRPH_
#define H_BLEPRPH_
#include <stdbool.h>
#include "nimble/ble.h"
#include "modlog/modlog.h"
#ifdef __cplusplus
extern "C" {
#endif
struct ble_hs_cfg;
struct ble_gatt_register_ctxt;
/** GATT server. */
#define GATT_SVR_SVC_ALERT_UUID 0x1811
#define GATT_SVR_CHR_SUP_NEW_ALERT_CAT_UUID 0x2A47
#define GATT_SVR_CHR_NEW_ALERT 0x2A46
#define GATT_SVR_CHR_SUP_UNR_ALERT_CAT_UUID 0x2A48
#define GATT_SVR_CHR_UNR_ALERT_STAT_UUID 0x2A45
#define GATT_SVR_CHR_ALERT_NOT_CTRL_PT 0x2A44
void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg);
int gatt_svr_init(void);
/* PHY support */
#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT)
#define CONN_HANDLE_INVALID 0xffff
void phy_init(void);
void phy_conn_changed(uint16_t handle);
void phy_update(uint8_t phy);
#endif
/** Misc. */
void print_bytes(const uint8_t *bytes, int len);
void print_addr(const void *addr);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,204 @@
/*
* 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 <stdio.h>
#include <string.h>
#include "bsp/bsp.h"
#include "host/ble_hs.h"
#include "host/ble_uuid.h"
#include "bleprph.h"
/**
* The vendor specific security test service consists of two characteristics:
* o random-number-generator: generates a random 32-bit number each time
* it is read. This characteristic can only be read over an encrypted
* connection.
* o static-value: a single-byte characteristic that can always be read,
* but can only be written over an encrypted connection.
*/
/* 59462f12-9543-9999-12c8-58b459a2712d */
static const ble_uuid128_t gatt_svr_svc_sec_test_uuid =
BLE_UUID128_INIT(0x2d, 0x71, 0xa2, 0x59, 0xb4, 0x58, 0xc8, 0x12,
0x99, 0x99, 0x43, 0x95, 0x12, 0x2f, 0x46, 0x59);
/* 5c3a659e-897e-45e1-b016-007107c96df6 */
static const ble_uuid128_t gatt_svr_chr_sec_test_rand_uuid =
BLE_UUID128_INIT(0xf6, 0x6d, 0xc9, 0x07, 0x71, 0x00, 0x16, 0xb0,
0xe1, 0x45, 0x7e, 0x89, 0x9e, 0x65, 0x3a, 0x5c);
/* 5c3a659e-897e-45e1-b016-007107c96df7 */
static const ble_uuid128_t gatt_svr_chr_sec_test_static_uuid =
BLE_UUID128_INIT(0xf7, 0x6d, 0xc9, 0x07, 0x71, 0x00, 0x16, 0xb0,
0xe1, 0x45, 0x7e, 0x89, 0x9e, 0x65, 0x3a, 0x5c);
static uint8_t gatt_svr_sec_test_static_val;
static int
gatt_svr_chr_access_sec_test(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg);
static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
{
/*** Service: Security test. */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = &gatt_svr_svc_sec_test_uuid.u,
.characteristics = (struct ble_gatt_chr_def[]) { {
/*** Characteristic: Random number generator. */
.uuid = &gatt_svr_chr_sec_test_rand_uuid.u,
.access_cb = gatt_svr_chr_access_sec_test,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_READ_ENC,
}, {
/*** Characteristic: Static value. */
.uuid = &gatt_svr_chr_sec_test_static_uuid.u,
.access_cb = gatt_svr_chr_access_sec_test,
.flags = BLE_GATT_CHR_F_READ |
BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_WRITE_ENC,
}, {
0, /* No more characteristics in this service. */
} },
},
{
0, /* No more services. */
},
};
static int
gatt_svr_chr_write(struct os_mbuf *om, uint16_t min_len, uint16_t max_len,
void *dst, uint16_t *len)
{
uint16_t om_len;
int rc;
om_len = OS_MBUF_PKTLEN(om);
if (om_len < min_len || om_len > max_len) {
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
rc = ble_hs_mbuf_to_flat(om, dst, max_len, len);
if (rc != 0) {
return BLE_ATT_ERR_UNLIKELY;
}
return 0;
}
static int
gatt_svr_chr_access_sec_test(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg)
{
const ble_uuid_t *uuid;
int rand_num;
int rc;
uuid = ctxt->chr->uuid;
/* Determine which characteristic is being accessed by examining its
* 128-bit UUID.
*/
if (ble_uuid_cmp(uuid, &gatt_svr_chr_sec_test_rand_uuid.u) == 0) {
assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
/* Respond with a 32-bit random number. */
rand_num = rand();
rc = os_mbuf_append(ctxt->om, &rand_num, sizeof rand_num);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
if (ble_uuid_cmp(uuid, &gatt_svr_chr_sec_test_static_uuid.u) == 0) {
switch (ctxt->op) {
case BLE_GATT_ACCESS_OP_READ_CHR:
rc = os_mbuf_append(ctxt->om, &gatt_svr_sec_test_static_val,
sizeof gatt_svr_sec_test_static_val);
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
case BLE_GATT_ACCESS_OP_WRITE_CHR:
rc = gatt_svr_chr_write(ctxt->om,
sizeof gatt_svr_sec_test_static_val,
sizeof gatt_svr_sec_test_static_val,
&gatt_svr_sec_test_static_val, NULL);
return rc;
default:
assert(0);
return BLE_ATT_ERR_UNLIKELY;
}
}
/* Unknown characteristic; the nimble stack should not have called this
* function.
*/
assert(0);
return BLE_ATT_ERR_UNLIKELY;
}
void
gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
{
char buf[BLE_UUID_STR_LEN];
switch (ctxt->op) {
case BLE_GATT_REGISTER_OP_SVC:
MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n",
ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf),
ctxt->svc.handle);
break;
case BLE_GATT_REGISTER_OP_CHR:
MODLOG_DFLT(DEBUG, "registering characteristic %s with "
"def_handle=%d val_handle=%d\n",
ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf),
ctxt->chr.def_handle,
ctxt->chr.val_handle);
break;
case BLE_GATT_REGISTER_OP_DSC:
MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n",
ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf),
ctxt->dsc.handle);
break;
default:
assert(0);
break;
}
}
int
gatt_svr_init(void)
{
int rc;
rc = ble_gatts_count_cfg(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
rc = ble_gatts_add_svcs(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
return 0;
}

View file

@ -0,0 +1,359 @@
/*
* 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 "bsp/bsp.h"
#include "hal/hal_gpio.h"
#include "console/console.h"
#include "hal/hal_system.h"
#include "config/config.h"
#include "split/split.h"
#if MYNEWT_VAL(BLE_SVC_DIS_FIRMWARE_REVISION_READ_PERM) >= 0
#include "bootutil/image.h"
#include "imgmgr/imgmgr.h"
#include "services/dis/ble_svc_dis.h"
#endif
/* BLE */
#include "nimble/ble.h"
#include "host/ble_hs.h"
#include "host/util/util.h"
#include "services/gap/ble_svc_gap.h"
/* Application-specified header. */
#include "bleprph.h"
static int bleprph_gap_event(struct ble_gap_event *event, void *arg);
/**
* Logs information about a connection to the console.
*/
static void
bleprph_print_conn_desc(struct ble_gap_conn_desc *desc)
{
MODLOG_DFLT(INFO, "handle=%d our_ota_addr_type=%d our_ota_addr=",
desc->conn_handle, desc->our_ota_addr.type);
print_addr(desc->our_ota_addr.val);
MODLOG_DFLT(INFO, " our_id_addr_type=%d our_id_addr=",
desc->our_id_addr.type);
print_addr(desc->our_id_addr.val);
MODLOG_DFLT(INFO, " peer_ota_addr_type=%d peer_ota_addr=",
desc->peer_ota_addr.type);
print_addr(desc->peer_ota_addr.val);
MODLOG_DFLT(INFO, " peer_id_addr_type=%d peer_id_addr=",
desc->peer_id_addr.type);
print_addr(desc->peer_id_addr.val);
MODLOG_DFLT(INFO, " conn_itvl=%d conn_latency=%d supervision_timeout=%d "
"encrypted=%d authenticated=%d bonded=%d\n",
desc->conn_itvl, desc->conn_latency,
desc->supervision_timeout,
desc->sec_state.encrypted,
desc->sec_state.authenticated,
desc->sec_state.bonded);
}
/**
* Enables advertising with the following parameters:
* o General discoverable mode.
* o Undirected connectable mode.
*/
static void
bleprph_advertise(void)
{
uint8_t own_addr_type;
struct ble_gap_adv_params adv_params;
struct ble_hs_adv_fields fields;
const char *name;
int rc;
/* Figure out address to use while advertising (no privacy for now) */
rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
return;
}
/**
* Set the advertisement data included in our advertisements:
* o Flags (indicates advertisement type and other general info).
* o Advertising tx power.
* o Device name.
* o 16-bit service UUIDs (alert notifications).
*/
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 assiging 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;
name = ble_svc_gap_device_name();
fields.name = (uint8_t *)name;
fields.name_len = strlen(name);
fields.name_is_complete = 1;
fields.uuids16 = (ble_uuid16_t[]){
BLE_UUID16_INIT(GATT_SVR_SVC_ALERT_UUID)
};
fields.num_uuids16 = 1;
fields.uuids16_is_complete = 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(own_addr_type, NULL, BLE_HS_FOREVER,
&adv_params, bleprph_gap_event, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc);
return;
}
}
/**
* The nimble host executes this callback when a GAP event occurs. The
* application associates a GAP event callback with each connection that forms.
* bleprph uses the same callback for all connections.
*
* @param event The type of event being signalled.
* @param ctxt Various information pertaining to the event.
* @param arg Application-specified argument; unuesd by
* bleprph.
*
* @return 0 if the application successfully handled the
* event; nonzero on failure. The semantics
* of the return code is specific to the
* particular GAP event being signalled.
*/
static int
bleprph_gap_event(struct ble_gap_event *event, void *arg)
{
struct ble_gap_conn_desc desc;
int rc;
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 ",
event->connect.status == 0 ? "established" : "failed",
event->connect.status);
if (event->connect.status == 0) {
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
assert(rc == 0);
bleprph_print_conn_desc(&desc);
#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT)
phy_conn_changed(event->connect.conn_handle);
#endif
}
MODLOG_DFLT(INFO, "\n");
if (event->connect.status != 0) {
/* Connection failed; resume advertising. */
bleprph_advertise();
}
return 0;
case BLE_GAP_EVENT_DISCONNECT:
MODLOG_DFLT(INFO, "disconnect; reason=%d ", event->disconnect.reason);
bleprph_print_conn_desc(&event->disconnect.conn);
MODLOG_DFLT(INFO, "\n");
#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT)
phy_conn_changed(CONN_HANDLE_INVALID);
#endif
/* Connection terminated; resume advertising. */
bleprph_advertise();
return 0;
case BLE_GAP_EVENT_CONN_UPDATE:
/* The central has updated the connection parameters. */
MODLOG_DFLT(INFO, "connection updated; status=%d ",
event->conn_update.status);
rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc);
assert(rc == 0);
bleprph_print_conn_desc(&desc);
MODLOG_DFLT(INFO, "\n");
return 0;
case BLE_GAP_EVENT_ADV_COMPLETE:
MODLOG_DFLT(INFO, "advertise complete; reason=%d",
event->adv_complete.reason);
bleprph_advertise();
return 0;
case BLE_GAP_EVENT_ENC_CHANGE:
/* Encryption has been enabled or disabled for this connection. */
MODLOG_DFLT(INFO, "encryption change event; status=%d ",
event->enc_change.status);
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
assert(rc == 0);
bleprph_print_conn_desc(&desc);
MODLOG_DFLT(INFO, "\n");
return 0;
case BLE_GAP_EVENT_SUBSCRIBE:
MODLOG_DFLT(INFO, "subscribe event; conn_handle=%d attr_handle=%d "
"reason=%d prevn=%d curn=%d previ=%d curi=%d\n",
event->subscribe.conn_handle,
event->subscribe.attr_handle,
event->subscribe.reason,
event->subscribe.prev_notify,
event->subscribe.cur_notify,
event->subscribe.prev_indicate,
event->subscribe.cur_indicate);
return 0;
case BLE_GAP_EVENT_MTU:
MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d cid=%d mtu=%d\n",
event->mtu.conn_handle,
event->mtu.channel_id,
event->mtu.value);
return 0;
case BLE_GAP_EVENT_REPEAT_PAIRING:
/* We already have a bond with the peer, but it is attempting to
* establish a new secure link. This app sacrifices security for
* convenience: just throw away the old bond and accept the new link.
*/
/* Delete the old bond. */
rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
assert(rc == 0);
ble_store_util_delete_peer(&desc.peer_id_addr);
/* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should
* continue with the pairing operation.
*/
return BLE_GAP_REPEAT_PAIRING_RETRY;
#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT)
case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE:
/* XXX: assume symmetric phy for now */
phy_update(event->phy_updated.tx_phy);
return 0;
#endif
}
return 0;
}
static void
bleprph_on_reset(int reason)
{
MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
}
static void
bleprph_on_sync(void)
{
int rc;
/* Make sure we have proper identity address set (public preferred) */
rc = ble_hs_util_ensure_addr(0);
assert(rc == 0);
/* Begin advertising. */
bleprph_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)
{
#if MYNEWT_VAL(BLE_SVC_DIS_FIRMWARE_REVISION_READ_PERM) >= 0
struct image_version ver;
static char ver_str[IMGMGR_NMGR_MAX_VER];
#endif
int rc;
/* Initialize OS */
sysinit();
/* Initialize the NimBLE host configuration. */
ble_hs_cfg.reset_cb = bleprph_on_reset;
ble_hs_cfg.sync_cb = bleprph_on_sync;
ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
rc = gatt_svr_init();
assert(rc == 0);
#if MYNEWT_VAL(BLE_SVC_DIS_FIRMWARE_REVISION_READ_PERM) >= 0
/* Set firmware version in DIS */
imgr_my_version(&ver);
imgr_ver_str(&ver, ver_str);
ble_svc_dis_firmware_revision_set(ver_str);
#endif
#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT)
phy_init();
#endif
conf_load();
/* If this app is acting as the loader in a split image setup, jump into
* the second stage application instead of starting the OS.
*/
#if MYNEWT_VAL(SPLIT_LOADER)
{
void *entry;
rc = split_app_go(&entry, true);
if (rc == 0) {
hal_system_start(entry);
}
}
#endif
/*
* As the last thing, process events from default event queue.
*/
while (1) {
os_eventq_run(os_eventq_dflt_get());
}
return 0;
}

View file

@ -0,0 +1,43 @@
/*
* 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 "bleprph.h"
/**
* Utility function to log an array of bytes.
*/
void
print_bytes(const uint8_t *bytes, int len)
{
int i;
for (i = 0; i < len; i++) {
MODLOG_DFLT(INFO, "%s0x%02x", i != 0 ? ":" : "", bytes[i]);
}
}
void
print_addr(const void *addr)
{
const uint8_t *u8p;
u8p = addr;
MODLOG_DFLT(INFO, "%02x:%02x:%02x:%02x:%02x:%02x",
u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]);
}

View file

@ -0,0 +1,128 @@
/*
* 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 "os/mynewt.h"
#include "bsp/bsp.h"
#include "hal/hal_gpio.h"
#include "host/ble_gap.h"
#include "bleprph.h"
#if MYNEWT_VAL(BLEPRPH_LE_PHY_SUPPORT)
static const int button_gpio[4] = MYNEWT_VAL(BLEPRPH_LE_PHY_BUTTON_GPIO);
static const int led_gpio[3] = MYNEWT_VAL(BLEPRPH_LE_PHY_LED_GPIO);
#define PHY_TO_PTR(_mask, _opts) (void *)(((_opts) << 16) | ((_mask)))
#define PTR_TO_PHY_MASK(_ptr) (uint8_t)(((int)_ptr) & 0x0ff)
#define PTR_TO_PHY_OPTS(_ptr) (uint8_t)(((int)_ptr) >> 16)
static struct os_event gpio_event;
static uint16_t conn_handle = CONN_HANDLE_INVALID;
static void
gpio_irq_handler(void *arg)
{
gpio_event.ev_arg = arg;
os_eventq_put(os_eventq_dflt_get(), &gpio_event);
}
static void
gpio_event_handler(struct os_event *ev)
{
uint8_t phy_mask;
uint8_t phy_opts;
int sr;
OS_ENTER_CRITICAL(sr);
phy_mask = PTR_TO_PHY_MASK(ev->ev_arg);
phy_opts = PTR_TO_PHY_OPTS(ev->ev_arg);
OS_EXIT_CRITICAL(sr);
if (conn_handle != CONN_HANDLE_INVALID) {
ble_gap_set_prefered_le_phy(conn_handle, phy_mask, phy_mask, phy_opts);
}
}
static void
setup_button_gpio(int button, uint8_t phy_mask, uint8_t phy_opts)
{
if (button < 0) {
return;
}
hal_gpio_irq_init(button, gpio_irq_handler, PHY_TO_PTR(phy_mask, phy_opts),
HAL_GPIO_TRIG_FALLING, HAL_GPIO_PULL_UP);
hal_gpio_irq_enable(button);
}
void
phy_init(void)
{
gpio_event.ev_cb = gpio_event_handler;
/*
* XXX: we could make this configurable, but for now assume all pins are
* valid, buttons gpio pins are pulled-up and LEDs are active-low - this
* is valid for nRF52840 PDK.
*/
setup_button_gpio(button_gpio[0], BLE_GAP_LE_PHY_1M_MASK,
BLE_GAP_LE_PHY_CODED_ANY);
setup_button_gpio(button_gpio[1], BLE_GAP_LE_PHY_2M_MASK,
BLE_GAP_LE_PHY_CODED_ANY);
setup_button_gpio(button_gpio[2], BLE_GAP_LE_PHY_CODED_MASK,
BLE_GAP_LE_PHY_CODED_S2);
setup_button_gpio(button_gpio[3], BLE_GAP_LE_PHY_CODED_MASK,
BLE_GAP_LE_PHY_CODED_S8);
hal_gpio_init_out(led_gpio[0], 1);
hal_gpio_init_out(led_gpio[1], 1);
hal_gpio_init_out(led_gpio[2], 1);
}
void
phy_conn_changed(uint16_t handle)
{
uint8_t phy = 0;
conn_handle = handle;
if (handle != CONN_HANDLE_INVALID) {
/* XXX: assume symmetric phy for now */
ble_gap_read_le_phy(handle, &phy, &phy);
}
phy_update(phy);
}
void
phy_update(uint8_t phy)
{
if (conn_handle == CONN_HANDLE_INVALID) {
hal_gpio_write(led_gpio[0], 1);
hal_gpio_write(led_gpio[1], 1);
hal_gpio_write(led_gpio[2], 1);
} else {
hal_gpio_write(led_gpio[0], !(phy == BLE_GAP_LE_PHY_1M));
hal_gpio_write(led_gpio[1], !(phy == BLE_GAP_LE_PHY_2M));
hal_gpio_write(led_gpio[2], !(phy == BLE_GAP_LE_PHY_CODED));
}
}
#endif

View file

@ -0,0 +1,68 @@
# 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.
#
syscfg.defs:
BLEPRPH_LE_PHY_SUPPORT:
description: >
Enable support for changing PHY preference on active connection.
PHY preference change is triggered by configured GPIO pins.
Current PHY is indicated using LEDs connected to configured
GPIO pins.
value: 0
BLEPRPH_LE_PHY_BUTTON_GPIO:
description: >
GPIO pins for changing PHY preference on active connection. This
is an array of 4 GPIO pin numbers for 1M, 2M, LE Coded S=2 and
LE Coded S=8 respectively.
value: "(int[]){ BUTTON_1, BUTTON_2, BUTTON_3, BUTTON_4 }"
BLEPRPH_LE_PHY_LED_GPIO:
description: >
GPIO pins for indicating current PHY on active connection. This
is an array of 3 GPIO pin numbers for 1M, 2M and LE Coded
respectively.
value: "(int[]){ LED_1, LED_2, LED_3 }"
syscfg.vals:
# Disable central and observer roles.
BLE_ROLE_BROADCASTER: 1
BLE_ROLE_CENTRAL: 0
BLE_ROLE_OBSERVER: 0
BLE_ROLE_PERIPHERAL: 1
# Configure DIS
BLE_SVC_DIS_FIRMWARE_REVISION_READ_PERM: 1
# Log reboot messages to a flash circular buffer.
REBOOT_LOG_FCB: 1
LOG_FCB: 1
CONFIG_FCB: 1
# Enable smp commands.
STATS_MGMT: 1
LOG_MGMT: 1
CONFIG_MGMT: 1
# OS main/default task
OS_MAIN_STACK_SIZE: 512
# Lots of smaller mbufs are required for smp using typical BLE ATT MTU
# values.
MSYS_1_BLOCK_COUNT: 22
MSYS_1_BLOCK_SIZE: 110
BLE_SVC_GAP_DEVICE_NAME: '"nimble-bleprph"'

View file

@ -0,0 +1,201 @@
BLE Stress Tests
******************************************************************************
QUICK TIPS:
You need 2 controllers supported by MyNewt. One will become TX device,
the other will become RX device.
1. Set role (TX=0, RX=1) for current device in syscfg.yml
BLE_STRESS_TEST_ROLE: 1
The RX has LED2 turned on.
2. Set (in syscfg.yml) number of times to repeat each tested action
in use case, e.g. do 1000 times connect/disconnect to complete use case.
RX_STRESS_REPEAT: 1000
3. To perform only specific test, go to rx_stress.c,
find definition of rx_stress_main_task_fn(void *arg) and in place of
for-loop just paste function rx_stress_start(i), where i is id of test.
******************************************************************************
No | Use case
-----------------------------------------------------------------------------
1 | Stress Connect -> Connect Cancel - repeat 1000
| RX: Nothing
| TX: Connect/Connect cancel
|
2 | Stress Connect/Disconnect legacy - repeat 1000
| RX: Advertise legacy
| TX: Connect/Disconnect
|
3 | Stress Connect/Disconnect ext adv - repeat 1000
| RX: Advertise Ext
| TX: Connect/Disconnect
|
4 | Stress connection params update (TX) - (1000)
| RX: Advertise
| TX: Connect/Connect param update
|
5 | Stress connection params update (RX) - (1000)
| RX: Advertise/Connect param update
| TX: Connect
|
6 | Stress Scan
| RX: Advertise a known data pattern
| TX: Scan and check received data with pattern
|
7 | Stress PHY Update (TX) - (1000)
| RX: Advertise
| TX: Connect/Phy update
|
8 | Stress PHY update (RX) - (1000)
| RX: Advertise/Phy update
| TX: Connect
|
9 | Stress multi connection
| RX: Advertise Ext
| TX: Establish and maintain as many instances as possible
|
10 | Stress L2CAP send
| RX: Send 64kB of data with L2CAP
| TX: Measure bit rate and max received data MTU
|
11 | Stress Advertise/Connect/Continue Adv/Disconnect
| RX: Advertise Ext/Continue same advertise after connect
| TX: Connect
|
12 | Stress GATT indicating
| RX: Indicate
| TX: Receive indication. Measure average time of indicating.
|
13 | Stress GATT notification
| RX: Notify. Measure average time of notifying.
| TX: Count the number of received notification.
|
14 | Stress GATT Subscribe/Notify/Unsubscribe
| RX: Notify on subscribe
| TX: Measure the average time from sending a subscription request
| to receiving a notification.
|
15 | Stress Connect/Send/Disconnect
| RX: Advertise/Send via ATT/Disconnect
| TX: Receive notification. Measure time of notifying.
******************************************************************************
Concept:
The RX device advertises data containing a UUID128 of test use case that
should be performed. The TX device scan for known UUIDs, when it finds,
adapts to the advertised use case and runs a test.
Stress Task vs Semaphore:
The rx_stress_start_auto function starts main stress test task that runs
all stress tests one by one. The tests are based on event handling, so to
synchronize main task with events, a semaphore is used. On use case start
there is 0 tokens for semaphore. After main task starts one of use cases,
it comes across a semaphore and has to wait. When use case is completed,
1 token is released, so main task can use it to pass through semaphore.
The tx_stress_start_auto function works analogically.
Newt target set example:
rymek@rymek:~/projects/bletiny_proj$ newt target show bletest_tx
targets/bletest_tx
app=@apache-mynewt-nimble/apps/blestress
bsp=@apache-mynewt-core/hw/bsp/nordic_pca10056
build_profile=debug
syscfg=BLE_STRESS_TEST_ROLE=0
rymek@rymek:~/projects/bletiny_proj$ newt target show bletest_rx
targets/bletest_rx
app=@apache-mynewt-nimble/apps/blestress
bsp=@apache-mynewt-core/hw/bsp/nordic_pca10056
build_profile=debug
syscfg=BLE_STRESS_TEST_ROLE=1
Example of expected logs on TX side(LOG_LEVEL > 1):
Start test num 1
>>>>>>>>>>>>>>>>>>>> Stress test 1 completed
Start scan for test
Start test num 2
>>>>>>>>>>>>>>>>>>>> Stress test 2 completed
Start scan for test
Start test num 3
>>>>>>>>>>>>>>>>>>>> Stress test 3 completed
Start scan for test
Start test num 4
>>>>>>>>>>>>>>>>>>>> Stress test 4 completed
Start scan for test
Start test num 5
>>>>>>>>>>>>>>>>>>>> Stress test 5 completed
Start scan for test
Start test num 6
>>>>>>>>>>>>>>>>>>>> Stress test 6 completed
Start scan for test
Start test num 7
>>>>>>>>>>>>>>>>>>>> Stress test 7 completed
Start scan for test
Start test num 8
>>>>>>>>>>>>>>>>>>>> Stress test 8 completed
All tests completed
Tests results:
Use case 1 - Stress Connect -> Connect Cancel:
Con attempts = 20
Con success = 0
Use case 2 - Stress Connect/Disconnect legacy:
Con attempts = 20
Con success = 20
Use case 3 - Stress Connect/Disconnect ext adv:
Con attempts = 20
Con success = 20
Use case 4 - Stress connection params update (TX):
Params updates = 20
Use case 5 - Stress connection params update (RX):
Params updates = 20
Use case 6 - Stress Scan:
Received first packets = 20
Received all packets = 20
Use case 7 - Stress PHY Update (TX):
PHY updates = 20
Use case 8 - Stress Connect -> Connect Cancel:
PHY updates = 20
Example of expected logs on RX side(LOG_LEVEL > 1):
Start test num 2
>>>>>>>>>>>>>>>>>>>> Stress test 2 completed
Start test num 3
>>>>>>>>>>>>>>>>>>>> Stress test 3 completed
Start test num 4
>>>>>>>>>>>>>>>>>>>> Stress test 4 completed
Start test num 5
>>>>>>>>>>>>>>>>>>>> Stress test 5 completed
Start test num 6
Received signal to switch test
Start test num 7
>>>>>>>>>>>>>>>>>>>> Stress test 7 completed
Start test num 8
>>>>>>>>>>>>>>>>>>>> Stress test 8 completed
All tests completed
Tests results:
Use case 1 - Stress Connect -> Connect Cancel:
Con attempts = 0
Con success = 0
Use case 2 - Stress Connect/Disconnect legacy:
Con attempts = 20
Con success = 20
Use case 3 - Stress Connect/Disconnect ext adv:
Con attempts = 20
Con success = 20
Use case 4 - Stress connection params update (TX):
Params updates = 20
Use case 5 - Stress connection params update (RX):
Params updates = 20
Use case 6 - Stress Scan:
Received first packets = 0
Received all packets = 0
Use case 7 - Stress PHY Update (TX):
PHY updates = 20
Use case 8 - Stress Connect -> Connect Cancel:
PHY updates = 20

View file

@ -0,0 +1,39 @@
#
# 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.
#
pkg.name: "apps/blestress"
pkg.type: app
pkg.description: "Stress tests sample application."
pkg.keywords:
pkg.deps:
- "@mcuboot/boot/bootutil"
- "@apache-mynewt-core/sys/log/modlog"
- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/sys/console/full"
- "@apache-mynewt-core/sys/log/full"
- "@apache-mynewt-core/sys/stats/full"
- "@apache-mynewt-core/sys/id"
- "@apache-mynewt-nimble/nimble/controller"
- "@apache-mynewt-nimble/nimble/host"
- "@apache-mynewt-nimble/nimble/host/util"
- "@apache-mynewt-nimble/nimble/host/services/gap"
- "@apache-mynewt-nimble/nimble/host/services/gatt"
- "@apache-mynewt-nimble/nimble/host/store/ram"
- "@apache-mynewt-nimble/nimble/transport/ram"

View file

@ -0,0 +1,99 @@
/*
* 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 <stdio.h>
#include "os/mynewt.h"
#include "config/config.h"
#include "bsp.h"
#include "hal/hal_gpio.h"
/* BLE */
#include "nimble/ble.h"
#include "host/ble_hs.h"
#include "host/util/util.h"
#include "services/gap/ble_svc_gap.h"
/* Application-specified header. */
#include "rx_stress.h"
#include "tx_stress.h"
#include "stress_gatt.h"
static void
stress_test_on_reset(int reason)
{
MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
}
static void
stress_test_on_sync(void)
{
int rc;
/* Make sure we have proper identity address set (public preferred) */
rc = ble_hs_util_ensure_addr(1);
assert(rc == 0);
#if MYNEWT_VAL(BLE_STRESS_TEST_ROLE)
rx_stress_start_auto();
#else
tx_stress_start_auto();
#endif
}
/**
* 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;
sysinit();
ble_hs_cfg.reset_cb = stress_test_on_reset;
ble_hs_cfg.sync_cb = stress_test_on_sync;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
/* Please do not change name. Otherwise some tests could fail. */
rc = ble_svc_gap_device_name_set("STRESS");
assert(rc == 0);
conf_load();
rc = gatt_svr_init();
assert(rc == 0);
#if MYNEWT_VAL(BLE_STRESS_TEST_ROLE)
/* RX device */
ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb;
hal_gpio_init_out(LED_2, 1);
hal_gpio_toggle(LED_2);
#endif
while (1) {
os_eventq_run(os_eventq_dflt_get());
}
return 0;
}

View file

@ -0,0 +1,224 @@
/*
* 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 "misc.h"
void
rand_bytes(uint8_t *data, int len) {
int i;
for (i = 0; i < len; ++i) {
data[i] = (uint8_t) rand() % UINT8_MAX;
}
}
/**
* Utility function to log an array of bytes.
*/
void
print_bytes(const uint8_t *bytes, int len)
{
int i;
for (i = 0; i < len; i++) {
MODLOG_DFLT(DEBUG, "%s0x%02x", i != 0 ? ":" : "", bytes[i]);
}
}
void
print_addr(const void *addr)
{
const uint8_t *u8p;
u8p = addr;
MODLOG_DFLT(DEBUG, "%02x:%02x:%02x:%02x:%02x:%02x",
u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]);
}
void
print_mbuf(const struct os_mbuf *om)
{
while (om != NULL) {
print_bytes(om->om_data, om->om_len);
om = SLIST_NEXT(om, om_next);
if(om == NULL) {
return;
}
/* Separate buf fields with colon to maintain continuity */
MODLOG_DFLT(DEBUG, ":");
}
}
char *
addr_str(const void *addr)
{
/* 6 bytes of MAC address * 2 signs for each byte in string + 5 colons to
* separate bytes + 1 byte for null-character appended by sprintf
*/
static char buf[6 * 2 + 5 + 1];
const uint8_t *u8p;
u8p = addr;
sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
u8p[5], u8p[4], u8p[3], u8p[2], u8p[1], u8p[0]);
return buf;
}
void
print_uuid(const ble_uuid_t *uuid)
{
char buf[BLE_UUID_STR_LEN];
MODLOG_DFLT(DEBUG, "%s", ble_uuid_to_str(uuid, buf));
}
/**
* Logs information about a connection to the console.
*/
void
print_conn_desc(const struct ble_gap_conn_desc *desc)
{
MODLOG_DFLT(DEBUG, "handle=%d our_ota_addr_type=%d our_ota_addr=%s ",
desc->conn_handle, desc->our_ota_addr.type,
addr_str(desc->our_ota_addr.val));
MODLOG_DFLT(DEBUG, "our_id_addr_type=%d our_id_addr=%s ",
desc->our_id_addr.type, addr_str(desc->our_id_addr.val));
MODLOG_DFLT(DEBUG, "peer_ota_addr_type=%d peer_ota_addr=%s ",
desc->peer_ota_addr.type, addr_str(desc->peer_ota_addr.val));
MODLOG_DFLT(DEBUG, "peer_id_addr_type=%d peer_id_addr=%s ",
desc->peer_id_addr.type, addr_str(desc->peer_id_addr.val));
MODLOG_DFLT(DEBUG, "conn_itvl=%d conn_latency=%d supervision_timeout=%d "
"encrypted=%d authenticated=%d bonded=%d",
desc->conn_itvl, desc->conn_latency,
desc->supervision_timeout,
desc->sec_state.encrypted,
desc->sec_state.authenticated,
desc->sec_state.bonded);
}
void
print_adv_fields(const struct ble_hs_adv_fields *fields)
{
char s[BLE_HS_ADV_MAX_SZ];
const uint8_t *u8p;
int i;
if (fields->flags != 0) {
MODLOG_DFLT(DEBUG, " flags=0x%02x\n", fields->flags);
}
if (fields->uuids16 != NULL) {
MODLOG_DFLT(DEBUG, " uuids16(%scomplete)=",
fields->uuids16_is_complete ? "" : "in");
for (i = 0; i < fields->num_uuids16; i++) {
print_uuid(&fields->uuids16[i].u);
MODLOG_DFLT(DEBUG, " ");
}
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->uuids32 != NULL) {
MODLOG_DFLT(DEBUG, " uuids32(%scomplete)=",
fields->uuids32_is_complete ? "" : "in");
for (i = 0; i < fields->num_uuids32; i++) {
print_uuid(&fields->uuids32[i].u);
MODLOG_DFLT(DEBUG, " ");
}
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->uuids128 != NULL) {
MODLOG_DFLT(DEBUG, " uuids128(%scomplete)=",
fields->uuids128_is_complete ? "" : "in");
for (i = 0; i < fields->num_uuids128; i++) {
print_uuid(&fields->uuids128[i].u);
MODLOG_DFLT(DEBUG, " ");
}
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->name != NULL) {
assert(fields->name_len < sizeof s - 1);
memcpy(s, fields->name, fields->name_len);
s[fields->name_len] = '\0';
MODLOG_DFLT(DEBUG, " name(%scomplete)=%s\n",
fields->name_is_complete ? "" : "in", s);
}
if (fields->tx_pwr_lvl_is_present) {
MODLOG_DFLT(DEBUG, " tx_pwr_lvl=%d\n", fields->tx_pwr_lvl);
}
if (fields->slave_itvl_range != NULL) {
MODLOG_DFLT(DEBUG, " slave_itvl_range=");
print_bytes(fields->slave_itvl_range, BLE_HS_ADV_SLAVE_ITVL_RANGE_LEN);
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->svc_data_uuid16 != NULL) {
MODLOG_DFLT(DEBUG, " svc_data_uuid16=");
print_bytes(fields->svc_data_uuid16, fields->svc_data_uuid16_len);
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->public_tgt_addr != NULL) {
MODLOG_DFLT(DEBUG, " public_tgt_addr=");
u8p = fields->public_tgt_addr;
for (i = 0; i < fields->num_public_tgt_addrs; i++) {
MODLOG_DFLT(DEBUG, "public_tgt_addr=%s ", addr_str(u8p));
u8p += BLE_HS_ADV_PUBLIC_TGT_ADDR_ENTRY_LEN;
}
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->appearance_is_present) {
MODLOG_DFLT(DEBUG, " appearance=0x%04x\n", fields->appearance);
}
if (fields->adv_itvl_is_present) {
MODLOG_DFLT(DEBUG, " adv_itvl=0x%04x\n", fields->adv_itvl);
}
if (fields->svc_data_uuid32 != NULL) {
MODLOG_DFLT(DEBUG, " svc_data_uuid32=");
print_bytes(fields->svc_data_uuid32, fields->svc_data_uuid32_len);
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->svc_data_uuid128 != NULL) {
MODLOG_DFLT(DEBUG, " svc_data_uuid128=");
print_bytes(fields->svc_data_uuid128, fields->svc_data_uuid128_len);
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->uri != NULL) {
MODLOG_DFLT(DEBUG, " uri=");
print_bytes(fields->uri, fields->uri_len);
MODLOG_DFLT(DEBUG, "\n");
}
if (fields->mfg_data != NULL) {
MODLOG_DFLT(DEBUG, " mfg_data=");
print_bytes(fields->mfg_data, fields->mfg_data_len);
MODLOG_DFLT(DEBUG, "\n");
}
}

View file

@ -0,0 +1,54 @@
/*
* 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.
*/
#ifndef BLE_TGT_MISC_H
#define BLE_TGT_MISC_H
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "host/ble_hs.h"
#include "host/ble_uuid.h"
#include "host/ble_hs_adv.h"
#ifdef __cplusplus
extern "C" {
#endif
void rand_bytes(uint8_t *data, int len);
void print_bytes(const uint8_t *bytes, int len);
void print_addr(const void *addr);
void print_mbuf(const struct os_mbuf *om);
char *addr_str(const void *addr);
void print_uuid(const ble_uuid_t *uuid);
void print_conn_desc(const struct ble_gap_conn_desc *desc);
void print_adv_fields(const struct ble_hs_adv_fields *fields);
#ifdef __cplusplus
}
#endif
#endif //BLE_TGT_MISC_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,53 @@
/*
* 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.
*/
#ifndef _BLE_STRESS_RX_H
#define _BLE_STRESS_RX_H
#include <assert.h>
#include <string.h>
#include <console/console.h>
#include <errno.h>
#include <nrfx/hal/nrf_aar.h>
/* BLE */
#include "nimble/ble.h"
#include "host/ble_hs.h"
#include "services/gap/ble_svc_gap.h"
#include "host/ble_gap.h"
#include <host/ble_l2cap.h>
#include "misc.h"
#include "stress.h"
#include "stress_gatt.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* Executes stress tests one by one.
*/
void rx_stress_start_auto();
#ifdef __cplusplus
}
#endif
#endif //_BLE_STRESS_RX_H

View file

@ -0,0 +1,389 @@
/*
* 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 "stress.h"
void
com_stress_print_report(const struct com_stress_test_ctx *test_ctxs)
{
console_printf("\033[0;32mAll tests completed\033[0m\n");
console_printf("Tests results:\n");
console_printf(
"\033[0;33mUse case 1 - Stress Connect -> Connect Cancel: \n\033[0m");
console_printf("Con attempts = %d\n", test_ctxs->con_stat[1].attempts_num);
console_printf("Con success = %d\n", test_ctxs->con_stat[1].num);
console_printf(
"\033[0;33mUse case 2 - Stress Connect/Disconnect legacy: \n\033[0m");
console_printf("Con attempts = %d\n", test_ctxs->con_stat[2].attempts_num);
console_printf("Con success = %d\n", test_ctxs->con_stat[2].num);
console_printf(
"\033[0;33mUse case 3 - Stress Connect/Disconnect ext adv: \n\033[0m");
console_printf("Con attempts = %d\n", test_ctxs->con_stat[3].attempts_num);
console_printf("Con success = %d\n", test_ctxs->con_stat[3].num);
console_printf(
"\033[0;33mUse case 4 - Stress connection params update (TX): \n\033[0m");
console_printf("Params updates = %d\n",
test_ctxs->con_stat[4].prms_upd_num);
console_printf(
"\033[0;33mUse case 5 - Stress connection params update (RX): \n\033[0m");
console_printf("Params updates = %d\n",
test_ctxs->con_stat[5].prms_upd_num);
console_printf("\033[0;33mUse case 6 - Stress Scan: \n\033[0m");
console_printf("Received first packets = %d\n",
test_ctxs->s6_rcv_adv_first);
console_printf("Received all packets = %d\n", test_ctxs->s6_rcv_adv_suc);
console_printf("\033[0;33mUse case 7 - Stress PHY Update (TX): \n\033[0m");
console_printf("PHY updates = %d\n", test_ctxs->con_stat[7].phy_upd_num);
console_printf("\033[0;33mUse case 8 - Stress PHY Update (RX): \n\033[0m");
console_printf("PHY updates = %d\n", test_ctxs->con_stat[8].phy_upd_num);
console_printf(
"\033[0;33mUse case 9 - Stress multi connection: \n\033[0m");
console_printf("Max reached num of connections = %d\n",
test_ctxs->con_stat[9].max_num);
console_printf("\033[0;33mUse case 10 - Stress L2CAP send: \n\033[0m");
console_printf("Average bit rate = %d\n", test_ctxs->s10_bit_rate);
console_printf("Max received MTU = %lld\n", test_ctxs->s10_max_mtu);
console_printf("\033[0;33mUse case 11 - "
"Stress Advertise/Connect/Continue adv \n\033[0m");
// console_printf(" = %d\n",);
console_printf("\033[0;33mUse case 12 - "
"Stress GATT indication: \n\033[0m");
console_printf("Average bit rate = %d\n", test_ctxs->s12_notif_time);
console_printf("\033[0;33mUse case 13 - "
"Stress GATT notification: \n\033[0m");
console_printf("Average time = %d\n", test_ctxs->s13_notif_time);
console_printf("\033[0;33mUse case 14 - "
"Stress GATT Subscribe/Notify/Unsubscribe: \n\033[0m");
console_printf("Average time = %d\n", test_ctxs->s14_notif_time);
console_printf("\033[0;33mUse case 15 - "
"Stress Connect/Send/Disconnect: \n\033[0m");
console_printf("Con num = %d\n", test_ctxs->con_stat[15].num);
}
void
stress_clear_ctx_reusable_var(struct com_stress_test_ctx *ctx)
{
ctx->cur_test_id = 0;
ctx->dev_addr.type = 0;
ctx->dev_addr.type = 0;
ctx->conn_handle = 0;
ctx->chan = 0;
ctx->rcv_data_bytes = 0;
ctx->rcv_num = 0;
ctx->send_num = 0;
ctx->begin_us = 0;
ctx->end_us = 0;
ctx->time_sum = 0;
ctx->bytes_sum = 0;
ctx->timeout_flag = 0;
ctx->rcv_data_flag = 0;
}
int
stress_fill_mbuf_with_pattern(struct os_mbuf *om, uint16_t len)
{
int rc, i, mul, rest;
mul = len / STRESS_PAT_LEN;
rest = len % STRESS_PAT_LEN;
for (i = 0; i < mul; ++i) {
rc = os_mbuf_append(om, &test_6_pattern[29], STRESS_PAT_LEN);
if (rc) {
os_mbuf_free_chain(om);
assert(0);
}
}
rc = os_mbuf_append(om, &test_6_pattern[29], rest);
if (rc) {
os_mbuf_free_chain(om);
assert(0);
}
return rc;
}
void
stress_l2cap_coc_recv(struct ble_l2cap_chan *chan, struct os_mbuf *sdu)
{
int rc;
console_printf("LE CoC SDU received, chan: 0x%08lx, data len %d\n",
(uint32_t) chan, OS_MBUF_PKTLEN(sdu));
rc = os_mbuf_free_chain(sdu);
assert(rc == 0);
/* Get buffer for data chain */
sdu = os_msys_get_pkthdr(STRESS_COC_MTU, 0);
assert(sdu != NULL);
/* Receive data chain */
rc = ble_l2cap_recv_ready(chan, sdu);
assert(rc == 0);
}
void
stress_l2cap_coc_accept(uint16_t peer_mtu, struct ble_l2cap_chan *chan)
{
struct os_mbuf *sdu_rx;
int rc;
console_printf("LE CoC accepting, chan: 0x%08lx, peer_mtu %d\n",
(uint32_t) chan, peer_mtu);
sdu_rx = os_msys_get_pkthdr(STRESS_COC_MTU, 0);
assert(sdu_rx != NULL);
rc = ble_l2cap_recv_ready(chan, sdu_rx);
assert(rc == 0);
}
void
stress_start_timer(uint32_t timeout_ms, os_event_fn *ev_cb)
{
int rc;
os_callout_stop(&stress_timer_callout);
os_callout_init(&stress_timer_callout, os_eventq_dflt_get(), ev_cb, NULL);
rc = os_callout_reset(&stress_timer_callout,
os_time_ms_to_ticks32(timeout_ms));
assert(rc == 0);
}
int64_t
stress_calc_bit_rate(int64_t us, int64_t bytes_num)
{
int bit_rate;
/* Multiply by 1000000 so you don't lose accuracy */
bit_rate = 1000000 * bytes_num / us;
return bit_rate;
}
static int
stress_disc_dsc_fn(uint16_t conn_handle,
const struct ble_gatt_error *error,
uint16_t chr_val_handle,
const struct ble_gatt_dsc *dsc,
void *arg)
{
struct stress_gatt_search_ctx *search_ctx;
static bool found = false;
search_ctx = (struct stress_gatt_search_ctx *) arg;
if (error->status == 0) {
if (!ble_uuid_cmp(&dsc->uuid.u, &search_ctx->dsc_uuid.u) && !found) {
MODLOG_DFLT(INFO, "Found chr descriptor\n");
search_ctx->dsc_handle = dsc->handle;
MODLOG_DFLT(INFO, "uuid=%#06x; handle=%#06x", dsc->uuid.u16.value,
dsc->handle);
found = true;
}
return 0;
}
if (error->status == BLE_HS_EDONE) {
MODLOG_DFLT(INFO, "Done descriptor discovery\n");
if (found) {
found = false;
search_ctx->disc_end_fn(search_ctx);
return 0;
}
MODLOG_DFLT(ERROR, "\033[0;31mDid not find particular descriptor"
"\033[0m\n");
return 0;
}
MODLOG_DFLT(ERROR, "\033[0;31mError during descriptor discovery"
"\033[0m\n");
assert(0);
return 0;
}
static int
stress_disc_chr_fn(uint16_t conn_handle,
const struct ble_gatt_error *error,
const struct ble_gatt_chr *chr, void *arg)
{
int rc;
struct stress_gatt_search_ctx *search_ctx;
static bool found = false;
search_ctx = (struct stress_gatt_search_ctx *) arg;
if (error->status == 0) {
MODLOG_DFLT(INFO, "Found characteristic\n");
search_ctx->chr_start_handle = chr->val_handle;
found = true;
return 0;
}
if (error->status == BLE_HS_EDONE) {
MODLOG_DFLT(INFO, "Done characteristic discovery\n");
if (found) {
found = false;
if (search_ctx->search_goal == STRESS_FIND_CHR) {
search_ctx->disc_end_fn(search_ctx);
return 0;
}
rc = ble_gattc_disc_all_dscs(conn_handle,
search_ctx->chr_start_handle,
search_ctx->srv_end_handle,
&stress_disc_dsc_fn, search_ctx);
assert(rc == 0);
return 0;
}
MODLOG_DFLT(ERROR, "\033[0;31mDid not find particular "
"characteristic\033[0m\n");
return 0;
}
MODLOG_DFLT(ERROR,
"\033[0;31mError during characteristic discovery\033[0m\n");
assert(0);
return 0;
}
static int
stress_disc_svc_fn(uint16_t conn_handle, const struct ble_gatt_error *error,
const struct ble_gatt_svc *service, void *arg)
{
int rc;
struct stress_gatt_search_ctx *search_ctx = NULL;
static bool found = false;
search_ctx = (struct stress_gatt_search_ctx *) arg;
if (error->status == 0) {
MODLOG_DFLT(INFO, "Found service\n");
search_ctx->srv_start_handle = service->start_handle;
search_ctx->srv_end_handle = service->end_handle;
found = true;
return 0;
}
if (error->status == BLE_HS_EDONE) {
MODLOG_DFLT(INFO, "Done service discovery\n");
if (found) {
found = false;
if (search_ctx->search_goal == STRESS_FIND_SRV) {
search_ctx->disc_end_fn(search_ctx);
return 0;
}
rc = ble_gattc_disc_chrs_by_uuid(conn_handle,
search_ctx->srv_start_handle,
search_ctx->srv_end_handle,
&search_ctx->chr_uuid.u,
&stress_disc_chr_fn,
search_ctx);
MODLOG_DFLT(INFO, "rc=%d\n", rc);
assert(rc == 0);
return 0;
}
MODLOG_DFLT(ERROR,
"\033[0;31mDid not find particular service\033[0m\n");
return 0;
}
MODLOG_DFLT(ERROR, "\033[0;31mError during service discovery\033[0m\n");
assert(0);
return 0;
}
static void
stress_gatt_find_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid,
const ble_uuid_t *chr_uuid, const ble_uuid_t *dsc_uuid,
stress_gatt_disc_end_fn *disc_end_fn,
uint8_t search_goal)
{
static struct stress_gatt_search_ctx search_ctx;
int rc;
search_ctx.conn_handle = conn_handle;
ble_uuid_copy((ble_uuid_any_t *) &search_ctx.srv_uuid, srv_uuid);
ble_uuid_copy((ble_uuid_any_t *) &search_ctx.chr_uuid, chr_uuid);
ble_uuid_copy((ble_uuid_any_t *) &search_ctx.dsc_uuid, dsc_uuid);
search_ctx.disc_end_fn = disc_end_fn;
search_ctx.search_goal = search_goal;
search_ctx.srv_start_handle = 0;
search_ctx.srv_end_handle = 0;
search_ctx.chr_start_handle = 0;
search_ctx.dsc_handle = 0;
rc = ble_gattc_disc_svc_by_uuid(conn_handle, srv_uuid, &stress_disc_svc_fn,
&search_ctx);
assert(rc == 0);
}
void
stress_find_svc_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid,
stress_gatt_disc_end_fn *disc_end_fn)
{
stress_gatt_find_handle(conn_handle, srv_uuid, NULL, NULL, disc_end_fn,
STRESS_FIND_SRV);
}
void
stress_find_chr_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid,
const ble_uuid_t *chr_uuid,
stress_gatt_disc_end_fn *disc_end_fn)
{
stress_gatt_find_handle(conn_handle, srv_uuid, chr_uuid, NULL, disc_end_fn,
STRESS_FIND_CHR);
}
void
stress_find_dsc_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid,
const ble_uuid_t *chr_uuid, const ble_uuid_t *dsc_uuid,
stress_gatt_disc_end_fn *disc_end_fn)
{
stress_gatt_find_handle(conn_handle, srv_uuid, chr_uuid, dsc_uuid,
disc_end_fn, STRESS_FIND_DSC);
}

View file

@ -0,0 +1,375 @@
/*
* 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.
*/
#ifndef BLE_TGT_STRESS_H
#define BLE_TGT_STRESS_H
#ifdef __cplusplus
extern "C" {
#endif
#include <nimble/ble.h>
#include <modlog/modlog.h>
#include <os/mynewt.h>
#include <console/console.h>
#include <host/ble_l2cap.h>
#include <host/ble_gatt.h>
#include <host/ble_gap.h>
#include <assert.h>
#define STRESS_UUIDS_NUM (16)
/* No preferred PHY */
#define TX_PHY_MASK 0
#define RX_PHY_MASK 0
/* L2CAP SDU */
#define STRESS_COC_MTU (64000)
#define STRESS_FIND_SRV 1
#define STRESS_FIND_CHR 2
#define STRESS_FIND_DSC 3
struct os_callout stress_timer_callout;
struct stress_gatt_search_ctx;
typedef void stress_gatt_disc_end_fn(struct stress_gatt_search_ctx *search_ctx);
struct stress_gatt_search_ctx {
/* Connection handle */
uint16_t conn_handle;
/* Wanted service uuid */
ble_uuid16_t srv_uuid;
/* Wanted characteristic uuid */
ble_uuid16_t chr_uuid;
/* Wanted descriptor uuid */
ble_uuid16_t dsc_uuid;
/* Search goal: 1 - service, 2 - characteristic, 3 - descriptor */
uint8_t search_goal;
/* Callback after reaching the goal */
stress_gatt_disc_end_fn *disc_end_fn;
/* Service start handle */
uint16_t srv_start_handle;
/* Service end handle */
uint16_t srv_end_handle;
/* Characteristic start handle */
uint16_t chr_start_handle;
/* Descriptor handle */
uint16_t dsc_handle;
};
struct stress_con_stat {
/* Number of successful connection attempts */
int num;
/* Max number of established connections */
int max_num;
/* Number connection attempts */
int attempts_num;
/* Number of params updates */
int prms_upd_num;
/* Number of PHY updates */
int phy_upd_num;
};
/* Common stress test context.
* (Reusable) - auxiliary variable
* (Stress x) - variable contains result of x stress test */
struct com_stress_test_ctx {
/* Which of tests are completed already. Each element for different
* stress test. */
bool completed[STRESS_UUIDS_NUM];
/* Connection stats. Each element for different stress test. */
struct stress_con_stat con_stat[STRESS_UUIDS_NUM];
/* Reusable variables */
/* Stress test number */
int cur_test_id;
/* Instance address */
ble_addr_t dev_addr;
/* Connection handler */
uint16_t conn_handle;
/* L2CAP channel (Reusable) */
struct ble_l2cap_chan * chan;
/* Size of received data */
int64_t rcv_data_bytes;
/* Number of received packages */
int rcv_num;
/* Number of send actions */
int send_num;
/* Variables for bit rate measurement */
int64_t begin_us;
int64_t end_us;
int64_t time_sum;
int64_t bytes_sum;
/* Timeout flag */
bool timeout_flag;
/* Data received flag */
bool rcv_data_flag;
uint16_t start_handle;
uint16_t end_handle;
uint16_t dsc_handle;
/* Variables for test results */
/* Reached timeout of scanning for test */
bool scan_timeout;
/* Number of received first packets of adverts */
int s6_rcv_adv_first;
/* Number of full received adverts */
int s6_rcv_adv_suc;
/* L2CAP Send Bit Rate */
int s10_bit_rate;
/* Average indication time */
int s12_notif_time;
/* Average notification time */
int s13_notif_time;
/* Average notification time */
int s14_notif_time;
/* Max size of received MTU */
int64_t s10_max_mtu;
};
#define STRESS_PAT_LEN 1650
static const uint8_t test_6_pattern[STRESS_PAT_LEN] = {
/* Random data */
0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, 0x08, 0x00, 0x0a,
0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14,
0x00, 0x16, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1e,
0x00, 0x20, 0x00, 0x22, 0x00, 0x24, 0x00, 0x26, 0x00, 0x28,
0x00, 0x2a, 0x00, 0x2c, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x32,
0x00, 0x34, 0x00, 0x36, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x3c,
0x00, 0x3e, 0x00, 0x40, 0x00, 0x42, 0x00, 0x44, 0x00, 0x46,
0x00, 0x48, 0x00, 0x4a, 0x00, 0x4c, 0x00, 0x4e, 0x00, 0x50,
0x00, 0x52, 0x00, 0x54, 0x00, 0x56, 0x00, 0x58, 0x00, 0x5a,
0x00, 0x5c, 0x00, 0x5e, 0x00, 0x60, 0x00, 0x62, 0x00, 0x64,
0x00, 0x66, 0x00, 0x68, 0x00, 0x6a, 0x00, 0x6c, 0x00, 0x6e,
0x00, 0x70, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x78,
0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7e, 0x00, 0x80, 0x00, 0x82,
0x00, 0x84, 0x00, 0x86, 0x00, 0x88, 0x00, 0x8a, 0x00, 0x8c,
0x00, 0x8e, 0x00, 0x90, 0x00, 0x92, 0x00, 0x94, 0x00, 0x96,
0x00, 0x98, 0x00, 0x9a, 0x00, 0x9c, 0x00, 0x9e, 0x00, 0xa0,
0x00, 0xa2, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa,
0x00, 0xac, 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb4,
0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbc, 0x00, 0xbe,
0x00, 0xc0, 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc8,
0x00, 0xca, 0x00, 0xcc, 0x00, 0xce, 0x00, 0xd0, 0x00, 0xd2,
0x00, 0xd4, 0x00, 0xd6, 0x00, 0xd8, 0x00, 0xda, 0x00, 0xdc,
0x00, 0xde, 0x00, 0xe0, 0x00, 0xe2, 0x00, 0xe4, 0x00, 0xe6,
0x00, 0xe8, 0x00, 0xea, 0x00, 0xec, 0x00, 0xee, 0x00, 0xf0,
0x00, 0xf2, 0x00, 0xf4, 0x00, 0xf6, 0x00, 0xf8, 0x00, 0xfa,
0x00, 0xfc, 0x00, 0xfe, 0x01, 0x01, 0x01, 0x03, 0x01, 0x05,
0x01, 0x07, 0x01, 0x09, 0x01, 0x0b, 0x01, 0x0d, 0x01, 0x0f,
0x01, 0x11, 0x01, 0x13, 0x01, 0x15, 0x01, 0x17, 0x01, 0x19,
0x01, 0x1b, 0x01, 0x1d, 0x01, 0x1f, 0x01, 0x21, 0x01, 0x23,
0x01, 0x25, 0x01, 0x27, 0x01, 0x29, 0x01, 0x2b, 0x01, 0x2d,
0x01, 0x2f, 0x01, 0x31, 0x01, 0x33, 0x01, 0x35, 0x01, 0x37,
0x01, 0x39, 0x01, 0x3b, 0x01, 0x3d, 0x01, 0x3f, 0x01, 0x41,
0x01, 0x43, 0x01, 0x45, 0x01, 0x47, 0x01, 0x49, 0x01, 0x4b,
0x01, 0x4d, 0x01, 0x4f, 0x01, 0x51, 0x01, 0x53, 0x01, 0x55,
0x01, 0x57, 0x01, 0x59, 0x01, 0x5b, 0x01, 0x5d, 0x01, 0x5f,
0x01, 0x61, 0x01, 0x63, 0x01, 0x65, 0x01, 0x67, 0x01, 0x69,
0x01, 0x6b, 0x01, 0x6d, 0x01, 0x6f, 0x01, 0x71, 0x01, 0x73,
0x01, 0x75, 0x01, 0x77, 0x01, 0x79, 0x01, 0x7b, 0x01, 0x7d,
0x01, 0x7f, 0x01, 0x81, 0x01, 0x83, 0x01, 0x85, 0x01, 0x87,
0x01, 0x89, 0x01, 0x8b, 0x01, 0x8d, 0x01, 0x8f, 0x01, 0x91,
0x01, 0x93, 0x01, 0x95, 0x01, 0x97, 0x01, 0x99, 0x01, 0x9b,
0x01, 0x9d, 0x01, 0x9f, 0x01, 0xa1, 0x01, 0xa3, 0x01, 0xa5,
0x01, 0xa7, 0x01, 0xa9, 0x01, 0xab, 0x01, 0xad, 0x01, 0xaf,
0x01, 0xb1, 0x01, 0xb3, 0x01, 0xb5, 0x01, 0xb7, 0x01, 0xb9,
0x01, 0xbb, 0x01, 0xbd, 0x01, 0xbf, 0x01, 0xc1, 0x01, 0xc3,
0x01, 0xc5, 0x01, 0xc7, 0x01, 0xc9, 0x01, 0xcb, 0x01, 0xcd,
0x01, 0xcf, 0x01, 0xd1, 0x01, 0xd3, 0x01, 0xd5, 0x01, 0xd7,
0x01, 0xd9, 0x01, 0xdb, 0x01, 0xdd, 0x01, 0xdf, 0x01, 0xe1,
0x01, 0xe3, 0x01, 0xe5, 0x01, 0xe7, 0x01, 0xe9, 0x01, 0xeb,
0x01, 0xed, 0x01, 0xef, 0x01, 0xf1, 0x01, 0xf3, 0x01, 0xf5,
0x01, 0xf7, 0x01, 0xf9, 0x01, 0xfb, 0x01, 0xfd, 0x02, 0x00,
0x02, 0x02, 0x02, 0x04, 0x02, 0x06, 0x02, 0x08, 0x02, 0x0a,
0x02, 0x0c, 0x02, 0x0e, 0x02, 0x10, 0x02, 0x12, 0x02, 0x14,
0x02, 0x16, 0x02, 0x18, 0x02, 0x1a, 0x02, 0x1c, 0x02, 0x1e,
0x02, 0x20, 0x02, 0x22, 0x02, 0x24, 0x02, 0x26, 0x02, 0x28,
0x02, 0x2a, 0x02, 0x2c, 0x02, 0x2e, 0x02, 0x30, 0x02, 0x32,
0x02, 0x34, 0x02, 0x36, 0x02, 0x38, 0x02, 0x3a, 0x02, 0x3c,
0x02, 0x3e, 0x02, 0x40, 0x02, 0x42, 0x02, 0x44, 0x02, 0x46,
0x02, 0x48, 0x02, 0x4a, 0x02, 0x4c, 0x02, 0x4e, 0x02, 0x50,
0x02, 0x52, 0x02, 0x54, 0x02, 0x56, 0x02, 0x58, 0x02, 0x5a,
0x02, 0x5c, 0x02, 0x5e, 0x02, 0x60, 0x02, 0x62, 0x02, 0x64,
0x02, 0x66, 0x02, 0x68, 0x02, 0x6a, 0x02, 0x6c, 0x02, 0x6e,
0x02, 0x70, 0x02, 0x72, 0x02, 0x74, 0x02, 0x76, 0x02, 0x78,
0x02, 0x7a, 0x02, 0x7c, 0x02, 0x7e, 0x02, 0x80, 0x02, 0x82,
0x02, 0x84, 0x02, 0x86, 0x02, 0x88, 0x02, 0x8a, 0x02, 0x8c,
0x02, 0x8e, 0x02, 0x90, 0x02, 0x92, 0x02, 0x94, 0x02, 0x96,
0x02, 0x98, 0x02, 0x9a, 0x02, 0x9c, 0x02, 0x9e, 0x02, 0xa0,
0x02, 0xa2, 0x02, 0xa4, 0x02, 0xa6, 0x02, 0xa8, 0x02, 0xaa,
0x02, 0xac, 0x02, 0xae, 0x02, 0xb0, 0x02, 0xb2, 0x02, 0xb4,
0x02, 0xb6, 0x02, 0xb8, 0x02, 0xba, 0x02, 0xbc, 0x02, 0xbe,
0x02, 0xc0, 0x02, 0xc2, 0x02, 0xc4, 0x02, 0xc6, 0x02, 0xc8,
0x02, 0xca, 0x02, 0xcc, 0x02, 0xce, 0x02, 0xd0, 0x02, 0xd2,
0x02, 0xd4, 0x02, 0xd6, 0x02, 0xd8, 0x02, 0xda, 0x02, 0xdc,
0x02, 0xde, 0x02, 0xe0, 0x02, 0xe2, 0x02, 0xe4, 0x02, 0xe6,
0x02, 0xe8, 0x02, 0xea, 0x02, 0xec, 0x02, 0xee, 0x02, 0xf0,
0x02, 0xf2, 0x02, 0xf4, 0x02, 0xf6, 0x02, 0xf8, 0x02, 0xfa,
0x02, 0xfc, 0x02, 0xfe, 0x03, 0x01, 0x03, 0x03, 0x03, 0x05,
0x03, 0x07, 0x03, 0x09, 0x03, 0x0b, 0x03, 0x0d, 0x03, 0x0f,
0x03, 0x11, 0x03, 0x13, 0x03, 0x15, 0x03, 0x17, 0x03, 0x19,
0x03, 0x1b, 0x03, 0x1d, 0x03, 0x1f, 0x03, 0x21, 0x03, 0x23,
0x03, 0x25, 0x03, 0x27, 0x03, 0x29, 0x03, 0x2b, 0x03, 0x2d,
0x03, 0x2f, 0x03, 0x31, 0x03, 0x33, 0x03, 0x35, 0x03, 0x37,
0x03, 0x39, 0x03, 0x3b, 0x03, 0x3d, 0x03, 0x3f, 0x03, 0x41,
0x03, 0x43, 0x03, 0x45, 0x03, 0x47, 0x03, 0x49, 0x03, 0x4b,
0x03, 0x4d, 0x03, 0x4f, 0x03, 0x51, 0x03, 0x53, 0x03, 0x55,
0x03, 0x57, 0x03, 0x59, 0x03, 0x5b, 0x03, 0x5d, 0x03, 0x5f,
0x03, 0x61, 0x03, 0x63, 0x03, 0x65, 0x03, 0x67, 0x03, 0x69,
0x03, 0x6b, 0x03, 0x6d, 0x03, 0x6f, 0x03, 0x71, 0x03, 0x73,
0x03, 0x75, 0x03, 0x77, 0x03, 0x79, 0x03, 0x7b, 0x03, 0x7d,
0x03, 0x7f, 0x03, 0x81, 0x03, 0x83, 0x03, 0x85, 0x03, 0x87,
0x03, 0x89, 0x03, 0x8b, 0x03, 0x8d, 0x03, 0x8f, 0x03, 0x91,
0x03, 0x93, 0x03, 0x95, 0x03, 0x97, 0x03, 0x99, 0x03, 0x9b,
0x03, 0x9d, 0x03, 0x9f, 0x03, 0xa1, 0x03, 0xa3, 0x03, 0xa5,
0x03, 0xa7, 0x03, 0xa9, 0x03, 0xab, 0x03, 0xad, 0x03, 0xaf,
0x03, 0xb1, 0x03, 0xb3, 0x03, 0xb5, 0x03, 0xb7, 0x03, 0xb9,
0x03, 0xbb, 0x03, 0xbd, 0x03, 0xbf, 0x03, 0xc1, 0x03, 0xc3,
0x03, 0xc5, 0x03, 0xc7, 0x03, 0xc9, 0x03, 0xcb, 0x03, 0xcd,
0x03, 0xcf, 0x03, 0xd1, 0x03, 0xd3, 0x03, 0xd5, 0x03, 0xd7,
0x03, 0xd9, 0x03, 0xdb, 0x03, 0xdd, 0x03, 0xdf, 0x03, 0xe1,
0x03, 0xe3, 0x03, 0xe5, 0x03, 0xe7, 0x03, 0xe9, 0x03, 0xeb,
0x03, 0xed, 0x03, 0xef, 0x03, 0xf1, 0x03, 0xf3, 0x03, 0xf5,
0x03, 0xf7, 0x03, 0xf9, 0x03, 0xfb, 0x03, 0xfd, 0x04, 0x00,
0x04, 0x02, 0x04, 0x04, 0x04, 0x06, 0x04, 0x08, 0x04, 0x0a,
0x04, 0x0c, 0x04, 0x0e, 0x04, 0x10, 0x04, 0x12, 0x04, 0x14,
0x04, 0x16, 0x04, 0x18, 0x04, 0x1a, 0x04, 0x1c, 0x04, 0x1e,
0x04, 0x20, 0x04, 0x22, 0x04, 0x24, 0x04, 0x26, 0x04, 0x28,
0x04, 0x2a, 0x04, 0x2c, 0x04, 0x2e, 0x04, 0x30, 0x04, 0x32,
0x04, 0x34, 0x04, 0x36, 0x04, 0x38, 0x04, 0x3a, 0x04, 0x3c,
0x04, 0x3e, 0x04, 0x40, 0x04, 0x42, 0x04, 0x44, 0x04, 0x46,
0x04, 0x48, 0x04, 0x4a, 0x04, 0x4c, 0x04, 0x4e, 0x04, 0x50,
0x04, 0x52, 0x04, 0x54, 0x04, 0x56, 0x04, 0x58, 0x04, 0x5a,
0x04, 0x5c, 0x04, 0x5e, 0x04, 0x60, 0x04, 0x62, 0x04, 0x64,
0x04, 0x66, 0x04, 0x68, 0x04, 0x6a, 0x04, 0x6c, 0x04, 0x6e,
0x04, 0x70, 0x04, 0x72, 0x04, 0x74, 0x04, 0x76, 0x04, 0x78,
0x04, 0x7a, 0x04, 0x7c, 0x04, 0x7e, 0x04, 0x80, 0x04, 0x82,
0x04, 0x84, 0x04, 0x86, 0x04, 0x88, 0x04, 0x8a, 0x04, 0x8c,
0x04, 0x8e, 0x04, 0x90, 0x04, 0x92, 0x04, 0x94, 0x04, 0x96,
0x04, 0x98, 0x04, 0x9a, 0x04, 0x9c, 0x04, 0x9e, 0x04, 0xa0,
0x04, 0xa2, 0x04, 0xa4, 0x04, 0xa6, 0x04, 0xa8, 0x04, 0xaa,
0x04, 0xac, 0x04, 0xae, 0x04, 0xb0, 0x04, 0xb2, 0x04, 0xb4,
0x04, 0xb6, 0x04, 0xb8, 0x04, 0xba, 0x04, 0xbc, 0x04, 0xbe,
0x04, 0xc0, 0x04, 0xc2, 0x04, 0xc4, 0x04, 0xc6, 0x04, 0xc8,
0x04, 0xca, 0x04, 0xcc, 0x04, 0xce, 0x04, 0xd0, 0x04, 0xd2,
0x04, 0xd4, 0x04, 0xd6, 0x04, 0xd8, 0x04, 0xda, 0x04, 0xdc,
0x04, 0xde, 0x04, 0xe0, 0x04, 0xe2, 0x04, 0xe4, 0x04, 0xe6,
0x04, 0xe8, 0x04, 0xea, 0x04, 0xec, 0x04, 0xee, 0x04, 0xf0,
0x04, 0xf2, 0x04, 0xf4, 0x04, 0xf6, 0x04, 0xf8, 0x04, 0xfa,
0x04, 0xfc, 0x04, 0xfe, 0x05, 0x01, 0x05, 0x03, 0x05, 0x05,
0x05, 0x07, 0x05, 0x09, 0x05, 0x0b, 0x05, 0x0d, 0x05, 0x0f,
0x05, 0x11, 0x05, 0x13, 0x05, 0x15, 0x05, 0x17, 0x05, 0x19,
0x05, 0x1b, 0x05, 0x1d, 0x05, 0x1f, 0x05, 0x21, 0x05, 0x23,
0x05, 0x25, 0x05, 0x27, 0x05, 0x29, 0x05, 0x2b, 0x05, 0x2d,
0x05, 0x2f, 0x05, 0x31, 0x05, 0x33, 0x05, 0x35, 0x05, 0x37,
0x05, 0x39, 0x05, 0x3b, 0x05, 0x3d, 0x05, 0x3f, 0x05, 0x41,
0x05, 0x43, 0x05, 0x45, 0x05, 0x47, 0x05, 0x49, 0x05, 0x4b,
0x05, 0x4d, 0x05, 0x4f, 0x05, 0x51, 0x05, 0x53, 0x05, 0x55,
0x05, 0x57, 0x05, 0x59, 0x05, 0x5b, 0x05, 0x5d, 0x05, 0x5f,
0x05, 0x61, 0x05, 0x63, 0x05, 0x65, 0x05, 0x67, 0x05, 0x69,
0x05, 0x6b, 0x05, 0x6d, 0x05, 0x6f, 0x05, 0x71, 0x05, 0x73,
0x05, 0x75, 0x05, 0x77, 0x05, 0x79, 0x05, 0x7b, 0x05, 0x7d,
0x05, 0x7f, 0x05, 0x81, 0x05, 0x83, 0x05, 0x85, 0x05, 0x87,
0x05, 0x89, 0x05, 0x8b, 0x05, 0x8d, 0x05, 0x8f, 0x05, 0x91,
0x05, 0x93, 0x05, 0x95, 0x05, 0x97, 0x05, 0x99, 0x05, 0x9b,
0x05, 0x9d, 0x05, 0x9f, 0x05, 0xa1, 0x05, 0xa3, 0x05, 0xa5,
0x05, 0xa7, 0x05, 0xa9, 0x05, 0xab, 0x05, 0xad, 0x05, 0xaf,
0x05, 0xb1, 0x05, 0xb3, 0x05, 0xb5, 0x05, 0xb7, 0x05, 0xb9,
0x05, 0xbb, 0x05, 0xbd, 0x05, 0xbf, 0x05, 0xc1, 0x05, 0xc3,
0x05, 0xc5, 0x05, 0xc7, 0x05, 0xc9, 0x05, 0xcb, 0x05, 0xcd,
0x05, 0xcf, 0x05, 0xd1, 0x05, 0xd3, 0x05, 0xd5, 0x05, 0xd7,
0x05, 0xd9, 0x05, 0xdb, 0x05, 0xdd, 0x05, 0xdf, 0x05, 0xe1,
0x05, 0xe3, 0x05, 0xe5, 0x05, 0xe7, 0x05, 0xe9, 0x05, 0xeb,
0x05, 0xed, 0x05, 0xef, 0x05, 0xf1, 0x05, 0xf3, 0x05, 0xf5,
0x05, 0xf7, 0x05, 0xf9, 0x05, 0xfb, 0x05, 0xfd, 0x06, 0x00,
0x06, 0x02, 0x06, 0x04, 0x06, 0x06, 0x06, 0x08, 0x06, 0x0a,
0x06, 0x0c, 0x06, 0x0e, 0x06, 0x10, 0x06, 0x12, 0x06, 0x14,
0x06, 0x16, 0x06, 0x18, 0x06, 0x1a, 0x06, 0x1c, 0x06, 0x1e,
0x06, 0x20, 0x06, 0x22, 0x06, 0x24, 0x06, 0x26, 0x06, 0x28,
0x06, 0x2a, 0x06, 0x2c, 0x06, 0x2e, 0x06, 0x30, 0x06, 0x32,
0x06, 0x34, 0x06, 0x36, 0x06, 0x38, 0x06, 0x3a, 0x06, 0x3c,
0x06, 0x3e, 0x06, 0x40, 0x06, 0x42, 0x06, 0x44, 0x06, 0x46,
0x06, 0x48, 0x06, 0x4a, 0x06, 0x4c, 0x06, 0x4e, 0x06, 0x50,
0x06, 0x52, 0x06, 0x54, 0x06, 0x56, 0x06, 0x58, 0x06, 0x5a,
0x06, 0x5c, 0x06, 0x5e, 0x06, 0x60, 0x06, 0x62, 0x06, 0x64,
0x06, 0x66, 0x06, 0x68, 0x06, 0x6a, 0x06, 0x6c, 0x06, 0x6e,
0x06, 0x70, 0x06, 0x72, 0x06, 0x74, 0x06, 0x76, 0x06, 0x78,
};
void stress_clear_ctx_reusable_var(struct com_stress_test_ctx *ctx);
void com_stress_print_report(const struct com_stress_test_ctx test_ctxs[]);
int stress_fill_mbuf_with_pattern(struct os_mbuf *om, uint16_t len);
void stress_l2cap_coc_recv(struct ble_l2cap_chan *chan, struct os_mbuf *sdu);
void stress_l2cap_coc_accept(uint16_t peer_mtu, struct ble_l2cap_chan *chan);
void stress_start_timer(uint32_t timeout_ms, os_event_fn *ev_cb);
int64_t stress_calc_bit_rate(int64_t us, int64_t bytes_num);
void stress_find_svc_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid,
stress_gatt_disc_end_fn *disc_end_fn);
void stress_find_chr_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid,
const ble_uuid_t *chr_uuid,
stress_gatt_disc_end_fn *disc_end_fn);
void stress_find_dsc_handle(uint16_t conn_handle, const ble_uuid_t *srv_uuid,
const ble_uuid_t *chr_uuid, const ble_uuid_t *dsc_uuid,
stress_gatt_disc_end_fn *disc_end_fn);
#ifdef __cplusplus
}
#endif
#endif //BLE_TGT_STRESS_H

View file

@ -0,0 +1,155 @@
/*
* 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 "stress_gatt.h"
uint16_t hrs_hrm_handle = 0xffff;
static int
stress_gatt_access_cb(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);
static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
{
/* Service: Heart-rate */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = BLE_UUID16_DECLARE(STRESS_GATT_UUID),
.characteristics = (struct ble_gatt_chr_def[]) {
{
/* Characteristic: read test */
.uuid = BLE_UUID16_DECLARE(STRESS_GATT_READ_UUID),
.access_cb = stress_gatt_access_cb,
.val_handle = &hrs_hrm_handle,
.flags = BLE_GATT_CHR_F_READ,
},
{
/* Characteristic: write test */
.uuid = BLE_UUID16_DECLARE(STRESS_GATT_WRITE_UUID),
.access_cb = stress_gatt_access_cb,
.val_handle = &hrs_hrm_handle,
.flags = BLE_GATT_CHR_F_WRITE,
},
{
/* Characteristic: notify test */
.uuid = BLE_UUID16_DECLARE(STRESS_GATT_NOTIFY_UUID),
.access_cb = stress_gatt_access_cb,
.val_handle = &hrs_hrm_handle,
.flags = BLE_GATT_CHR_F_NOTIFY,
},
{
/* Characteristic: indicate test */
.uuid = BLE_UUID16_DECLARE(STRESS_GATT_INDICATE_UUID),
.access_cb = stress_gatt_access_cb,
.val_handle = &hrs_hrm_handle,
.flags = BLE_GATT_CHR_F_INDICATE,
},
{
0, /* No more characteristics in this service */
},}
},
{
0, /* No more services */
},
};
static int
stress_gatt_access_cb(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
/* Sensor location, set to "Chest" */
static uint8_t chr_value[] = "Hello";
uint16_t uuid;
int rc;
//chr_value = (uint8_t)rand() % 256;
uuid = ble_uuid_u16(ctxt->chr->uuid);
switch(uuid){
case STRESS_GATT_READ_UUID:
MODLOG_DFLT(INFO, "GATT Read event\n");
rc = os_mbuf_append(ctxt->om, &chr_value, sizeof(chr_value));
assert(rc == 0);
//return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
return 0;
case STRESS_GATT_WRITE_UUID:
MODLOG_DFLT(INFO, "GATT Write event\n");
print_mbuf(ctxt->om);
return 0;
case STRESS_GATT_NOTIFY_UUID:
MODLOG_DFLT(INFO, "GATT Notify event\n");
return 0;
case STRESS_GATT_INDICATE_UUID:
MODLOG_DFLT(INFO, "GATT Indicate event\n");
return 0;
default:
MODLOG_DFLT(ERROR, "GATT UUID does not exist\n");
assert(0);
return BLE_ATT_ERR_UNLIKELY;
}
}
void
gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
{
char buf[BLE_UUID_STR_LEN];
switch (ctxt->op) {
case BLE_GATT_REGISTER_OP_SVC:
MODLOG_DFLT(DEBUG, "registered service %s with handle=%d\n",
ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf),
ctxt->svc.handle);
break;
case BLE_GATT_REGISTER_OP_CHR:
MODLOG_DFLT(DEBUG, "registering characteristic %s with "
"def_handle=%d val_handle=%d\n",
ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf),
ctxt->chr.def_handle,
ctxt->chr.val_handle);
break;
case BLE_GATT_REGISTER_OP_DSC:
MODLOG_DFLT(DEBUG, "registering descriptor %s with handle=%d\n",
ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf),
ctxt->dsc.handle);
break;
default:
assert(0);
break;
}
}
int
gatt_svr_init(void)
{
int rc;
rc = ble_gatts_count_cfg(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
rc = ble_gatts_add_svcs(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
return 0;
}

View file

@ -0,0 +1,54 @@
/*
* 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.
*/
#ifndef BLE_TGT_STRESS_GATT_H
#define BLE_TGT_STRESS_GATT_H
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "host/ble_hs.h"
#include "host/ble_uuid.h"
#include "nimble/ble.h"
#include "modlog/modlog.h"
#include "misc.h"
#ifdef __cplusplus
extern "C" {
#endif
extern uint16_t hrs_hrm_handle;
/* Heart-rate configuration */
#define STRESS_GATT_UUID 0xC0DE
#define STRESS_GATT_READ_UUID 0xC1DE
#define STRESS_GATT_WRITE_UUID 0xC2DE
#define STRESS_GATT_INDICATE_UUID 0xC3DE
#define STRESS_GATT_NOTIFY_UUID 0xC4DE
int gatt_svr_init(void);
void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg);
#ifdef __cplusplus
}
#endif
#endif //BLE_TGT_STRESS_GATT_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,49 @@
/*
* 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.
*/
#ifndef _BLE_STRESS_TX_H
#define _BLE_STRESS_TX_H
#include <assert.h>
#include <string.h>
#include <host/ble_gap.h>
#include <stdlib.h>
/* BLE */
#include "nimble/ble.h"
#include "host/ble_hs.h"
#include "misc.h"
#include "stress.h"
#include "stress_gatt.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* Scan and execute tests one by one.
*/
void tx_stress_start_auto();
#ifdef __cplusplus
}
#endif
#endif //_BLE_STRESS_TX_H

View file

@ -0,0 +1,74 @@
#
# 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.
#
# Settings this app defines.
syscfg.defs:
BLE_STRESS_TEST_ROLE:
description: 0 - TX device, 1 - RX device
value: 0
BLE_STRESS_REPEAT:
description: Number of times to repeat each stress test use case
value: 50
BLE_STRESS_UUID_BASE:
description: Part of the test UUID that is specific to the current
device couple.
value: ((uint8_t[13]){0xA5, 0x4A, 0xB4, 0x44, 0xC3, 0xBF, 0xB5,
0xF8, 0xF9, 0x42, 0x83, 0xA1, 0xDA})
# Settings this app overrides.
syscfg.vals:
# Change these settings:
# Set 0 to print all debug logs, but keep in mind that plenty of
# adv packets will be lost during scan tests.
LOG_LEVEL: 1
# The maximum number of concurrent connections. For some devices, this
# value will need to be reduced due to the RAM capacity.
BLE_MAX_CONNECTIONS: 50
# Should not change these settings:
# Enable Extended Advertising
BLE_EXT_ADV: 1
# Max advertising data size
BLE_EXT_ADV_MAX_SIZE: 1650
# Number of multi-advertising instances. Note that due
# to historical reasons total number of advertising
# instances is BLE_MULTI_ADV_INSTANCES + 1 as instance
# 0 is always available
BLE_MULTI_ADV_INSTANCES: 2
# Controller uses msys pool for storing advertising data and scan responses.
# Since we advertise a lot of data (~4k in total) at the same time we need
# to increase block count.
MSYS_1_BLOCK_COUNT: 50
#
BLE_L2CAP_COC_MAX_NUM: 2
# Enable 2M PHY
BLE_LL_CFG_FEAT_LE_2M_PHY: 1
# Enable CODED PHY
BLE_LL_CFG_FEAT_LE_CODED_PHY: 1

View file

@ -0,0 +1,39 @@
# 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.
#
pkg.name: apps/btshell
pkg.type: app
pkg.description: Shell application exposing the nimble GAP and GATT.
pkg.author: "Apache Mynewt <dev@mynewt.apache.org>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
pkg.deps:
- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/sys/log/full"
- "@apache-mynewt-core/sys/log/modlog"
- "@apache-mynewt-core/sys/stats/full"
- "@apache-mynewt-core/sys/console/full"
- "@apache-mynewt-core/sys/shell"
- nimble/host
- nimble/host/services/gap
- nimble/host/services/gatt
- nimble/host/store/ram
- nimble/transport
pkg.deps.BTSHELL_ANS:
- nimble/host/services/ans

View file

@ -0,0 +1,212 @@
/*
* 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.
*/
#ifndef H_BTSHELL_PRIV_
#define H_BTSHELL_PRIV_
#include <inttypes.h>
#include "os/mynewt.h"
#include "nimble/ble.h"
#include "nimble/nimble_opt.h"
#include "modlog/modlog.h"
#include "host/ble_gatt.h"
#include "host/ble_gap.h"
#ifdef __cplusplus
extern "C" {
#endif
struct ble_gap_white_entry;
struct ble_hs_adv_fields;
struct ble_gap_upd_params;
struct ble_gap_conn_params;
struct hci_adv_params;
struct ble_l2cap_sig_update_req;
struct ble_l2cap_sig_update_params;
union ble_store_value;
union ble_store_key;
struct ble_gap_adv_params;
struct ble_gap_conn_desc;
struct ble_gap_disc_params;
struct btshell_dsc {
SLIST_ENTRY(btshell_dsc) next;
struct ble_gatt_dsc dsc;
};
SLIST_HEAD(btshell_dsc_list, btshell_dsc);
struct btshell_chr {
SLIST_ENTRY(btshell_chr) next;
struct ble_gatt_chr chr;
struct btshell_dsc_list dscs;
};
SLIST_HEAD(btshell_chr_list, btshell_chr);
struct btshell_svc {
SLIST_ENTRY(btshell_svc) next;
struct ble_gatt_svc svc;
struct btshell_chr_list chrs;
bool discovered;
};
SLIST_HEAD(btshell_svc_list, btshell_svc);
struct btshell_l2cap_coc {
SLIST_ENTRY(btshell_l2cap_coc) next;
struct ble_l2cap_chan *chan;
bool stalled;
};
SLIST_HEAD(btshell_l2cap_coc_list, btshell_l2cap_coc);
struct btshell_conn {
uint16_t handle;
struct btshell_svc_list svcs;
struct btshell_l2cap_coc_list coc_list;
};
struct btshell_scan_opts {
uint16_t limit;
uint8_t ignore_legacy:1;
uint8_t periodic_only:1;
};
extern struct btshell_conn btshell_conns[MYNEWT_VAL(BLE_MAX_CONNECTIONS)];
extern int btshell_num_conns;
int btshell_exchange_mtu(uint16_t conn_handle);
int btshell_disc_svcs(uint16_t conn_handle);
int btshell_disc_svc_by_uuid(uint16_t conn_handle, const ble_uuid_t *uuid);
int btshell_disc_all_chrs(uint16_t conn_handle, uint16_t start_handle,
uint16_t end_handle);
int btshell_disc_all_chrs_in_svc(uint16_t conn_handle, struct btshell_svc *svc);
int btshell_disc_chrs_by_uuid(uint16_t conn_handle, uint16_t start_handle,
uint16_t end_handle, const ble_uuid_t *uuid);
int btshell_disc_all_dscs(uint16_t conn_handle, uint16_t start_handle,
uint16_t end_handle);
int btshell_disc_full(uint16_t conn_handle);
int btshell_find_inc_svcs(uint16_t conn_handle, uint16_t start_handle,
uint16_t end_handle);
int btshell_read(uint16_t conn_handle, uint16_t attr_handle);
int btshell_read_long(uint16_t conn_handle, uint16_t attr_handle,
uint16_t offset);
int btshell_read_by_uuid(uint16_t conn_handle, uint16_t start_handle,
uint16_t end_handle, const ble_uuid_t *uuid);
int btshell_read_mult(uint16_t conn_handle, uint16_t *attr_handles,
int num_attr_handles);
int btshell_write(uint16_t conn_handle, uint16_t attr_handle,
struct os_mbuf *om);
int btshell_write_no_rsp(uint16_t conn_handle, uint16_t attr_handle,
struct os_mbuf *om);
int btshell_write_long(uint16_t conn_handle, uint16_t attr_handle,
uint16_t offset, struct os_mbuf *om);
int btshell_write_reliable(uint16_t conn_handle,
struct ble_gatt_attr *attrs, int num_attrs);
#if MYNEWT_VAL(BLE_EXT_ADV)
int btshell_ext_adv_configure(uint8_t instance,
const struct ble_gap_ext_adv_params *params,
int8_t *selected_tx_power);
int btshell_ext_adv_start(uint8_t instance, int duration,
int max_events, bool restart);
int btshell_ext_adv_stop(uint8_t instance);
#endif
int btshell_adv_start(uint8_t own_addr_type, const ble_addr_t *direct_addr,
int32_t duration_ms,
const struct ble_gap_adv_params *params,
bool restart);
int btshell_adv_stop(void);
int btshell_conn_initiate(uint8_t own_addr_type, const ble_addr_t *peer_addr,
int32_t duration_ms,
struct ble_gap_conn_params *params);
int btshell_ext_conn_initiate(uint8_t own_addr_type,
const ble_addr_t *peer_addr,
int32_t duration_ms,
struct ble_gap_conn_params *phy_1m_params,
struct ble_gap_conn_params *phy_2m_params,
struct ble_gap_conn_params *phy_coded_params);
int btshell_conn_cancel(void);
int btshell_term_conn(uint16_t conn_handle, uint8_t reason);
int btshell_wl_set(ble_addr_t *addrs, int addrs_count);
int btshell_scan(uint8_t own_addr_type, int32_t duration_ms,
const struct ble_gap_disc_params *disc_params, void *cb_args);
int btshell_ext_scan(uint8_t own_addr_type, uint16_t duration, uint16_t period,
uint8_t filter_duplicates, uint8_t filter_policy,
uint8_t limited,
const struct ble_gap_ext_disc_params *uncoded_params,
const struct ble_gap_ext_disc_params *coded_params,
void *cb_args);
int btshell_scan_cancel(void);
int btshell_update_conn(uint16_t conn_handle,
struct ble_gap_upd_params *params);
void btshell_notify(uint16_t attr_handle);
int btshell_datalen(uint16_t conn_handle, uint16_t tx_octets,
uint16_t tx_time);
int btshell_l2cap_update(uint16_t conn_handle,
struct ble_l2cap_sig_update_params *params);
int btshell_sec_start(uint16_t conn_handle);
int btshell_sec_pair(uint16_t conn_handle);
int btshell_sec_unpair(ble_addr_t *peer_addr);
int btshell_sec_restart(uint16_t conn_handle, uint8_t key_size,
uint8_t *ltk, uint16_t ediv,
uint64_t rand_val, int auth);
int btshell_tx_start(uint16_t conn_handle, uint16_t len, uint16_t rate,
uint16_t num);
void btshell_tx_stop(void);
int btshell_rssi(uint16_t conn_handle, int8_t *out_rssi);
int btshell_l2cap_create_srv(uint16_t psm, uint16_t mtu, int accept_response);
int btshell_l2cap_connect(uint16_t conn, uint16_t psm, uint16_t mtu, uint8_t num);
int btshell_l2cap_disconnect(uint16_t conn, uint16_t idx);
int btshell_l2cap_send(uint16_t conn, uint16_t idx, uint16_t bytes);
int btshell_l2cap_reconfig(uint16_t conn_handle, uint16_t mtu,
uint8_t num, uint8_t idxs[]);
int btshell_gap_event(struct ble_gap_event *event, void *arg);
void btshell_sync_stats(uint16_t handle);
/** GATT server. */
#define GATT_SVR_SVC_ALERT_UUID 0x1811
#define GATT_SVR_CHR_SUP_NEW_ALERT_CAT_UUID 0x2A47
#define GATT_SVR_CHR_NEW_ALERT 0x2A46
#define GATT_SVR_CHR_SUP_UNR_ALERT_CAT_UUID 0x2A48
#define GATT_SVR_CHR_UNR_ALERT_STAT_UUID 0x2A45
#define GATT_SVR_CHR_ALERT_NOT_CTRL_PT 0x2A44
void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg);
int gatt_svr_init(void);
void gatt_svr_print_svcs(void);
/** Misc. */
void print_bytes(const uint8_t *bytes, int len);
void print_mbuf(const struct os_mbuf *om);
void print_addr(const void *addr);
void print_uuid(const ble_uuid_t *uuid);
int svc_is_empty(const struct btshell_svc *svc);
uint16_t chr_end_handle(const struct btshell_svc *svc,
const struct btshell_chr *chr);
int chr_is_empty(const struct btshell_svc *svc, const struct btshell_chr *chr);
void print_conn_desc(const struct ble_gap_conn_desc *desc);
void print_svc(struct btshell_svc *svc);
#ifdef __cplusplus
}
#endif
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,68 @@
/*
* 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.
*/
#ifndef CMD_H
#define CMD_H
#include <inttypes.h>
#include "host/ble_uuid.h"
struct kv_pair {
char *key;
int val;
};
uint32_t parse_arg_time_dflt(char *name, int step, uint32_t dflt, int *out_status);
const struct kv_pair *parse_kv_find(const struct kv_pair *kvs, char *name);
int parse_arg_find_idx(const char *key);
char *parse_arg_extract(const char *key);
long parse_arg_long_bounds(char *name, long min, long max, int *out_status);
long parse_arg_long_bounds_dflt(char *name, long min, long max,
long dflt, int *out_status);
uint64_t parse_arg_uint64_bounds(char *name, uint64_t min,
uint64_t max, int *out_status);
long parse_arg_long(char *name, int *staus);
uint8_t parse_arg_bool(char *name, int *status);
uint8_t parse_arg_bool_dflt(char *name, uint8_t dflt, int *out_status);
uint8_t parse_arg_uint8(char *name, int *status);
uint8_t parse_arg_uint8_dflt(char *name, uint8_t dflt, int *out_status);
uint16_t parse_arg_uint16(char *name, int *status);
uint16_t parse_arg_uint16_dflt(char *name, uint16_t dflt, int *out_status);
uint32_t parse_arg_uint32(char *name, int *out_status);
uint32_t parse_arg_uint32_dflt(char *name, uint32_t dflt, int *out_status);
uint64_t parse_arg_uint64(char *name, int *out_status);
int parse_arg_kv(char *name, const struct kv_pair *kvs, int *out_status);
int parse_arg_kv_dflt(char *name, const struct kv_pair *kvs, int def_val,
int *out_status);
int parse_arg_byte_stream(char *name, int max_len, uint8_t *dst, int *out_len);
int parse_arg_uint8_list_with_separator(char *name, char *separator, int max_len,
uint8_t *dst, int *out_len);
int parse_arg_byte_stream_exact_length(char *name, uint8_t *dst, int len);
int parse_arg_mac(char *name, uint8_t *dst);
int parse_arg_addr(char *name, ble_addr_t *addr);
int parse_arg_uuid(char *name, ble_uuid_any_t *uuid);
int parse_arg_all(int argc, char **argv);
int parse_eddystone_url(char *full_url, uint8_t *out_scheme, char *out_body,
uint8_t *out_body_len, uint8_t *out_suffix);
int cmd_parse_conn_start_end(uint16_t *out_conn, uint16_t *out_start,
uint16_t *out_end);
void cmd_init(void);
#endif

View file

@ -0,0 +1,587 @@
/*
* 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 <inttypes.h>
#include <errno.h>
#include "bsp/bsp.h"
#include "host/ble_hs_mbuf.h"
#include "host/ble_gap.h"
#include "services/gatt/ble_svc_gatt.h"
#include "console/console.h"
#include "btshell.h"
#include "cmd.h"
#include "cmd_gatt.h"
#define CMD_BUF_SZ 256
static bssnz_t uint8_t cmd_buf[CMD_BUF_SZ];
/*****************************************************************************
* $gatt-discover *
*****************************************************************************/
int
cmd_gatt_discover_characteristic(int argc, char **argv)
{
uint16_t start_handle;
uint16_t conn_handle;
uint16_t end_handle;
ble_uuid_any_t uuid;
int rc;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
rc = cmd_parse_conn_start_end(&conn_handle, &start_handle, &end_handle);
if (rc != 0) {
console_printf("invalid 'conn start end' parameter\n");
return rc;
}
rc = parse_arg_uuid("uuid", &uuid);
if (rc == 0) {
rc = btshell_disc_chrs_by_uuid(conn_handle, start_handle, end_handle,
&uuid.u);
} else if (rc == ENOENT) {
rc = btshell_disc_all_chrs(conn_handle, start_handle, end_handle);
} else {
console_printf("invalid 'uuid' parameter\n");
return rc;
}
if (rc != 0) {
console_printf("error discovering characteristics; rc=%d\n", rc);
return rc;
}
return 0;
}
int
cmd_gatt_discover_descriptor(int argc, char **argv)
{
uint16_t start_handle;
uint16_t conn_handle;
uint16_t end_handle;
int rc;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
rc = cmd_parse_conn_start_end(&conn_handle, &start_handle, &end_handle);
if (rc != 0) {
console_printf("invalid 'conn start end' parameter\n");
return rc;
}
rc = btshell_disc_all_dscs(conn_handle, start_handle, end_handle);
if (rc != 0) {
console_printf("error discovering descriptors; rc=%d\n", rc);
return rc;
}
return 0;
}
int
cmd_gatt_discover_service(int argc, char **argv)
{
ble_uuid_any_t uuid;
int conn_handle;
int rc;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
conn_handle = parse_arg_uint16("conn", &rc);
if (rc != 0) {
console_printf("invalid 'conn' parameter\n");
return rc;
}
rc = parse_arg_uuid("uuid", &uuid);
if (rc == 0) {
rc = btshell_disc_svc_by_uuid(conn_handle, &uuid.u);
} else if (rc == ENOENT) {
rc = btshell_disc_svcs(conn_handle);
} else {
console_printf("invalid 'uuid' parameter\n");
return rc;
}
if (rc != 0) {
console_printf("error discovering services; rc=%d\n", rc);
return rc;
}
return 0;
}
int
cmd_gatt_discover_full(int argc, char **argv)
{
int conn_handle;
int rc;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
conn_handle = parse_arg_uint16("conn", &rc);
if (rc != 0) {
console_printf("invalid 'conn' parameter\n");
return rc;
}
rc = btshell_disc_full(conn_handle);
if (rc != 0) {
console_printf("error discovering all; rc=%d\n", rc);
return rc;
}
return 0;
}
/*****************************************************************************
* $gatt-exchange-mtu *
*****************************************************************************/
int
cmd_gatt_exchange_mtu(int argc, char **argv)
{
uint16_t conn_handle;
int rc;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
conn_handle = parse_arg_uint16("conn", &rc);
if (rc != 0) {
console_printf("invalid 'conn' parameter\n");
return rc;
}
rc = btshell_exchange_mtu(conn_handle);
if (rc != 0) {
console_printf("error exchanging mtu; rc=%d\n", rc);
return rc;
}
return 0;
}
/*****************************************************************************
* $gatt-notify *
*****************************************************************************/
int
cmd_gatt_notify(int argc, char **argv)
{
uint16_t attr_handle;
int rc;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
attr_handle = parse_arg_uint16("attr", &rc);
if (rc != 0) {
console_printf("invalid 'attr' parameter\n");
return rc;
}
btshell_notify(attr_handle);
return 0;
}
/*****************************************************************************
* $gatt-read *
*****************************************************************************/
#define CMD_READ_MAX_ATTRS 8
int
cmd_gatt_read(int argc, char **argv)
{
static uint16_t attr_handles[CMD_READ_MAX_ATTRS];
uint16_t conn_handle;
uint16_t start;
uint16_t end;
uint16_t offset;
ble_uuid_any_t uuid;
uint8_t num_attr_handles;
int is_uuid;
int is_long;
int rc;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
conn_handle = parse_arg_uint16("conn", &rc);
if (rc != 0) {
console_printf("invalid 'conn' parameter\n");
return rc;
}
is_long = parse_arg_long("long", &rc);
if (rc == ENOENT) {
is_long = 0;
} else if (rc != 0) {
console_printf("invalid 'long' parameter\n");
return rc;
}
for (num_attr_handles = 0;
num_attr_handles < CMD_READ_MAX_ATTRS;
num_attr_handles++) {
attr_handles[num_attr_handles] = parse_arg_uint16("attr", &rc);
if (rc == ENOENT) {
break;
} else if (rc != 0) {
console_printf("invalid 'attr' parameter\n");
return rc;
}
}
rc = parse_arg_uuid("uuid", &uuid);
if (rc == ENOENT) {
is_uuid = 0;
} else if (rc == 0) {
is_uuid = 1;
} else {
console_printf("invalid 'uuid' parameter\n");
return rc;
}
start = parse_arg_uint16("start", &rc);
if (rc == ENOENT) {
start = 0;
} else if (rc != 0) {
console_printf("invalid 'start' parameter\n");
return rc;
}
end = parse_arg_uint16("end", &rc);
if (rc == ENOENT) {
end = 0;
} else if (rc != 0) {
console_printf("invalid 'end' parameter\n");
return rc;
}
offset = parse_arg_uint16("offset", &rc);
if (rc == ENOENT) {
offset = 0;
} else if (rc != 0) {
console_printf("invalid 'offset' parameter\n");
return rc;
}
if (num_attr_handles == 1) {
if (is_long) {
rc = btshell_read_long(conn_handle, attr_handles[0], offset);
} else {
rc = btshell_read(conn_handle, attr_handles[0]);
}
} else if (num_attr_handles > 1) {
rc = btshell_read_mult(conn_handle, attr_handles, num_attr_handles);
} else if (is_uuid) {
if (start == 0 || end == 0) {
rc = EINVAL;
} else {
rc = btshell_read_by_uuid(conn_handle, start, end, &uuid.u);
}
} else {
rc = EINVAL;
}
if (rc != 0) {
console_printf("error reading characteristic; rc=%d\n", rc);
return rc;
}
return 0;
}
/*****************************************************************************
* $gatt-service-changed *
*****************************************************************************/
int
cmd_gatt_service_changed(int argc, char **argv)
{
uint16_t start;
uint16_t end;
int rc;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
start = parse_arg_uint16("start", &rc);
if (rc != 0) {
console_printf("invalid 'start' parameter\n");
return rc;
}
end = parse_arg_uint16("end", &rc);
if (rc != 0) {
console_printf("invalid 'end' parameter\n");
return rc;
}
ble_svc_gatt_changed(start, end);
return 0;
}
/*****************************************************************************
* $gatt-service-visibility *
*****************************************************************************/
int
cmd_gatt_service_visibility(int argc, char **argv)
{
uint16_t handle;
bool vis;
int rc;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
handle = parse_arg_uint16("handle", &rc);
if (rc != 0) {
console_printf("invalid 'handle' parameter\n");
return rc;
}
vis = parse_arg_bool("visibility", &rc);
if (rc != 0) {
console_printf("invalid 'visibility' parameter\n");
return rc;
}
ble_gatts_svc_set_visibility(handle, vis);
return 0;
}
/*****************************************************************************
* $gatt-find-included-services *
*****************************************************************************/
int
cmd_gatt_find_included_services(int argc, char **argv)
{
uint16_t start_handle;
uint16_t conn_handle;
uint16_t end_handle;
int rc;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
rc = cmd_parse_conn_start_end(&conn_handle, &start_handle, &end_handle);
if (rc != 0) {
console_printf("invalid 'conn start end' parameter\n");
return rc;
}
rc = btshell_find_inc_svcs(conn_handle, start_handle, end_handle);
if (rc != 0) {
console_printf("error finding included services; rc=%d\n", rc);
return rc;
}
return 0;
}
/*****************************************************************************
* $gatt-show *
*****************************************************************************/
int
cmd_gatt_show(int argc, char **argv)
{
struct btshell_conn *conn;
struct btshell_svc *svc;
int i;
for (i = 0; i < btshell_num_conns; i++) {
conn = btshell_conns + i;
console_printf("CONNECTION: handle=%d\n", conn->handle);
SLIST_FOREACH(svc, &conn->svcs, next) {
print_svc(svc);
}
}
return 0;
}
int
cmd_gatt_show_local(int argc, char **argv)
{
gatt_svr_print_svcs();
return 0;
}
/*****************************************************************************
* $gatt-write *
*****************************************************************************/
int
cmd_gatt_write(int argc, char **argv)
{
struct ble_gatt_attr attrs[MYNEWT_VAL(BLE_GATT_WRITE_MAX_ATTRS)] = { { 0 } };
uint16_t attr_handle;
uint16_t conn_handle;
uint16_t offset;
int total_attr_len;
int num_attrs;
int attr_len;
int is_long;
int no_rsp;
int rc;
int i;
rc = parse_arg_all(argc - 1, argv + 1);
if (rc != 0) {
return rc;
}
conn_handle = parse_arg_uint16("conn", &rc);
if (rc != 0) {
console_printf("invalid 'conn' parameter\n");
return rc;
}
no_rsp = parse_arg_bool_dflt("no_rsp", 0, &rc);
if (rc != 0) {
console_printf("invalid 'no_rsp' parameter\n");
return rc;
}
is_long = parse_arg_bool_dflt("long", 0, &rc);
if (rc != 0) {
console_printf("invalid 'long' parameter\n");
return rc;
}
total_attr_len = 0;
num_attrs = 0;
while (1) {
attr_handle = parse_arg_uint16("attr", &rc);
if (rc == ENOENT) {
break;
} else if (rc != 0) {
rc = -rc;
console_printf("invalid 'attr' parameter\n");
goto done;
}
rc = parse_arg_byte_stream("value", sizeof cmd_buf - total_attr_len,
cmd_buf + total_attr_len, &attr_len);
if (rc == ENOENT) {
break;
} else if (rc != 0) {
console_printf("invalid 'value' parameter\n");
goto done;
}
offset = parse_arg_uint16("offset", &rc);
if (rc == ENOENT) {
offset = 0;
} else if (rc != 0) {
console_printf("invalid 'offset' parameter\n");
return rc;
}
if (num_attrs >= sizeof attrs / sizeof attrs[0]) {
rc = -EINVAL;
goto done;
}
attrs[num_attrs].handle = attr_handle;
attrs[num_attrs].offset = offset;
attrs[num_attrs].om = ble_hs_mbuf_from_flat(cmd_buf + total_attr_len,
attr_len);
if (attrs[num_attrs].om == NULL) {
goto done;
}
total_attr_len += attr_len;
num_attrs++;
}
if (no_rsp) {
if (num_attrs != 1) {
rc = -EINVAL;
goto done;
}
rc = btshell_write_no_rsp(conn_handle, attrs[0].handle, attrs[0].om);
attrs[0].om = NULL;
} else if (is_long) {
if (num_attrs != 1) {
rc = -EINVAL;
goto done;
}
rc = btshell_write_long(conn_handle, attrs[0].handle,
attrs[0].offset, attrs[0].om);
attrs[0].om = NULL;
} else if (num_attrs > 1) {
rc = btshell_write_reliable(conn_handle, attrs, num_attrs);
} else if (num_attrs == 1) {
rc = btshell_write(conn_handle, attrs[0].handle, attrs[0].om);
attrs[0].om = NULL;
} else {
rc = -EINVAL;
}
done:
for (i = 0; i < sizeof attrs / sizeof attrs[0]; i++) {
os_mbuf_free_chain(attrs[i].om);
}
if (rc != 0) {
console_printf("error writing characteristic; rc=%d\n", rc);
}
return rc;
}

Some files were not shown because too many files have changed in this diff Show more