Merge branch 'feat/ble_mesh_sensor_model_example_v3.3' into 'release/v3.3'

ble_mesh: Add ble mesh sensor model examples (v3.3)

See merge request espressif/esp-idf!8509
This commit is contained in:
Island 2020-05-06 11:11:36 +08:00
commit 93a13dfd10
38 changed files with 3036 additions and 39 deletions

View file

@ -345,6 +345,91 @@ esp_err_t esp_ble_mesh_sensor_client_set_state(esp_ble_mesh_client_common_param_
*/
#define ESP_BLE_MESH_SENSOR_DATA_ZERO_LEN 0x7F
/** @def ESP_BLE_MESH_GET_SENSOR_DATA_FORMAT
*
* @brief Get format of the sensor data.
*
* @note Multiple sensor data may be concatenated. Make sure the _data pointer is
* updated before getting the format of the corresponding sensor data.
*
* @param _data Pointer to the start of the sensor data.
*
* @return Format of the sensor data.
*/
#define ESP_BLE_MESH_GET_SENSOR_DATA_FORMAT(_data) (((_data)[0]) & BIT_MASK(1))
/** @def ESP_BLE_MESH_GET_SENSOR_DATA_LENGTH
*
* @brief Get length of the sensor data.
*
* @note Multiple sensor data may be concatenated. Make sure the _data pointer is
* updated before getting the length of the corresponding sensor data.
*
* @param _data Pointer to the start of the sensor data.
* @param _fmt Format of the sensor data.
*
* @return Length (zero-based) of the sensor data.
*/
#define ESP_BLE_MESH_GET_SENSOR_DATA_LENGTH(_data, _fmt) \
(((_fmt) == ESP_BLE_MESH_SENSOR_DATA_FORMAT_A) ? ((((_data)[0]) >> 1) & BIT_MASK(4)) : ((((_data)[0]) >> 1) & BIT_MASK(7)))
/** @def ESP_BLE_MESH_GET_SENSOR_DATA_PROPERTY_ID
*
* @brief Get Sensor Property ID of the sensor data.
*
* @note Multiple sensor data may be concatenated. Make sure the _data pointer is
* updated before getting Sensor Property ID of the corresponding sensor data.
*
* @param _data Pointer to the start of the sensor data.
* @param _fmt Format of the sensor data.
*
* @return Sensor Property ID of the sensor data.
*/
#define ESP_BLE_MESH_GET_SENSOR_DATA_PROPERTY_ID(_data, _fmt) \
(((_fmt) == ESP_BLE_MESH_SENSOR_DATA_FORMAT_A) ? ((((_data)[1]) << 3) | (((_data)[0]) >> 5)) : ((((_data)[2]) << 8) | ((_data)[1])))
/** @def ESP_BLE_MESH_SENSOR_DATA_FORMAT_A_MPID
*
* @brief Generate a MPID value for sensor data with Format A.
*
* @note 1. The Format field is 0b0 and indicates that Format A is used.
* 2. The Length field is a 1-based uint4 value (valid range 0x00xF,
* representing range of 116).
* 3. The Property ID is an 11-bit bit field representing 11 LSb of a Property ID.
* 4. This format may be used for Property Values that are not longer than 16
* octets and for Property IDs less than 0x0800.
*
* @param _len Length of Sensor Raw value.
* @param _id Sensor Property ID.
*
* @return 2-octet MPID value for sensor data with Format A.
*
*/
#define ESP_BLE_MESH_SENSOR_DATA_FORMAT_A_MPID(_len, _id) \
((((_id) & BIT_MASK(11)) << 5) | (((_len) & BIT_MASK(4)) << 1) | ESP_BLE_MESH_SENSOR_DATA_FORMAT_A)
/** @def ESP_BLE_MESH_SENSOR_DATA_FORMAT_B_MPID
*
* @brief Generate a MPID value for sensor data with Format B.
*
* @note 1. The Format field is 0b1 and indicates Format B is used.
* 2. The Length field is a 1-based uint7 value (valid range 0x00x7F, representing
* range of 1127). The value 0x7F represents a length of zero.
* 3. The Property ID is a 16-bit bit field representing a Property ID.
* 4. This format may be used for Property Values not longer than 128 octets and for
* any Property IDs. Property values longer than 128 octets are not supported by
* the Sensor Status message.
* 5. Exclude the generated 1-octet value, the 2-octet Sensor Property ID
*
* @param _len Length of Sensor Raw value.
* @param _id Sensor Property ID.
*
* @return 3-octet MPID value for sensor data with Format B.
*
*/
#define ESP_BLE_MESH_SENSOR_DATA_FORMAT_B_MPID(_len, _id) \
(((_id) << 8) | (((_len) & BIT_MASK(7)) << 1) | ESP_BLE_MESH_SENSOR_DATA_FORMAT_B)
/** This enum value is value of Sensor Sampling Function */
enum esp_ble_mesh_sensor_sample_func {
ESP_BLE_MESH_SAMPLE_FUNC_UNSPECIFIED,

View file

@ -290,7 +290,8 @@ static void send_sensor_settings_status(struct bt_mesh_model *model,
for (i = 0; i < srv->state_count; i++) {
state = &srv->states[i];
if (state->sensor_property_id != INVALID_SENSOR_PROPERTY_ID &&
state->sensor_property_id == prop_id) {
state->sensor_property_id == prop_id &&
state->setting_count && state->settings) {
for (j = 0; j < state->setting_count; j++) {
item = &state->settings[j];
if (item->property_id != INVALID_SENSOR_SETTING_PROPERTY_ID) {
@ -325,7 +326,8 @@ static struct sensor_setting *find_sensor_setting(struct bt_mesh_model *model,
for (i = 0; i < srv->state_count; i++) {
state = &srv->states[i];
if (state->sensor_property_id != INVALID_SENSOR_PROPERTY_ID &&
state->sensor_property_id == prop_id) {
state->sensor_property_id == prop_id &&
state->setting_count && state->settings) {
for (j = 0; j < state->setting_count; j++) {
item = &state->settings[j];
if (item->property_id != INVALID_SENSOR_SETTING_PROPERTY_ID &&
@ -422,14 +424,22 @@ static void send_sensor_column_status(struct bt_mesh_model *model,
state = &srv->states[i];
if (state->sensor_property_id != INVALID_SENSOR_PROPERTY_ID &&
state->sensor_property_id == prop_id) {
length = SENSOR_PROPERTY_ID_LEN + state->series_column.raw_value_x->len;
length = SENSOR_PROPERTY_ID_LEN;
if (state->series_column.raw_value_x) {
length += state->series_column.raw_value_x->len;
}
/**
* TODO: column width & raw value y in Sensor Column Status are optional,
* here we need to add some conditions to decide whether put these two
* in the status message.
*/
if (optional) {
length += state->series_column.column_width->len + state->series_column.raw_value_y->len;
if (state->series_column.column_width) {
length += state->series_column.column_width->len;
}
if (state->series_column.raw_value_y) {
length += state->series_column.raw_value_y->len;
}
}
break;
}
@ -453,13 +463,19 @@ static void send_sensor_column_status(struct bt_mesh_model *model,
bt_mesh_model_msg_init(msg, BLE_MESH_MODEL_OP_SENSOR_COLUMN_STATUS);
net_buf_simple_add_le16(msg, prop_id);
if (i != srv->state_count) {
net_buf_simple_add_mem(msg, state->series_column.raw_value_x->data,
state->series_column.raw_value_x->len);
if (state->series_column.raw_value_x) {
net_buf_simple_add_mem(msg, state->series_column.raw_value_x->data,
state->series_column.raw_value_x->len);
}
if (optional) {
net_buf_simple_add_mem(msg, state->series_column.column_width->data,
state->series_column.column_width->len);
net_buf_simple_add_mem(msg, state->series_column.raw_value_y->data,
state->series_column.raw_value_y->len);
if (state->series_column.column_width) {
net_buf_simple_add_mem(msg, state->series_column.column_width->data,
state->series_column.column_width->len);
}
if (state->series_column.raw_value_y) {
net_buf_simple_add_mem(msg, state->series_column.raw_value_y->data,
state->series_column.raw_value_y->len);
}
}
}
@ -489,9 +505,15 @@ static void send_sensor_series_status(struct bt_mesh_model *model,
* decide whether put these three in the status message.
*/
if (optional) {
length += state->series_column.raw_value_x->len +
state->series_column.column_width->len +
state->series_column.raw_value_y->len;
if (state->series_column.raw_value_x) {
length += state->series_column.raw_value_x->len;
}
if (state->series_column.column_width) {
length += state->series_column.column_width->len;
}
if (state->series_column.raw_value_y) {
length += state->series_column.raw_value_y->len;
}
}
break;
}
@ -517,12 +539,18 @@ static void send_sensor_series_status(struct bt_mesh_model *model,
net_buf_simple_add_le16(msg, prop_id);
if (i != srv->state_count) {
if (optional) {
net_buf_simple_add_mem(msg, state->series_column.raw_value_x->data,
state->series_column.raw_value_x->len);
net_buf_simple_add_mem(msg, state->series_column.column_width->data,
state->series_column.column_width->len);
net_buf_simple_add_mem(msg, state->series_column.raw_value_y->data,
state->series_column.raw_value_y->len);
if (state->series_column.raw_value_x) {
net_buf_simple_add_mem(msg, state->series_column.raw_value_x->data,
state->series_column.raw_value_x->len);
}
if (state->series_column.column_width) {
net_buf_simple_add_mem(msg, state->series_column.column_width->data,
state->series_column.column_width->len);
}
if (state->series_column.raw_value_y) {
net_buf_simple_add_mem(msg, state->series_column.raw_value_y->data,
state->series_column.raw_value_y->len);
}
}
}
@ -972,17 +1000,29 @@ static int check_sensor_server_init(struct bt_mesh_sensor_state *state_start,
BT_ERR("%s, Invalid Sensor Property ID 0x%04x", __func__, state->sensor_property_id);
return -EINVAL;
}
if (state->setting_count == 0U || state->settings == NULL) {
BT_ERR("%s, Invalid Sensor Setting state", __func__);
return -EINVAL;
}
for (j = 0; j < state->setting_count; j++) {
setting = &state->settings[j];
if (setting->property_id == INVALID_SENSOR_SETTING_PROPERTY_ID || setting->raw == NULL) {
BT_ERR("%s, Invalid Sensor Setting state internal parameter", __func__);
/* Check if the same Sensor Property ID exists */
for (int k = i + 1; k < state_count; k++) {
if (state->sensor_property_id == state_start[k].sensor_property_id) {
BT_ERR("%s, Same Sensor Property ID 0x%04x exists", __func__, state->sensor_property_id);
return -EINVAL;
}
}
if (state->setting_count && state->settings) {
for (j = 0; j < state->setting_count; j++) {
setting = &state->settings[j];
if (setting->property_id == INVALID_SENSOR_SETTING_PROPERTY_ID || setting->raw == NULL) {
BT_ERR("%s, Invalid Sensor Setting state", __func__);
return -EINVAL;
}
/* Check if the same Sensor Setting Property ID exists */
for (int k = j + 1; k < state->setting_count; k++) {
if (setting->property_id == state->settings[k].property_id) {
BT_ERR("%s, Same Sensor Setting Property ID 0x%04x exists", __func__, setting->property_id);
return -EINVAL;
}
}
}
}
if (state->cadence) {
if (state->cadence->trigger_delta_down == NULL ||
state->cadence->trigger_delta_up == NULL ||
@ -996,12 +1036,6 @@ static int check_sensor_server_init(struct bt_mesh_sensor_state *state_start,
BT_ERR("%s, Invalid Sensor Data state", __func__);
return -EINVAL;
}
if (state->series_column.raw_value_x == NULL ||
state->series_column.column_width == NULL ||
state->series_column.raw_value_y == NULL) {
BT_ERR("%s, Invalid Sensor Series column state", __func__);
return -EINVAL;
}
}
return 0;
@ -1018,11 +1052,10 @@ static int sensor_server_init(struct bt_mesh_model *model)
case BLE_MESH_MODEL_ID_SENSOR_SRV: {
struct bt_mesh_sensor_srv *srv = model->user_data;
if (srv->state_count == 0U || srv->states == NULL) {
BT_ERR("%s, Invalid Sensor state parameter, model_id 0x%04x", __func__, model->id);
BT_ERR("%s, Invalid Sensor state, model_id 0x%04x", __func__, model->id);
return -EINVAL;
}
if (check_sensor_server_init(srv->states, srv->state_count)) {
BT_ERR("%s, Invalid Sensor Server init value", __func__);
return -EINVAL;
}
srv->model = model;
@ -1031,11 +1064,10 @@ static int sensor_server_init(struct bt_mesh_model *model)
case BLE_MESH_MODEL_ID_SENSOR_SETUP_SRV: {
struct bt_mesh_sensor_setup_srv *srv = model->user_data;
if (srv->state_count == 0U || srv->states == NULL) {
BT_ERR("%s, Invalid parameter, model_id 0x%04x", __func__, model->id);
BT_ERR("%s, Invalid Sensor state, model_id 0x%04x", __func__, model->id);
return -EINVAL;
}
if (check_sensor_server_init(srv->states, srv->state_count)) {
BT_ERR("%s, Invalid Sensor Setup Server init value", __func__);
return -EINVAL;
}
srv->model = model;

View file

@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(sensor_client)

View file

@ -0,0 +1,10 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := sensor_client
COMPONENT_ADD_INCLUDEDIRS := components/include
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,89 @@
| Supported Targets | ESP32 |
| ----------------- | ----- |
ESP BLE Mesh Sensor Client Example
==================================
This example demonstrates how to create a sensor client model in Provisioner, and the [sensor server example](../sensor_server) demonstrates how to create a sensor server model and a sensor setup server model in an unprovisioned device.
### 1. Introduction
Sensor Server model is model which is used to expose a series of sensor states. When Sensor Server model is present on an element, the Sensor Setup Server model shall also be present as an extension of the Sensor Server model.
Sensor Client model is used to consume the state values exposed by the Sensor Server/Setup Server models via Sensor messages.
Sensor state is a composite state which contains the following parts:
* Sensor Descriptor state
* Sensor Setting state
* Sensor Cadence state
* Sensor Data state
* Sensor Series Column state
#### Sensor Descriptor state
Sensor Descriptor state represents the attributes describing the sensor data. This state does not change throughout the lifetime of an element.
#### Sensor Setting state
Sensor Setting state controls parameters of a sensor. For example, an occupancy sensor may have a “sensitivity” setting that controls the sensitivity of the sensor. Sensitivity may be adjusted to prevent small animals from triggering the sensor.
#### Sensor Cadence state
Sensor Cadence state controls the cadence of sensor reports. It allows a sensor to be configured to send measured values using Sensor Status messages at a different cadence for a range of measured values. It also allows a sensor to be configured to send measured values when the value changes up or down by more than a configured delta value.
#### Sensor Data state
Sensor Data state is a sequence of one or more pairs of Sensor Property ID and Raw Value fields, with each Raw Value field size and representation defined by the characteristics referenced by the Sensor Property ID.
#### Sensor Series Column state
Values measured by sensors may be organized as arrays (and represented as series of columns, such as histograms). It contains Sensor Raw Value X, Sensor Column Width and Sensor Raw Value Y.
> Note: For the supported sensor types, please refer to the [Mesh Device Properties Spec](https://www.bluetooth.com/specifications/mesh-specifications/mesh-properties/).
### 2. Example Description
#### Sensor Client example
In the [Sensor Client example](./), the mesh device is a Provisioner with a sensor client model. Once the device which runs the sensor server example is provisioned and configured successfully by the Provisioner, users can press the button to send sensor messages to get the corresponding state (i.e. sensor descriptor, sensor data, etc.) values.
#### Sensor Server example
In the [Sensor Server example](../sensor_server), the mesh device is an unprovisioned device with a sensor server model and a sensor setup server model.
The sensor server model supports two instances of Sensor states, the first one (Sensor Property ID 0x0056) represents the "Present Indoor Ambient Temperature", and the other (Sensor Property ID 0x005B) represents the "Present Outdoor Ambient Temperature". The characteristic of the two properties is "Temperature 8", which is used to represent a measure of temperature with a unit of 0.5 degree Celsius. The minimum value of the characteristic is -64.0, and the maximum value is 63.5.
Currently only the Sensor Descriptor state, Sensor Data state are supported by the Sensor states. The Sensor Property ID, Sensor Descriptor state, format and length of the corresponding Sensor Data state are all pre-initialized.
### 3. Procedures
* Device A runs the sensor client example, and device B runs the sensor server example;
* Working as a Provisioner, once the unprovisioned device beacon from device B is received by device A, device A will provision and configure device B into a node;
* After the provisioning and configuration procedure is completed successfully, users can press the "Boot" button on device A to get the sensor state values of device B;
* Based on the press count, different sensor messages will be sent to device B. Sensor Descriptor Get will be sent when the button is pressed for the first time, at the second time Sensor Cadence Get will be sent, and the following messages are: Sensor Settings Get, Sensor Get and Sensor Series Get;
* If continuing to press the button, the same sensor messages with the above sequence will be sent to device B.
### 4. Relationship
This section is about the relationship between sensor models and messages, which gives a list of messages that can be sent or published by a specific sensor model.
* Sensor Client model
* Sensor Descriptor Get
* Sensor Cadence Get
* Sensor Cadence Set
* Sensor Cadence Set Unacknowledged
* Sensor Settings Get
* Sensor Setting Get
* Sensor Setting Set
* Sensor Setting Set Unacknowledged
* Sensor Get
* Sensor Column Get
* Sensor Series Get
* Sensor Server model
* Sensor Descriptor Status
* Sensor Status
* Sensor Column Status
* Sensor Series Status
* Sensor Setup Server model
* Sensor Cadence Status
* Sensor Settings Status
* Sensor Setting Status

View file

@ -0,0 +1,7 @@
set(COMPONENT_SRCS "button.c"
"button_obj.cpp")
set(COMPONENT_ADD_INCLUDEDIRS ". include")
register_component()

View file

@ -0,0 +1,21 @@
menu "Button"
choice BUTTON_TIMER_IMPLEMENT
bool "Button Timer Mode"
default BUTTON_USE_ESP_TIMER
help
Choose a implementation of timer for button instance.
config BUTTON_USE_RTOS_TIMER
bool "Use FreeRTOS Timer"
config BUTTON_USE_ESP_TIMER
bool "Use ESP Timer"
endchoice
config BUTTON_IO_GLITCH_FILTER_TIME_MS
int "IO glitch filter timer ms (10~100)"
range 10 100
default 50
endmenu

View file

@ -0,0 +1,46 @@
# Component: Button
* This component defines a button as a well encapsulated object.
* A button device is defined by:
* GPIO number on which the button is attached.
* Active level which decided by peripheral hardware.
* Trigger mode which decides whether to call serial trigger callback during pressing
* Serial threshold seconds which decides that serial trigger callback will be called after how many seconds' pressing
* A button device can provide:
* One push event callback
* One release event callback
* One short-time tap event callback
* One serial trigger event callback
* Several long-time press event callback
We can set different jitter filters for all the events.
Once any of the long press callback is triggered, the short tap event will not be triggered.
These components are based on GPIO provided by ESP-IDF and soft timer provided by FreeRTOS.
* To use the button device, you need to:
* create a button object returned by iot_button_create().
* Then hook different event callbacks to the button object.
* To free the object, you can call iot_button_delete to delete the button object and free the used memory.
* Todo: Add hardware timer mode(because sometimes soft-timer callback function is limited)
### NOTE:
> All the event callback function are implemented by FreeRTOS soft timer APIs, the callback must follow the rule:
```
Button callback functions execute in the context of the timer service task.
It is therefore essential that button callback functions never attempt to block.
For example, a button callback function must not call vTaskDelay(), vTaskDelayUntil(), or specify a non zero block time when accessing a queue or a semaphore.
```
> In addition:
> You can adjust the following macros within FreeRTOS to adjust the stack depth/queue length/task priority of the timer service.
```
#define configUSE_TIMERS //enable soft-timer
#define configTIMER_TASK_PRIORITY // priority of the timers service task
#define configQueue_LENGTH // length of timer command queue
#define configTIMER_TASK_STACK_DEPTH // stack depth of the soft-timer
```

View file

@ -0,0 +1,434 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// 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.
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/timers.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "iot_button.h"
#include "esp_timer.h"
#define USE_ESP_TIMER CONFIG_BUTTON_USE_ESP_TIMER
#if USE_ESP_TIMER
#define STOP_TIMER(tmr) esp_timer_stop(tmr)
#define DELETE_TIMER(tmr) esp_timer_delete(tmr)
#else
#define STOP_TIMER(tmr) xTimerStop(tmr, portMAX_DELAY)
#define DELETE_TIMER(tmr) xTimerDelete(tmr, portMAX_DELAY);
#endif
#define IOT_CHECK(tag, a, ret) if(!(a)) { \
ESP_LOGE(tag,"%s:%d (%s)", __FILE__, __LINE__, __FUNCTION__); \
return (ret); \
}
#define ERR_ASSERT(tag, param) IOT_CHECK(tag, (param) == ESP_OK, ESP_FAIL)
#define POINT_ASSERT(tag, param, ret) IOT_CHECK(tag, (param) != NULL, (ret))
typedef enum {
BUTTON_STATE_IDLE = 0,
BUTTON_STATE_PUSH,
BUTTON_STATE_PRESSED,
} button_status_t;
typedef struct button_dev button_dev_t;
typedef struct btn_cb button_cb_t;
struct btn_cb{
TickType_t interval;
button_cb cb;
void* arg;
#if !USE_ESP_TIMER
TimerHandle_t tmr;
#else
esp_timer_handle_t tmr;
#endif
button_dev_t *pbtn;
button_cb_t *next_cb;
};
struct button_dev{
uint8_t io_num;
uint8_t active_level;
uint32_t serial_thres_sec;
button_status_t state;
button_cb_t tap_short_cb;
button_cb_t tap_psh_cb;
button_cb_t tap_rls_cb;
button_cb_t press_serial_cb;
button_cb_t* cb_head;
};
#define BUTTON_GLITCH_FILTER_TIME_MS CONFIG_BUTTON_IO_GLITCH_FILTER_TIME_MS
static const char* TAG = "button";
// static void button_press_cb(xTimerHandle tmr)
static void button_press_cb(void* tmr)
{
#if !USE_ESP_TIMER
button_cb_t* btn_cb = (button_cb_t*) pvTimerGetTimerID(tmr);
#else
button_cb_t* btn_cb = (button_cb_t*)(tmr);
#endif
button_dev_t* btn = btn_cb->pbtn;
// low, then restart
if (btn->active_level == gpio_get_level(btn->io_num)) {
btn->state = BUTTON_STATE_PRESSED;
if (btn_cb->cb) {
btn_cb->cb(btn_cb->arg);
}
}
}
// static void button_tap_psh_cb(xTimerHandle tmr)
static void button_tap_psh_cb(void* tmr)
{
#if !USE_ESP_TIMER
button_cb_t* btn_cb = (button_cb_t*) pvTimerGetTimerID(tmr);
#else
button_cb_t* btn_cb = (button_cb_t*)(tmr);
#endif
button_dev_t* btn = btn_cb->pbtn;
STOP_TIMER(btn->tap_rls_cb.tmr);
int lv = gpio_get_level(btn->io_num);
if (btn->active_level == lv) {
// high, then key is up
btn->state = BUTTON_STATE_PUSH;
if (btn->press_serial_cb.tmr) {
#if !USE_ESP_TIMER
xTimerChangePeriod(btn->press_serial_cb.tmr, btn->serial_thres_sec*1000 / portTICK_PERIOD_MS, portMAX_DELAY);
xTimerReset(btn->press_serial_cb.tmr, portMAX_DELAY);
#else
esp_timer_stop(btn->press_serial_cb.tmr);
esp_timer_start_once(btn->press_serial_cb.tmr, btn->serial_thres_sec * 1000 * 1000);
#endif
}
if (btn->tap_psh_cb.cb) {
btn->tap_psh_cb.cb(btn->tap_psh_cb.arg);
}
} else {
// 50ms, check if this is a real key up
if (btn->tap_rls_cb.tmr) {
STOP_TIMER(btn->tap_rls_cb.tmr);
#if !USE_ESP_TIMER
xTimerReset(btn->tap_rls_cb.tmr, portMAX_DELAY);
#else
esp_timer_start_once(btn->tap_rls_cb.tmr, btn->tap_rls_cb.interval * portTICK_PERIOD_MS * 1000);
#endif
}
}
}
static void button_tap_rls_cb(void* tmr)
{
#if !USE_ESP_TIMER
button_cb_t* btn_cb = (button_cb_t*) pvTimerGetTimerID(tmr);
#else
button_cb_t* btn_cb = (button_cb_t*)(tmr);
#endif
button_dev_t* btn = btn_cb->pbtn;
STOP_TIMER(btn->tap_rls_cb.tmr);
if (btn->active_level == gpio_get_level(btn->io_num)) {
} else {
// high, then key is up
button_cb_t *pcb = btn->cb_head;
while (pcb != NULL) {
if (pcb->tmr != NULL) {
STOP_TIMER(pcb->tmr);
}
pcb = pcb->next_cb;
}
if (btn->press_serial_cb.tmr && btn->press_serial_cb.tmr != NULL) {
STOP_TIMER(btn->press_serial_cb.tmr);
}
if (btn->tap_short_cb.cb && btn->state == BUTTON_STATE_PUSH) {
btn->tap_short_cb.cb(btn->tap_short_cb.arg);
}
if(btn->tap_rls_cb.cb && btn->state != BUTTON_STATE_IDLE) {
btn->tap_rls_cb.cb(btn->tap_rls_cb.arg);
}
btn->state = BUTTON_STATE_IDLE;
}
}
static void button_press_serial_cb(void* tmr)
{
#if !USE_ESP_TIMER
button_dev_t* btn = (button_dev_t*) pvTimerGetTimerID(tmr);
#else
button_dev_t* btn = (button_dev_t*)(tmr);
#endif
if (btn->press_serial_cb.cb) {
btn->press_serial_cb.cb(btn->press_serial_cb.arg);
}
#if !USE_ESP_TIMER
xTimerChangePeriod(btn->press_serial_cb.tmr, btn->press_serial_cb.interval, portMAX_DELAY);
xTimerReset(btn->press_serial_cb.tmr, portMAX_DELAY);
#else
esp_timer_stop(btn->press_serial_cb.tmr);
esp_timer_start_once(btn->press_serial_cb.tmr, btn->press_serial_cb.interval * portTICK_PERIOD_MS * 1000);
#endif
}
static void button_gpio_isr_handler(void* arg)
{
button_dev_t* btn = (button_dev_t*) arg;
portBASE_TYPE HPTaskAwoken = pdFALSE;
int level = gpio_get_level(btn->io_num);
if (level == btn->active_level) {
if (btn->tap_psh_cb.tmr) {
#if !USE_ESP_TIMER
xTimerStopFromISR(btn->tap_psh_cb.tmr, &HPTaskAwoken);
xTimerResetFromISR(btn->tap_psh_cb.tmr, &HPTaskAwoken);
#else
esp_timer_stop(btn->tap_psh_cb.tmr);
esp_timer_start_once(btn->tap_psh_cb.tmr, btn->tap_psh_cb.interval * portTICK_PERIOD_MS * 1000);
#endif
}
button_cb_t *pcb = btn->cb_head;
while (pcb != NULL) {
if (pcb->tmr != NULL) {
#if !USE_ESP_TIMER
xTimerStopFromISR(pcb->tmr, &HPTaskAwoken);
xTimerResetFromISR(pcb->tmr, &HPTaskAwoken);
#else
esp_timer_stop(pcb->tmr);
esp_timer_start_once(pcb->tmr, pcb->interval * portTICK_PERIOD_MS * 1000);
#endif
}
pcb = pcb->next_cb;
}
} else {
// 50ms, check if this is a real key up
if (btn->tap_rls_cb.tmr) {
#if !USE_ESP_TIMER
xTimerStopFromISR(btn->tap_rls_cb.tmr, &HPTaskAwoken);
xTimerResetFromISR(btn->tap_rls_cb.tmr, &HPTaskAwoken);
#else
esp_timer_stop(btn->tap_rls_cb.tmr);
esp_timer_start_once(btn->tap_rls_cb.tmr, btn->tap_rls_cb.interval * portTICK_PERIOD_MS * 1000);
#endif
}
}
if(HPTaskAwoken == pdTRUE) {
portYIELD_FROM_ISR();
}
}
#if !USE_ESP_TIMER
static void button_free_tmr(xTimerHandle* tmr)
#else
static void button_free_tmr(esp_timer_handle_t *tmr)
#endif
{
if (tmr && *tmr) {
STOP_TIMER(*tmr);
DELETE_TIMER(*tmr);
*tmr = NULL;
}
}
esp_err_t iot_button_delete(button_handle_t btn_handle)
{
POINT_ASSERT(TAG, btn_handle, ESP_ERR_INVALID_ARG);
button_dev_t* btn = (button_dev_t*) btn_handle;
gpio_set_intr_type(btn->io_num, GPIO_INTR_DISABLE);
gpio_isr_handler_remove(btn->io_num);
button_free_tmr(&btn->tap_rls_cb.tmr);
button_free_tmr(&btn->tap_psh_cb.tmr);
button_free_tmr(&btn->tap_short_cb.tmr);
button_free_tmr(&btn->press_serial_cb.tmr);
button_cb_t *pcb = btn->cb_head;
while (pcb != NULL) {
button_cb_t *cb_next = pcb->next_cb;
button_free_tmr(&pcb->tmr);
free(pcb);
pcb = cb_next;
}
free(btn);
return ESP_OK;
}
button_handle_t iot_button_create(gpio_num_t gpio_num, button_active_t active_level)
{
#if USE_ESP_TIMER
ets_printf("use esp timer !!!\n");
esp_timer_init();
#endif
IOT_CHECK(TAG, gpio_num < GPIO_NUM_MAX, NULL);
button_dev_t* btn = (button_dev_t*) calloc(1, sizeof(button_dev_t));
POINT_ASSERT(TAG, btn, NULL);
btn->active_level = active_level;
btn->io_num = gpio_num;
btn->state = BUTTON_STATE_IDLE;
btn->tap_rls_cb.arg = NULL;
btn->tap_rls_cb.cb = NULL;
btn->tap_rls_cb.interval = BUTTON_GLITCH_FILTER_TIME_MS / portTICK_PERIOD_MS;
btn->tap_rls_cb.pbtn = btn;
#if !USE_ESP_TIMER
btn->tap_rls_cb.tmr = xTimerCreate("btn_rls_tmr", btn->tap_rls_cb.interval, pdFALSE,
&btn->tap_rls_cb, button_tap_rls_cb);
#else
esp_timer_create_args_t tmr_param_rls;
tmr_param_rls.arg = &btn->tap_rls_cb;
tmr_param_rls.callback = button_tap_rls_cb;
tmr_param_rls.dispatch_method = ESP_TIMER_TASK;
tmr_param_rls.name = "btn_rls_tmr";
esp_timer_create(&tmr_param_rls, &btn->tap_rls_cb.tmr);
#endif
btn->tap_psh_cb.arg = NULL;
btn->tap_psh_cb.cb = NULL;
btn->tap_psh_cb.interval = BUTTON_GLITCH_FILTER_TIME_MS / portTICK_PERIOD_MS;
btn->tap_psh_cb.pbtn = btn;
#if !USE_ESP_TIMER
btn->tap_psh_cb.tmr = xTimerCreate("btn_psh_tmr", btn->tap_psh_cb.interval, pdFALSE,
&btn->tap_psh_cb, button_tap_psh_cb);
#else
esp_timer_create_args_t tmr_param_psh;
tmr_param_psh.arg = &btn->tap_psh_cb;
tmr_param_psh.callback = button_tap_psh_cb;
tmr_param_psh.dispatch_method = ESP_TIMER_TASK;
tmr_param_psh.name = "btn_psh_tmr";
esp_timer_create(&tmr_param_psh, &btn->tap_psh_cb.tmr);
#endif
gpio_install_isr_service(0);
gpio_config_t gpio_conf;
gpio_conf.intr_type = GPIO_INTR_ANYEDGE;
gpio_conf.mode = GPIO_MODE_INPUT;
gpio_conf.pin_bit_mask = (1ULL << gpio_num);
gpio_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
gpio_conf.pull_up_en = GPIO_PULLUP_ENABLE;
gpio_config(&gpio_conf);
gpio_isr_handler_add(gpio_num, button_gpio_isr_handler, btn);
return (button_handle_t) btn;
}
esp_err_t iot_button_rm_cb(button_handle_t btn_handle, button_cb_type_t type)
{
button_dev_t* btn = (button_dev_t*) btn_handle;
button_cb_t* btn_cb = NULL;
if (type == BUTTON_CB_PUSH) {
btn_cb = &btn->tap_psh_cb;
} else if (type == BUTTON_CB_RELEASE) {
btn_cb = &btn->tap_rls_cb;
} else if (type == BUTTON_CB_TAP) {
btn_cb = &btn->tap_short_cb;
} else if (type == BUTTON_CB_SERIAL) {
btn_cb = &btn->press_serial_cb;
}
btn_cb->cb = NULL;
btn_cb->arg = NULL;
btn_cb->pbtn = btn;
button_free_tmr(&btn_cb->tmr);
return ESP_OK;
}
esp_err_t iot_button_set_serial_cb(button_handle_t btn_handle, uint32_t start_after_sec, TickType_t interval_tick, button_cb cb, void* arg)
{
button_dev_t* btn = (button_dev_t*) btn_handle;
btn->serial_thres_sec = start_after_sec;
if (btn->press_serial_cb.tmr == NULL) {
#if !USE_ESP_TIMER
btn->press_serial_cb.tmr = xTimerCreate("btn_serial_tmr", btn->serial_thres_sec*1000 / portTICK_PERIOD_MS,
pdFALSE, btn, button_press_serial_cb);
#else
esp_timer_create_args_t tmr_param_ser;
tmr_param_ser.arg = btn;
tmr_param_ser.callback = button_press_serial_cb;
tmr_param_ser.dispatch_method = ESP_TIMER_TASK;
tmr_param_ser.name = "btn_serial_tmr";
esp_timer_create(&tmr_param_ser, &btn->press_serial_cb.tmr);
#endif
}
btn->press_serial_cb.arg = arg;
btn->press_serial_cb.cb = cb;
btn->press_serial_cb.interval = interval_tick;
btn->press_serial_cb.pbtn = btn;
#if !USE_ESP_TIMER
xTimerChangePeriod(btn->press_serial_cb.tmr, btn->serial_thres_sec*1000 / portTICK_PERIOD_MS, portMAX_DELAY);
#endif
return ESP_OK;
}
esp_err_t iot_button_set_evt_cb(button_handle_t btn_handle, button_cb_type_t type, button_cb cb, void* arg)
{
POINT_ASSERT(TAG, btn_handle, ESP_ERR_INVALID_ARG);
button_dev_t* btn = (button_dev_t*) btn_handle;
if (type == BUTTON_CB_PUSH) {
btn->tap_psh_cb.arg = arg;
btn->tap_psh_cb.cb = cb;
btn->tap_psh_cb.interval = BUTTON_GLITCH_FILTER_TIME_MS / portTICK_RATE_MS;
btn->tap_psh_cb.pbtn = btn;
#if !USE_ESP_TIMER
xTimerChangePeriod(btn->tap_psh_cb.tmr, btn->tap_psh_cb.interval, portMAX_DELAY);
#endif
} else if (type == BUTTON_CB_RELEASE) {
btn->tap_rls_cb.arg = arg;
btn->tap_rls_cb.cb = cb;
btn->tap_rls_cb.interval = BUTTON_GLITCH_FILTER_TIME_MS / portTICK_RATE_MS;
btn->tap_rls_cb.pbtn = btn;
#if !USE_ESP_TIMER
xTimerChangePeriod(btn->tap_rls_cb.tmr, btn->tap_psh_cb.interval, portMAX_DELAY);
#endif
} else if (type == BUTTON_CB_TAP) {
btn->tap_short_cb.arg = arg;
btn->tap_short_cb.cb = cb;
btn->tap_short_cb.interval = BUTTON_GLITCH_FILTER_TIME_MS / portTICK_RATE_MS;
btn->tap_short_cb.pbtn = btn;
} else if (type == BUTTON_CB_SERIAL) {
iot_button_set_serial_cb(btn_handle, 1, 1000 / portTICK_RATE_MS, cb, arg);
}
return ESP_OK;
}
esp_err_t iot_button_add_custom_cb(button_handle_t btn_handle, uint32_t press_sec, button_cb cb, void* arg)
{
POINT_ASSERT(TAG, btn_handle, ESP_ERR_INVALID_ARG);
IOT_CHECK(TAG, press_sec != 0, ESP_ERR_INVALID_ARG);
button_dev_t* btn = (button_dev_t*) btn_handle;
button_cb_t* cb_new = (button_cb_t*) calloc(1, sizeof(button_cb_t));
POINT_ASSERT(TAG, cb_new, ESP_FAIL);
cb_new->arg = arg;
cb_new->cb = cb;
cb_new->interval = press_sec * 1000 / portTICK_PERIOD_MS;
cb_new->pbtn = btn;
#if !USE_ESP_TIMER
cb_new->tmr = xTimerCreate("btn_press_tmr", cb_new->interval, pdFALSE, cb_new, button_press_cb);
#else
esp_timer_create_args_t tmr_param_cus;
tmr_param_cus.arg = cb_new;
tmr_param_cus.callback = button_press_cb;
tmr_param_cus.dispatch_method = ESP_TIMER_TASK;
tmr_param_cus.name = "btn_press_custom_tmr";
esp_timer_create(&tmr_param_cus, &cb_new->tmr);
#endif
cb_new->next_cb = btn->cb_head;
btn->cb_head = cb_new;
return ESP_OK;
}

View file

@ -0,0 +1,48 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// 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.
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "iot_button.h"
CButton::CButton(gpio_num_t gpio_num, button_active_t active_level)
{
m_btn_handle = iot_button_create(gpio_num, active_level);
}
CButton::~CButton()
{
iot_button_delete(m_btn_handle);
m_btn_handle = NULL;
}
esp_err_t CButton::set_evt_cb(button_cb_type_t type, button_cb cb, void* arg)
{
return iot_button_set_evt_cb(m_btn_handle, type, cb, arg);
}
esp_err_t CButton::set_serial_cb(button_cb cb, void* arg, TickType_t interval_tick, uint32_t start_after_sec)
{
return iot_button_set_serial_cb(m_btn_handle, start_after_sec, interval_tick, cb, arg);
}
esp_err_t CButton::add_custom_cb(uint32_t press_sec, button_cb cb, void* arg)
{
return iot_button_add_custom_cb(m_btn_handle, press_sec, cb, arg);
}
esp_err_t CButton::rm_cb(button_cb_type_t type)
{
return iot_button_rm_cb(m_btn_handle, type);
}

View file

@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -0,0 +1,231 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// 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.
#ifndef _IOT_BUTTON_H_
#define _IOT_BUTTON_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "driver/gpio.h"
#include "freertos/portmacro.h"
typedef void (* button_cb)(void*);
typedef void* button_handle_t;
typedef enum {
BUTTON_ACTIVE_HIGH = 1, /*!<button active level: high level*/
BUTTON_ACTIVE_LOW = 0, /*!<button active level: low level*/
} button_active_t;
typedef enum {
BUTTON_CB_PUSH = 0, /*!<button push callback event */
BUTTON_CB_RELEASE, /*!<button release callback event */
BUTTON_CB_TAP, /*!<button quick tap callback event(will not trigger if there already is a "PRESS" event) */
BUTTON_CB_SERIAL, /*!<button serial trigger callback event */
} button_cb_type_t;
/**
* @brief Init button functions
*
* @param gpio_num GPIO index of the pin that the button uses
* @param active_level button hardware active level.
* For "BUTTON_ACTIVE_LOW" it means when the button pressed, the GPIO will read low level.
*
* @return A button_handle_t handle to the created button object, or NULL in case of error.
*/
button_handle_t iot_button_create(gpio_num_t gpio_num, button_active_t active_level);
/**
* @brief Register a callback function for a serial trigger event.
*
* @param btn_handle handle of the button object
* @start_after_sec define the time after which to start serial trigger action
* @interval_tick serial trigger interval
* @cb callback function for "TAP" action.
* @arg Parameter for callback function
* @note
* Button callback functions execute in the context of the timer service task.
* It is therefore essential that button callback functions never attempt to block.
* For example, a button callback function must not call vTaskDelay(), vTaskDelayUntil(),
* or specify a non zero block time when accessing a queue or a semaphore.
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t iot_button_set_serial_cb(button_handle_t btn_handle, uint32_t start_after_sec, TickType_t interval_tick, button_cb cb, void* arg);
/**
* @brief Register a callback function for a button_cb_type_t action.
*
* @param btn_handle handle of the button object
* @param type callback function type
* @param cb callback function for "TAP" action.
* @param arg Parameter for callback function
* @note
* Button callback functions execute in the context of the timer service task.
* It is therefore essential that button callback functions never attempt to block.
* For example, a button callback function must not call vTaskDelay(), vTaskDelayUntil(),
* or specify a non zero block time when accessing a queue or a semaphore.
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t iot_button_set_evt_cb(button_handle_t btn_handle, button_cb_type_t type, button_cb cb, void* arg);
/**
* @brief
*
* @param btn_handle handle of the button object
* @param press_sec the callback function would be called if you press the button for a specified period of time
* @param cb callback function for "PRESS" action.
* @param arg Parameter for callback function
*
* @note
* Button callback functions execute in the context of the timer service task.
* It is therefore essential that button callback functions never attempt to block.
* For example, a button callback function must not call vTaskDelay(), vTaskDelayUntil(),
* or specify a non zero block time when accessing a queue or a semaphore.
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t iot_button_add_custom_cb(button_handle_t btn_handle, uint32_t press_sec, button_cb cb, void* arg);
/**
* @brief Delete button object and free memory
* @param btn_handle handle of the button object
*
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t iot_button_delete(button_handle_t btn_handle);
/**
* @brief Remove callback
*
* @param btn_handle The handle of the button object
* @param type callback function event type
*
* @return
* - ESP_OK Success
*/
esp_err_t iot_button_rm_cb(button_handle_t btn_handle, button_cb_type_t type);
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus
/**
* class of button
* simple usage:
* CButton* btn = new CButton(BUTTON_IO_NUM, BUTTON_ACTIVE_LEVEL, BUTTON_SERIAL_TRIGGER, 3);
* btn->add_cb(BUTTON_CB_PUSH, button_tap_cb, (void*) push, 50 / portTICK_PERIOD_MS);
* btn->add_custom_cb(5, button_press_5s_cb, NULL);
* ......
* delete btn;
*/
class CButton
{
private:
button_handle_t m_btn_handle;
/**
* prevent copy constructing
*/
CButton(const CButton&);
CButton& operator = (const CButton&);
public:
/**
* @brief constructor of CButton
*
* @param gpio_num GPIO index of the pin that the button uses
* @param active_level button hardware active level.
* For "BUTTON_ACTIVE_LOW" it means when the button pressed, the GPIO will read low level.
*/
CButton(gpio_num_t gpio_num, button_active_t active_level = BUTTON_ACTIVE_LOW);
~CButton();
/**
* @brief Register a callback function for a button_cb_type_t action.
*
* @param type callback function type
* @param cb callback function for "TAP" action.
* @param arg Parameter for callback function
* @note
* Button callback functions execute in the context of the timer service task.
* It is therefore essential that button callback functions never attempt to block.
* For example, a button callback function must not call vTaskDelay(), vTaskDelayUntil(),
* or specify a non zero block time when accessing a queue or a semaphore.
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t set_evt_cb(button_cb_type_t type, button_cb cb, void* arg);
/**
* @brief Register a callback function for a serial trigger event.
*
* @param btn_handle handle of the button object
* @start_after_sec define the time after which to start serial trigger action
* @interval_tick serial trigger interval
* @cb callback function for "TAP" action.
* @arg Parameter for callback function
* @note
* Button callback functions execute in the context of the timer service task.
* It is therefore essential that button callback functions never attempt to block.
* For example, a button callback function must not call vTaskDelay(), vTaskDelayUntil(),
* or specify a non zero block time when accessing a queue or a semaphore.
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t set_serial_cb(button_cb cb, void* arg, TickType_t interval_tick, uint32_t start_after_sec);
/**
* @brief
*
* @param press_sec the callback function would be called if you press the button for a specified period of time
* @param cb callback function for "PRESS" action.
* @param arg Parameter for callback function
*
* @note
* Button callback functions execute in the context of the timer service task.
* It is therefore essential that button callback functions never attempt to block.
* For example, a button callback function must not call vTaskDelay(), vTaskDelayUntil(),
* or specify a non zero block time when accessing a queue or a semaphore.
* @return
* - ESP_OK Success
* - ESP_FAIL Parameter error
*/
esp_err_t add_custom_cb(uint32_t press_sec, button_cb cb, void* arg);
/**
* @brief Remove callback
*
* @param type callback function event type
*
* @return
* - ESP_OK Success
*/
esp_err_t rm_cb(button_cb_type_t type);
};
#endif
#endif

View file

@ -0,0 +1,7 @@
set(COMPONENT_SRCS "ble_mesh_demo_main.c"
"ble_mesh_demo_init.c"
"board.c")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View file

@ -0,0 +1,152 @@
/*
* Copyright (c) 2017 Intel Corporation
* Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include <sdkconfig.h>
/* BLE */
#ifdef CONFIG_BLUEDROID_ENABLED
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#endif
#ifdef CONFIG_NIMBLE_ENABLED
#include "esp_nimble_hci.h"
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
#include "host/ble_hs.h"
#include "host/util/util.h"
#include "console/console.h"
#endif
#include "esp_ble_mesh_defs.h"
#include "ble_mesh_demo_init.h"
#include "esp_ble_mesh_common_api.h"
#ifdef CONFIG_BLUEDROID_ENABLED
void ble_mesh_get_dev_uuid(uint8_t *dev_uuid)
{
if (dev_uuid == NULL) {
ESP_LOGE(TAG, "%s, Invalid device uuid", __func__);
return;
}
/* Copy device address to the device uuid with offset equals to 2 here.
* The first two bytes is used for matching device uuid by Provisioner.
* And using device address here is to avoid using the same device uuid
* by different unprovisioned devices.
*/
memcpy(dev_uuid + 2, esp_bt_dev_get_address(), BD_ADDR_LEN);
}
esp_err_t bluetooth_init(void)
{
esp_err_t ret;
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
ret = esp_bt_controller_init(&bt_cfg);
if (ret) {
ESP_LOGE(TAG, "%s initialize controller failed", __func__);
return ret;
}
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
if (ret) {
ESP_LOGE(TAG, "%s enable controller failed", __func__);
return ret;
}
ret = esp_bluedroid_init();
if (ret) {
ESP_LOGE(TAG, "%s init bluetooth failed", __func__);
return ret;
}
ret = esp_bluedroid_enable();
if (ret) {
ESP_LOGE(TAG, "%s enable bluetooth failed", __func__);
return ret;
}
return ret;
}
#endif
#ifdef CONFIG_NIMBLE_ENABLED
static SemaphoreHandle_t mesh_sem;
static uint8_t own_addr_type;
void ble_store_config_init(void);
static uint8_t addr_val[BD_ADDR_LEN] = {0};
void ble_mesh_get_dev_uuid(uint8_t *dev_uuid)
{
memcpy(dev_uuid + 2, addr_val, BD_ADDR_LEN);
}
static void mesh_on_reset(int reason)
{
ESP_LOGI(TAG, "Resetting state; reason=%d", reason);
}
static void mesh_on_sync(void)
{
int rc;
rc = ble_hs_util_ensure_addr(0);
assert(rc == 0);
/* Figure out address to use while advertising (no privacy for now) */
rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) {
ESP_LOGI(TAG, "error determining address type; rc=%d", rc);
return;
}
rc = ble_hs_id_copy_addr(own_addr_type, addr_val, NULL);
xSemaphoreGive(mesh_sem);
}
void mesh_host_task(void *param)
{
ESP_LOGI(TAG, "BLE Host Task Started");
/* This function will return only when nimble_port_stop() is executed */
nimble_port_run();
nimble_port_freertos_deinit();
}
esp_err_t bluetooth_init(void)
{
mesh_sem = xSemaphoreCreateBinary();
if (mesh_sem == NULL) {
ESP_LOGE(TAG, "Failed to create mesh semaphore");
return ESP_FAIL;
}
ESP_ERROR_CHECK(esp_nimble_hci_and_controller_init());
nimble_port_init();
/* Initialize the NimBLE host configuration. */
ble_hs_cfg.reset_cb = mesh_on_reset;
ble_hs_cfg.sync_cb = mesh_on_sync;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
/* XXX Need to have template for store */
ble_store_config_init();
nimble_port_freertos_init(mesh_host_task);
xSemaphoreTake(mesh_sem, portMAX_DELAY);
return ESP_OK;
}
#endif

View file

@ -0,0 +1,18 @@
/*
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#ifndef _BLE_MESH_DEMO_INIT_H_
#define _BLE_MESH_DEMO_INIT_H_
#define TAG "Client"
void ble_mesh_get_dev_uuid(uint8_t *dev_uuid);
esp_err_t bluetooth_init(void);
#endif

View file

@ -0,0 +1,691 @@
/* main.c - Application main entry point */
/*
* Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_ble_mesh_defs.h"
#include "esp_ble_mesh_common_api.h"
#include "esp_ble_mesh_provisioning_api.h"
#include "esp_ble_mesh_networking_api.h"
#include "esp_ble_mesh_config_model_api.h"
#include "esp_ble_mesh_sensor_model_api.h"
#include "ble_mesh_demo_init.h"
#include "board.h"
#define CID_ESP 0x02E5
#define CID_NVAL 0xFFFF
#define PROV_OWN_ADDR 0x0001
#define MSG_SEND_TTL 3
#define MSG_SEND_REL false
#define MSG_TIMEOUT 0
#define MSG_ROLE ROLE_PROVISIONER
#define COMP_DATA_PAGE_0 0x00
#define APP_KEY_IDX 0x0000
#define APP_KEY_OCTET 0x12
#define COMP_DATA_1_OCTET(msg, offset) (msg[offset])
#define COMP_DATA_2_OCTET(msg, offset) (msg[offset + 1] << 8 | msg[offset])
static uint8_t dev_uuid[ESP_BLE_MESH_OCTET16_LEN];
static uint16_t server_address = ESP_BLE_MESH_ADDR_UNASSIGNED;
static uint16_t sensor_prop_id;
static struct esp_ble_mesh_key {
uint16_t net_idx;
uint16_t app_idx;
uint8_t app_key[ESP_BLE_MESH_OCTET16_LEN];
} prov_key;
static esp_ble_mesh_cfg_srv_t config_server = {
.beacon = ESP_BLE_MESH_BEACON_DISABLED,
#if defined(CONFIG_BLE_MESH_FRIEND)
.friend_state = ESP_BLE_MESH_FRIEND_ENABLED,
#else
.friend_state = ESP_BLE_MESH_FRIEND_NOT_SUPPORTED,
#endif
.default_ttl = 7,
/* 3 transmissions with 20ms interval */
.net_transmit = ESP_BLE_MESH_TRANSMIT(2, 20),
.relay_retransmit = ESP_BLE_MESH_TRANSMIT(2, 20),
};
static esp_ble_mesh_client_t config_client;
static esp_ble_mesh_client_t sensor_client;
static esp_ble_mesh_model_t root_models[] = {
ESP_BLE_MESH_MODEL_CFG_SRV(&config_server),
ESP_BLE_MESH_MODEL_CFG_CLI(&config_client),
ESP_BLE_MESH_MODEL_SENSOR_CLI(NULL, &sensor_client),
};
static esp_ble_mesh_elem_t elements[] = {
ESP_BLE_MESH_ELEMENT(0, root_models, ESP_BLE_MESH_MODEL_NONE),
};
static esp_ble_mesh_comp_t composition = {
.cid = CID_ESP,
.elements = elements,
.element_count = ARRAY_SIZE(elements),
};
static esp_ble_mesh_prov_t provision = {
.prov_uuid = dev_uuid,
.prov_unicast_addr = PROV_OWN_ADDR,
.prov_start_address = 0x0005,
};
static void example_ble_mesh_set_msg_common(esp_ble_mesh_client_common_param_t *common,
esp_ble_mesh_node_t *node,
esp_ble_mesh_model_t *model, uint32_t opcode)
{
common->opcode = opcode;
common->model = model;
common->ctx.net_idx = prov_key.net_idx;
common->ctx.app_idx = prov_key.app_idx;
common->ctx.addr = node->unicast_addr;
common->ctx.send_ttl = MSG_SEND_TTL;
common->ctx.send_rel = MSG_SEND_REL;
common->msg_timeout = MSG_TIMEOUT;
common->msg_role = MSG_ROLE;
}
static esp_err_t prov_complete(uint16_t node_index, const esp_ble_mesh_octet16_t uuid,
uint16_t primary_addr, uint8_t element_num, uint16_t net_idx)
{
esp_ble_mesh_client_common_param_t common = {0};
esp_ble_mesh_cfg_client_get_state_t get = {0};
esp_ble_mesh_node_t *node = NULL;
char name[11] = {'\0'};
esp_err_t err = ESP_OK;
ESP_LOGI(TAG, "node_index %u, primary_addr 0x%04x, element_num %u, net_idx 0x%03x",
node_index, primary_addr, element_num, net_idx);
ESP_LOG_BUFFER_HEX("uuid", uuid, ESP_BLE_MESH_OCTET16_LEN);
server_address = primary_addr;
sprintf(name, "%s%02x", "NODE-", node_index);
err = esp_ble_mesh_provisioner_set_node_name(node_index, name);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to set node name");
return ESP_FAIL;
}
node = esp_ble_mesh_provisioner_get_node_with_addr(primary_addr);
if (node == NULL) {
ESP_LOGE(TAG, "Failed to get node 0x%04x info", primary_addr);
return ESP_FAIL;
}
example_ble_mesh_set_msg_common(&common, node, config_client.model, ESP_BLE_MESH_MODEL_OP_COMPOSITION_DATA_GET);
get.comp_data_get.page = COMP_DATA_PAGE_0;
err = esp_ble_mesh_config_client_get_state(&common, &get);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send Config Composition Data Get");
return ESP_FAIL;
}
return ESP_OK;
}
static void recv_unprov_adv_pkt(uint8_t dev_uuid[ESP_BLE_MESH_OCTET16_LEN], uint8_t addr[BD_ADDR_LEN],
esp_ble_mesh_addr_type_t addr_type, uint16_t oob_info,
uint8_t adv_type, esp_ble_mesh_prov_bearer_t bearer)
{
esp_ble_mesh_unprov_dev_add_t add_dev = {0};
esp_err_t err = ESP_OK;
/* Due to the API esp_ble_mesh_provisioner_set_dev_uuid_match, Provisioner will only
* use this callback to report the devices, whose device UUID starts with 0xdd & 0xdd,
* to the application layer.
*/
ESP_LOG_BUFFER_HEX("Device address", addr, BD_ADDR_LEN);
ESP_LOGI(TAG, "Address type 0x%02x, adv type 0x%02x", addr_type, adv_type);
ESP_LOG_BUFFER_HEX("Device UUID", dev_uuid, ESP_BLE_MESH_OCTET16_LEN);
ESP_LOGI(TAG, "oob info 0x%04x, bearer %s", oob_info, (bearer & ESP_BLE_MESH_PROV_ADV) ? "PB-ADV" : "PB-GATT");
memcpy(add_dev.addr, addr, BD_ADDR_LEN);
add_dev.addr_type = (uint8_t)addr_type;
memcpy(add_dev.uuid, dev_uuid, ESP_BLE_MESH_OCTET16_LEN);
add_dev.oob_info = oob_info;
add_dev.bearer = (uint8_t)bearer;
/* Note: If unprovisioned device adv packets have not been received, we should not add
device with ADD_DEV_START_PROV_NOW_FLAG set. */
err = esp_ble_mesh_provisioner_add_unprov_dev(&add_dev,
ADD_DEV_RM_AFTER_PROV_FLAG | ADD_DEV_START_PROV_NOW_FLAG | ADD_DEV_FLUSHABLE_DEV_FLAG);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to start provisioning device");
}
}
static void example_ble_mesh_provisioning_cb(esp_ble_mesh_prov_cb_event_t event,
esp_ble_mesh_prov_cb_param_t *param)
{
switch (event) {
case ESP_BLE_MESH_PROV_REGISTER_COMP_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_PROV_REGISTER_COMP_EVT, err_code %d", param->prov_register_comp.err_code);
break;
case ESP_BLE_MESH_PROVISIONER_PROV_ENABLE_COMP_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_PROV_ENABLE_COMP_EVT, err_code %d", param->provisioner_prov_enable_comp.err_code);
break;
case ESP_BLE_MESH_PROVISIONER_PROV_DISABLE_COMP_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_PROV_DISABLE_COMP_EVT, err_code %d", param->provisioner_prov_disable_comp.err_code);
break;
case ESP_BLE_MESH_PROVISIONER_RECV_UNPROV_ADV_PKT_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_RECV_UNPROV_ADV_PKT_EVT");
recv_unprov_adv_pkt(param->provisioner_recv_unprov_adv_pkt.dev_uuid, param->provisioner_recv_unprov_adv_pkt.addr,
param->provisioner_recv_unprov_adv_pkt.addr_type, param->provisioner_recv_unprov_adv_pkt.oob_info,
param->provisioner_recv_unprov_adv_pkt.adv_type, param->provisioner_recv_unprov_adv_pkt.bearer);
break;
case ESP_BLE_MESH_PROVISIONER_PROV_LINK_OPEN_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_PROV_LINK_OPEN_EVT, bearer %s",
param->provisioner_prov_link_open.bearer == ESP_BLE_MESH_PROV_ADV ? "PB-ADV" : "PB-GATT");
break;
case ESP_BLE_MESH_PROVISIONER_PROV_LINK_CLOSE_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_PROV_LINK_CLOSE_EVT, bearer %s, reason 0x%02x",
param->provisioner_prov_link_close.bearer == ESP_BLE_MESH_PROV_ADV ? "PB-ADV" : "PB-GATT", param->provisioner_prov_link_close.reason);
break;
case ESP_BLE_MESH_PROVISIONER_PROV_COMPLETE_EVT:
prov_complete(param->provisioner_prov_complete.node_idx, param->provisioner_prov_complete.device_uuid,
param->provisioner_prov_complete.unicast_addr, param->provisioner_prov_complete.element_num,
param->provisioner_prov_complete.netkey_idx);
break;
case ESP_BLE_MESH_PROVISIONER_ADD_UNPROV_DEV_COMP_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_ADD_UNPROV_DEV_COMP_EVT, err_code %d", param->provisioner_add_unprov_dev_comp.err_code);
break;
case ESP_BLE_MESH_PROVISIONER_SET_DEV_UUID_MATCH_COMP_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_SET_DEV_UUID_MATCH_COMP_EVT, err_code %d", param->provisioner_set_dev_uuid_match_comp.err_code);
break;
case ESP_BLE_MESH_PROVISIONER_SET_NODE_NAME_COMP_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_SET_NODE_NAME_COMP_EVT, err_code %d", param->provisioner_set_node_name_comp.err_code);
if (param->provisioner_set_node_name_comp.err_code == 0) {
const char *name = esp_ble_mesh_provisioner_get_node_name(param->provisioner_set_node_name_comp.node_index);
if (name) {
ESP_LOGI(TAG, "Node %d name %s", param->provisioner_set_node_name_comp.node_index, name);
}
}
break;
case ESP_BLE_MESH_PROVISIONER_ADD_LOCAL_APP_KEY_COMP_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_ADD_LOCAL_APP_KEY_COMP_EVT, err_code %d", param->provisioner_add_app_key_comp.err_code);
if (param->provisioner_add_app_key_comp.err_code == 0) {
prov_key.app_idx = param->provisioner_add_app_key_comp.app_idx;
esp_err_t err = esp_ble_mesh_provisioner_bind_app_key_to_local_model(PROV_OWN_ADDR, prov_key.app_idx,
ESP_BLE_MESH_MODEL_ID_SENSOR_CLI, CID_NVAL);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to bind AppKey to sensor client");
}
}
break;
case ESP_BLE_MESH_PROVISIONER_BIND_APP_KEY_TO_MODEL_COMP_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_BIND_APP_KEY_TO_MODEL_COMP_EVT, err_code %d", param->provisioner_bind_app_key_to_model_comp.err_code);
break;
case ESP_BLE_MESH_PROVISIONER_STORE_NODE_COMP_DATA_COMP_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_PROVISIONER_STORE_NODE_COMP_DATA_COMP_EVT, err_code %d", param->provisioner_store_node_comp_data_comp.err_code);
break;
default:
break;
}
}
static void example_ble_mesh_parse_node_comp_data(const uint8_t *data, uint16_t length)
{
uint16_t cid, pid, vid, crpl, feat;
uint16_t loc, model_id, company_id;
uint8_t nums, numv;
uint16_t offset;
int i;
cid = COMP_DATA_2_OCTET(data, 0);
pid = COMP_DATA_2_OCTET(data, 2);
vid = COMP_DATA_2_OCTET(data, 4);
crpl = COMP_DATA_2_OCTET(data, 6);
feat = COMP_DATA_2_OCTET(data, 8);
offset = 10;
ESP_LOGI(TAG, "********************** Composition Data Start **********************");
ESP_LOGI(TAG, "* CID 0x%04x, PID 0x%04x, VID 0x%04x, CRPL 0x%04x, Features 0x%04x *", cid, pid, vid, crpl, feat);
for (; offset < length; ) {
loc = COMP_DATA_2_OCTET(data, offset);
nums = COMP_DATA_1_OCTET(data, offset + 2);
numv = COMP_DATA_1_OCTET(data, offset + 3);
offset += 4;
ESP_LOGI(TAG, "* Loc 0x%04x, NumS 0x%02x, NumV 0x%02x *", loc, nums, numv);
for (i = 0; i < nums; i++) {
model_id = COMP_DATA_2_OCTET(data, offset);
ESP_LOGI(TAG, "* SIG Model ID 0x%04x *", model_id);
offset += 2;
}
for (i = 0; i < numv; i++) {
company_id = COMP_DATA_2_OCTET(data, offset);
model_id = COMP_DATA_2_OCTET(data, offset + 2);
ESP_LOGI(TAG, "* Vendor Model ID 0x%04x, Company ID 0x%04x *", model_id, company_id);
offset += 4;
}
}
ESP_LOGI(TAG, "*********************** Composition Data End ***********************");
}
static void example_ble_mesh_config_client_cb(esp_ble_mesh_cfg_client_cb_event_t event,
esp_ble_mesh_cfg_client_cb_param_t *param)
{
esp_ble_mesh_client_common_param_t common = {0};
esp_ble_mesh_cfg_client_set_state_t set = {0};
static uint16_t wait_model_id, wait_cid;
esp_ble_mesh_node_t *node = NULL;
esp_err_t err = ESP_OK;
ESP_LOGI(TAG, "Config client, event %u, addr 0x%04x, opcode 0x%04x",
event, param->params->ctx.addr, param->params->opcode);
if (param->error_code) {
ESP_LOGE(TAG, "Send config client message failed (err %d)", param->error_code);
return;
}
node = esp_ble_mesh_provisioner_get_node_with_addr(param->params->ctx.addr);
if (!node) {
ESP_LOGE(TAG, "Node 0x%04x not exists", param->params->ctx.addr);
return;
}
switch (event) {
case ESP_BLE_MESH_CFG_CLIENT_GET_STATE_EVT:
if (param->params->opcode == ESP_BLE_MESH_MODEL_OP_COMPOSITION_DATA_GET) {
ESP_LOG_BUFFER_HEX("Composition data", param->status_cb.comp_data_status.composition_data->data,
param->status_cb.comp_data_status.composition_data->len);
example_ble_mesh_parse_node_comp_data(param->status_cb.comp_data_status.composition_data->data,
param->status_cb.comp_data_status.composition_data->len);
err = esp_ble_mesh_provisioner_store_node_comp_data(param->params->ctx.addr,
param->status_cb.comp_data_status.composition_data->data,
param->status_cb.comp_data_status.composition_data->len);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to store node composition data");
break;
}
example_ble_mesh_set_msg_common(&common, node, config_client.model, ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD);
set.app_key_add.net_idx = prov_key.net_idx;
set.app_key_add.app_idx = prov_key.app_idx;
memcpy(set.app_key_add.app_key, prov_key.app_key, ESP_BLE_MESH_OCTET16_LEN);
err = esp_ble_mesh_config_client_set_state(&common, &set);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send Config AppKey Add");
}
}
break;
case ESP_BLE_MESH_CFG_CLIENT_SET_STATE_EVT:
if (param->params->opcode == ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD) {
example_ble_mesh_set_msg_common(&common, node, config_client.model, ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND);
set.model_app_bind.element_addr = node->unicast_addr;
set.model_app_bind.model_app_idx = prov_key.app_idx;
set.model_app_bind.model_id = ESP_BLE_MESH_MODEL_ID_SENSOR_SRV;
set.model_app_bind.company_id = CID_NVAL;
err = esp_ble_mesh_config_client_set_state(&common, &set);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send Config Model App Bind");
return;
}
wait_model_id = ESP_BLE_MESH_MODEL_ID_SENSOR_SRV;
wait_cid = CID_NVAL;
} else if (param->params->opcode == ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND) {
if (param->status_cb.model_app_status.model_id == ESP_BLE_MESH_MODEL_ID_SENSOR_SRV &&
param->status_cb.model_app_status.company_id == CID_NVAL) {
example_ble_mesh_set_msg_common(&common, node, config_client.model, ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND);
set.model_app_bind.element_addr = node->unicast_addr;
set.model_app_bind.model_app_idx = prov_key.app_idx;
set.model_app_bind.model_id = ESP_BLE_MESH_MODEL_ID_SENSOR_SETUP_SRV;
set.model_app_bind.company_id = CID_NVAL;
err = esp_ble_mesh_config_client_set_state(&common, &set);
if (err) {
ESP_LOGE(TAG, "Failed to send Config Model App Bind");
return;
}
wait_model_id = ESP_BLE_MESH_MODEL_ID_SENSOR_SETUP_SRV;
wait_cid = CID_NVAL;
} else if (param->status_cb.model_app_status.model_id == ESP_BLE_MESH_MODEL_ID_SENSOR_SETUP_SRV &&
param->status_cb.model_app_status.company_id == CID_NVAL) {
ESP_LOGW(TAG, "Provision and config successfully");
}
}
break;
case ESP_BLE_MESH_CFG_CLIENT_PUBLISH_EVT:
if (param->params->opcode == ESP_BLE_MESH_MODEL_OP_COMPOSITION_DATA_STATUS) {
ESP_LOG_BUFFER_HEX("Composition data", param->status_cb.comp_data_status.composition_data->data,
param->status_cb.comp_data_status.composition_data->len);
}
break;
case ESP_BLE_MESH_CFG_CLIENT_TIMEOUT_EVT:
switch (param->params->opcode) {
case ESP_BLE_MESH_MODEL_OP_COMPOSITION_DATA_GET: {
esp_ble_mesh_cfg_client_get_state_t get = {0};
example_ble_mesh_set_msg_common(&common, node, config_client.model, ESP_BLE_MESH_MODEL_OP_COMPOSITION_DATA_GET);
get.comp_data_get.page = COMP_DATA_PAGE_0;
err = esp_ble_mesh_config_client_get_state(&common, &get);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send Config Composition Data Get");
}
break;
}
case ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD:
example_ble_mesh_set_msg_common(&common, node, config_client.model, ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD);
set.app_key_add.net_idx = prov_key.net_idx;
set.app_key_add.app_idx = prov_key.app_idx;
memcpy(set.app_key_add.app_key, prov_key.app_key, ESP_BLE_MESH_OCTET16_LEN);
err = esp_ble_mesh_config_client_set_state(&common, &set);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send Config AppKey Add");
}
break;
case ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND:
example_ble_mesh_set_msg_common(&common, node, config_client.model, ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND);
set.model_app_bind.element_addr = node->unicast_addr;
set.model_app_bind.model_app_idx = prov_key.app_idx;
set.model_app_bind.model_id = wait_model_id;
set.model_app_bind.company_id = wait_cid;
err = esp_ble_mesh_config_client_set_state(&common, &set);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send Config Model App Bind");
}
break;
default:
break;
}
break;
default:
ESP_LOGE(TAG, "Invalid config client event %u", event);
break;
}
}
void example_ble_mesh_send_sensor_message(uint32_t opcode)
{
esp_ble_mesh_sensor_client_get_state_t get = {0};
esp_ble_mesh_client_common_param_t common = {0};
esp_ble_mesh_node_t *node = NULL;
esp_err_t err = ESP_OK;
node = esp_ble_mesh_provisioner_get_node_with_addr(server_address);
if (node == NULL) {
ESP_LOGE(TAG, "Node 0x%04x not exists", server_address);
return;
}
example_ble_mesh_set_msg_common(&common, node, sensor_client.model, opcode);
switch (opcode) {
case ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_GET:
get.cadence_get.property_id = sensor_prop_id;
break;
case ESP_BLE_MESH_MODEL_OP_SENSOR_SETTINGS_GET:
get.settings_get.sensor_property_id = sensor_prop_id;
break;
case ESP_BLE_MESH_MODEL_OP_SENSOR_SERIES_GET:
get.series_get.property_id = sensor_prop_id;
break;
default:
break;
}
err = esp_ble_mesh_sensor_client_get_state(&common, &get);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send sensor message 0x%04x", opcode);
}
}
static void example_ble_mesh_sensor_timeout(uint32_t opcode)
{
switch (opcode) {
case ESP_BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_GET:
ESP_LOGW(TAG, "Sensor Descriptor Get timeout, opcode 0x%04x", opcode);
break;
case ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_GET:
ESP_LOGW(TAG, "Sensor Cadence Get timeout, opcode 0x%04x", opcode);
break;
case ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET:
ESP_LOGW(TAG, "Sensor Cadence Set timeout, opcode 0x%04x", opcode);
break;
case ESP_BLE_MESH_MODEL_OP_SENSOR_SETTINGS_GET:
ESP_LOGW(TAG, "Sensor Settings Get timeout, opcode 0x%04x", opcode);
break;
case ESP_BLE_MESH_MODEL_OP_SENSOR_SETTING_GET:
ESP_LOGW(TAG, "Sensor Setting Get timeout, opcode 0x%04x", opcode);
break;
case ESP_BLE_MESH_MODEL_OP_SENSOR_SETTING_SET:
ESP_LOGW(TAG, "Sensor Setting Set timeout, opcode 0x%04x", opcode);
break;
case ESP_BLE_MESH_MODEL_OP_SENSOR_GET:
ESP_LOGW(TAG, "Sensor Get timeout 0x%04x", opcode);
break;
case ESP_BLE_MESH_MODEL_OP_SENSOR_COLUMN_GET:
ESP_LOGW(TAG, "Sensor Column Get timeout, opcode 0x%04x", opcode);
break;
case ESP_BLE_MESH_MODEL_OP_SENSOR_SERIES_GET:
ESP_LOGW(TAG, "Sensor Series Get timeout, opcode 0x%04x", opcode);
break;
default:
ESP_LOGE(TAG, "Unknown Sensor Get/Set opcode 0x%04x", opcode);
return;
}
example_ble_mesh_send_sensor_message(opcode);
}
static void example_ble_mesh_sensor_client_cb(esp_ble_mesh_sensor_client_cb_event_t event,
esp_ble_mesh_sensor_client_cb_param_t *param)
{
esp_ble_mesh_node_t *node = NULL;
ESP_LOGI(TAG, "Sensor client, event %u, addr 0x%04x", event, param->params->ctx.addr);
if (param->error_code) {
ESP_LOGE(TAG, "Send sensor client message failed (err %d)", param->error_code);
return;
}
node = esp_ble_mesh_provisioner_get_node_with_addr(param->params->ctx.addr);
if (!node) {
ESP_LOGE(TAG, "Node 0x%04x not exists", param->params->ctx.addr);
return;
}
switch (event) {
case ESP_BLE_MESH_SENSOR_CLIENT_GET_STATE_EVT:
switch (param->params->opcode) {
case ESP_BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_GET:
ESP_LOGI(TAG, "Sensor Descriptor Status, opcode 0x%04x", param->params->ctx.recv_op);
if (param->status_cb.descriptor_status.descriptor->len != ESP_BLE_MESH_SENSOR_SETTING_PROPERTY_ID_LEN &&
param->status_cb.descriptor_status.descriptor->len % ESP_BLE_MESH_SENSOR_DESCRIPTOR_LEN) {
ESP_LOGE(TAG, "Invalid Sensor Descriptor Status length %d", param->status_cb.descriptor_status.descriptor->len);
return;
}
if (param->status_cb.descriptor_status.descriptor->len) {
ESP_LOG_BUFFER_HEX("Sensor Descriptor", param->status_cb.descriptor_status.descriptor->data,
param->status_cb.descriptor_status.descriptor->len);
/* If running with sensor server example, sensor client can get two Sensor Property IDs.
* Currently we use the first Sensor Property ID for the following demonstration.
*/
sensor_prop_id = param->status_cb.descriptor_status.descriptor->data[1] << 8 |
param->status_cb.descriptor_status.descriptor->data[0];
}
break;
case ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_GET:
ESP_LOGI(TAG, "Sensor Cadence Status, opcode 0x%04x, Sensor Property ID 0x%04x",
param->params->ctx.recv_op, param->status_cb.cadence_status.property_id);
ESP_LOG_BUFFER_HEX("Sensor Cadence", param->status_cb.cadence_status.sensor_cadence_value->data,
param->status_cb.cadence_status.sensor_cadence_value->len);
break;
case ESP_BLE_MESH_MODEL_OP_SENSOR_SETTINGS_GET:
ESP_LOGI(TAG, "Sensor Settings Status, opcode 0x%04x, Sensor Property ID 0x%04x",
param->params->ctx.recv_op, param->status_cb.settings_status.sensor_property_id);
ESP_LOG_BUFFER_HEX("Sensor Settings", param->status_cb.settings_status.sensor_setting_property_ids->data,
param->status_cb.settings_status.sensor_setting_property_ids->len);
break;
case ESP_BLE_MESH_MODEL_OP_SENSOR_SETTING_GET:
ESP_LOGI(TAG, "Sensor Setting Status, opcode 0x%04x, Sensor Property ID 0x%04x, Sensor Setting Property ID 0x%04x",
param->params->ctx.recv_op, param->status_cb.setting_status.sensor_property_id,
param->status_cb.setting_status.sensor_setting_property_id);
if (param->status_cb.setting_status.op_en) {
ESP_LOGI(TAG, "Sensor Setting Access 0x%02x", param->status_cb.setting_status.sensor_setting_access);
ESP_LOG_BUFFER_HEX("Sensor Setting Raw", param->status_cb.setting_status.sensor_setting_raw->data,
param->status_cb.setting_status.sensor_setting_raw->len);
}
break;
case ESP_BLE_MESH_MODEL_OP_SENSOR_GET:
ESP_LOGI(TAG, "Sensor Status, opcode 0x%04x", param->params->ctx.recv_op);
if (param->status_cb.sensor_status.marshalled_sensor_data->len) {
ESP_LOG_BUFFER_HEX("Sensor Data", param->status_cb.sensor_status.marshalled_sensor_data->data,
param->status_cb.sensor_status.marshalled_sensor_data->len);
uint8_t *data = param->status_cb.sensor_status.marshalled_sensor_data->data;
uint16_t length = 0;
for (; length < param->status_cb.sensor_status.marshalled_sensor_data->len; ) {
uint8_t fmt = ESP_BLE_MESH_GET_SENSOR_DATA_FORMAT(data);
uint8_t data_len = ESP_BLE_MESH_GET_SENSOR_DATA_LENGTH(data, fmt);
uint16_t prop_id = ESP_BLE_MESH_GET_SENSOR_DATA_PROPERTY_ID(data, fmt);
uint8_t mpid_len = (fmt == ESP_BLE_MESH_SENSOR_DATA_FORMAT_A ?
ESP_BLE_MESH_SENSOR_DATA_FORMAT_A_MPID_LEN : ESP_BLE_MESH_SENSOR_DATA_FORMAT_B_MPID_LEN);
ESP_LOGI(TAG, "Format %s, length 0x%02x, Sensor Property ID 0x%04x",
fmt == ESP_BLE_MESH_SENSOR_DATA_FORMAT_A ? "A" : "B", data_len, prop_id);
if (data_len != ESP_BLE_MESH_SENSOR_DATA_ZERO_LEN) {
ESP_LOG_BUFFER_HEX("Sensor Data", data + mpid_len, data_len + 1);
length += mpid_len + data_len + 1;
} else {
length += mpid_len;
}
data += length;
}
}
break;
case ESP_BLE_MESH_MODEL_OP_SENSOR_COLUMN_GET:
ESP_LOGI(TAG, "Sensor Column Status, opcode 0x%04x, Sensor Property ID 0x%04x",
param->params->ctx.recv_op, param->status_cb.column_status.property_id);
ESP_LOG_BUFFER_HEX("Sensor Column", param->status_cb.column_status.sensor_column_value->data,
param->status_cb.column_status.sensor_column_value->len);
break;
case ESP_BLE_MESH_MODEL_OP_SENSOR_SERIES_GET:
ESP_LOGI(TAG, "Sensor Series Status, opcode 0x%04x, Sensor Property ID 0x%04x",
param->params->ctx.recv_op, param->status_cb.series_status.property_id);
ESP_LOG_BUFFER_HEX("Sensor Series", param->status_cb.series_status.sensor_series_value->data,
param->status_cb.series_status.sensor_series_value->len);
break;
default:
ESP_LOGE(TAG, "Unknown Sensor Get opcode 0x%04x", param->params->ctx.recv_op);
break;
}
break;
case ESP_BLE_MESH_SENSOR_CLIENT_SET_STATE_EVT:
switch (param->params->opcode) {
case ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET:
ESP_LOGI(TAG, "Sensor Cadence Status, opcode 0x%04x, Sensor Property ID 0x%04x",
param->params->ctx.recv_op, param->status_cb.cadence_status.property_id);
ESP_LOG_BUFFER_HEX("Sensor Cadence", param->status_cb.cadence_status.sensor_cadence_value->data,
param->status_cb.cadence_status.sensor_cadence_value->len);
break;
case ESP_BLE_MESH_MODEL_OP_SENSOR_SETTING_SET:
ESP_LOGI(TAG, "Sensor Setting Status, opcode 0x%04x, Sensor Property ID 0x%04x, Sensor Setting Property ID 0x%04x",
param->params->ctx.recv_op, param->status_cb.setting_status.sensor_property_id,
param->status_cb.setting_status.sensor_setting_property_id);
if (param->status_cb.setting_status.op_en) {
ESP_LOGI(TAG, "Sensor Setting Access 0x%02x", param->status_cb.setting_status.sensor_setting_access);
ESP_LOG_BUFFER_HEX("Sensor Setting Raw", param->status_cb.setting_status.sensor_setting_raw->data,
param->status_cb.setting_status.sensor_setting_raw->len);
}
break;
default:
ESP_LOGE(TAG, "Unknown Sensor Set opcode 0x%04x", param->params->ctx.recv_op);
break;
}
break;
case ESP_BLE_MESH_SENSOR_CLIENT_PUBLISH_EVT:
break;
case ESP_BLE_MESH_SENSOR_CLIENT_TIMEOUT_EVT:
example_ble_mesh_sensor_timeout(param->params->opcode);
default:
break;
}
}
static esp_err_t ble_mesh_init(void)
{
uint8_t match[2] = { 0x32, 0x10 };
esp_err_t err = ESP_OK;
prov_key.net_idx = ESP_BLE_MESH_KEY_PRIMARY;
prov_key.app_idx = APP_KEY_IDX;
memset(prov_key.app_key, APP_KEY_OCTET, sizeof(prov_key.app_key));
esp_ble_mesh_register_prov_callback(example_ble_mesh_provisioning_cb);
esp_ble_mesh_register_config_client_callback(example_ble_mesh_config_client_cb);
esp_ble_mesh_register_sensor_client_callback(example_ble_mesh_sensor_client_cb);
err = esp_ble_mesh_init(&provision, &composition);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize mesh stack");
return err;
}
err = esp_ble_mesh_provisioner_set_dev_uuid_match(match, sizeof(match), 0x0, false);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to set matching device uuid");
return err;
}
err = esp_ble_mesh_provisioner_prov_enable(ESP_BLE_MESH_PROV_ADV | ESP_BLE_MESH_PROV_GATT);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to enable mesh provisioner");
return err;
}
err = esp_ble_mesh_provisioner_add_local_app_key(prov_key.app_key, prov_key.net_idx, prov_key.app_idx);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to add local AppKey");
return err;
}
ESP_LOGI(TAG, "BLE Mesh sensor client initialized");
return ESP_OK;
}
void app_main(void)
{
esp_err_t err = ESP_OK;
ESP_LOGI(TAG, "Initializing...");
err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES) {
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK(err);
board_init();
err = bluetooth_init();
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp32_bluetooth_init failed (err %d)", err);
return;
}
ble_mesh_get_dev_uuid(dev_uuid);
/* Initialize the Bluetooth Mesh Subsystem */
err = ble_mesh_init();
if (err != ESP_OK) {
ESP_LOGE(TAG, "Bluetooth mesh init failed (err %d)", err);
}
}

View file

@ -0,0 +1,49 @@
/* board.c - Board-specific hooks */
/*
* Copyright (c) 2017 Intel Corporation
* Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include "esp_log.h"
#include "driver/uart.h"
#include "iot_button.h"
#include "esp_ble_mesh_sensor_model_api.h"
#define TAG "BOARD"
#define BUTTON_IO_NUM 0
#define BUTTON_ACTIVE_LEVEL 0
extern void example_ble_mesh_send_sensor_message(uint32_t opcode);
static uint32_t send_opcode[] = {
[0] = ESP_BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_GET,
[1] = ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_GET,
[2] = ESP_BLE_MESH_MODEL_OP_SENSOR_SETTINGS_GET,
[3] = ESP_BLE_MESH_MODEL_OP_SENSOR_GET,
[4] = ESP_BLE_MESH_MODEL_OP_SENSOR_SERIES_GET,
};
static uint8_t press_count;
static void button_tap_cb(void* arg)
{
example_ble_mesh_send_sensor_message(send_opcode[press_count++]);
press_count = press_count % ARRAY_SIZE(send_opcode);
}
static void board_button_init(void)
{
button_handle_t btn_handle = iot_button_create(BUTTON_IO_NUM, BUTTON_ACTIVE_LEVEL);
if (btn_handle) {
iot_button_set_evt_cb(btn_handle, BUTTON_CB_RELEASE, button_tap_cb, "RELEASE");
}
}
void board_init(void)
{
board_button_init();
}

View file

@ -0,0 +1,14 @@
/* board.h - Board-specific hooks */
/*
* Copyright (c) 2017 Intel Corporation
* Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _BOARD_H_
#define _BOARD_H_
void board_init(void);
#endif /* _BOARD_H_ */

View file

@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -0,0 +1,14 @@
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y
CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CONTROLLER_MODE_BTDM=n
CONFIG_BTDM_CONTROLLER_MODEM_SLEEP=n
CONFIG_SCAN_DUPLICATE_BY_ADV_DATA_AND_DEVICE_ADDR=y
CONFIG_BLE_MESH_SCAN_DUPLICATE_EN=y
CONFIG_BTU_TASK_STACK_SIZE=4512
CONFIG_BLE_MESH=y
CONFIG_BLE_MESH_PROVISIONER=y
CONFIG_BLE_MESH_PB_GATT=y
CONFIG_BLE_MESH_CFG_CLI=y
CONFIG_BLE_MESH_SENSOR_CLI=y

View file

@ -0,0 +1,14 @@
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y
CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CONTROLLER_MODE_BTDM=n
CONFIG_BTDM_CONTROLLER_MODEM_SLEEP=n
CONFIG_SCAN_DUPLICATE_BY_ADV_DATA_AND_DEVICE_ADDR=y
CONFIG_BLE_MESH_SCAN_DUPLICATE_EN=y
CONFIG_NIMBLE_ENABLED=y
CONFIG_BLE_MESH=y
CONFIG_BLE_MESH_PROVISIONER=y
CONFIG_BLE_MESH_PB_GATT=y
CONFIG_BLE_MESH_CFG_CLI=y
CONFIG_BLE_MESH_SENSOR_CLI=y

View file

@ -0,0 +1,19 @@
# Override some defaults so BT stack is enabled
# by default in this example
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y
CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CONTROLLER_MODE_BTDM=n
CONFIG_BTDM_CONTROLLER_MODEM_SLEEP=n
CONFIG_SCAN_DUPLICATE_BY_ADV_DATA_AND_DEVICE_ADDR=y
CONFIG_BLE_MESH_SCAN_DUPLICATE_EN=y
CONFIG_BTU_TASK_STACK_SIZE=4512
# Override some defaults of ESP BLE Mesh
CONFIG_BLE_MESH=y
CONFIG_BLE_MESH_PROVISIONER=y
CONFIG_BLE_MESH_PB_GATT=y
CONFIG_BLE_MESH_TX_SEG_MSG_COUNT=10
CONFIG_BLE_MESH_RX_SEG_MSG_COUNT=10
CONFIG_BLE_MESH_CFG_CLI=y
CONFIG_BLE_MESH_SENSOR_CLI=y

View file

@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(sensor_server)

View file

@ -0,0 +1,10 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := sensor_server
COMPONENT_ADD_INCLUDEDIRS := components/include
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,7 @@
| Supported Targets | ESP32 |
| ----------------- | ----- |
ESP BLE Mesh Sensor Server Example
==================================
For description of this example please refer to [ESP BLE Mesh Sensor Client Example](../sensor_client/README.md)

View file

@ -0,0 +1,7 @@
set(COMPONENT_SRCS "ble_mesh_demo_main.c"
"ble_mesh_demo_init.c"
"board.c")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()

View file

@ -0,0 +1,22 @@
menu "Example Configuration"
choice BLE_MESH_EXAMPLE_BOARD
prompt "Board selection for BLE Mesh"
default BLE_MESH_ESP_WROOM_32
help
Select this option to choose the board for BLE Mesh. The default is ESP32-WROOM-32
config BLE_MESH_ESP_WROOM_32
bool "ESP32-WROOM-32"
config BLE_MESH_ESP_WROVER
bool "ESP32-WROVER"
endchoice
config BLE_MESH_PATCH_FOR_SLAB_APP_1_1_0
bool "Fix bug of Silicon Lab Android App v1.1.0 when reconnection will cause sequence number to recount from 0"
default y
help
It is an ad hoc solution and needs further modifications
endmenu

View file

@ -0,0 +1,153 @@
/*
* Copyright (c) 2017 Intel Corporation
* Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include <sdkconfig.h>
/* BLE */
#ifdef CONFIG_BLUEDROID_ENABLED
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#endif
#ifdef CONFIG_NIMBLE_ENABLED
#include "esp_nimble_hci.h"
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
#include "host/ble_hs.h"
#include "host/util/util.h"
#include "console/console.h"
#endif
#include "esp_ble_mesh_defs.h"
#include "ble_mesh_demo_init.h"
#include "esp_ble_mesh_common_api.h"
#ifdef CONFIG_BLUEDROID_ENABLED
void ble_mesh_get_dev_uuid(uint8_t *dev_uuid)
{
if (dev_uuid == NULL) {
ESP_LOGE(TAG, "%s, Invalid device uuid", __func__);
return;
}
/* Copy device address to the device uuid with offset equals to 2 here.
* The first two bytes is used for matching device uuid by Provisioner.
* And using device address here is to avoid using the same device uuid
* by different unprovisioned devices.
*/
memcpy(dev_uuid + 2, esp_bt_dev_get_address(), BD_ADDR_LEN);
}
esp_err_t bluetooth_init(void)
{
esp_err_t ret;
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
ret = esp_bt_controller_init(&bt_cfg);
if (ret) {
ESP_LOGE(TAG, "%s initialize controller failed", __func__);
return ret;
}
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
if (ret) {
ESP_LOGE(TAG, "%s enable controller failed", __func__);
return ret;
}
ret = esp_bluedroid_init();
if (ret) {
ESP_LOGE(TAG, "%s init bluetooth failed", __func__);
return ret;
}
ret = esp_bluedroid_enable();
if (ret) {
ESP_LOGE(TAG, "%s enable bluetooth failed", __func__);
return ret;
}
return ret;
}
#endif
#ifdef CONFIG_NIMBLE_ENABLED
static SemaphoreHandle_t mesh_sem;
static uint8_t own_addr_type;
void ble_store_config_init(void);
static uint8_t addr_val[BD_ADDR_LEN] = {0};
void ble_mesh_get_dev_uuid(uint8_t *dev_uuid)
{
memcpy(dev_uuid + 2, addr_val, BD_ADDR_LEN);
}
static void mesh_on_reset(int reason)
{
ESP_LOGI(TAG, "Resetting state; reason=%d", reason);
}
static void mesh_on_sync(void)
{
int rc;
rc = ble_hs_util_ensure_addr(0);
assert(rc == 0);
/* Figure out address to use while advertising (no privacy for now) */
rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) {
ESP_LOGI(TAG, "error determining address type; rc=%d", rc);
return;
}
rc = ble_hs_id_copy_addr(own_addr_type, addr_val, NULL);
xSemaphoreGive(mesh_sem);
}
void mesh_host_task(void *param)
{
ESP_LOGI(TAG, "BLE Host Task Started");
/* This function will return only when nimble_port_stop() is executed */
nimble_port_run();
nimble_port_freertos_deinit();
}
esp_err_t bluetooth_init(void)
{
mesh_sem = xSemaphoreCreateBinary();
if (mesh_sem == NULL) {
ESP_LOGE(TAG, "Failed to create mesh semaphore");
return ESP_FAIL;
}
ESP_ERROR_CHECK(esp_nimble_hci_and_controller_init());
nimble_port_init();
/* Initialize the NimBLE host configuration. */
ble_hs_cfg.reset_cb = mesh_on_reset;
ble_hs_cfg.sync_cb = mesh_on_sync;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
/* XXX Need to have template for store */
ble_store_config_init();
nimble_port_freertos_init(mesh_host_task);
xSemaphoreTake(mesh_sem, portMAX_DELAY);
return ESP_OK;
}
#endif

View file

@ -0,0 +1,18 @@
/*
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#ifndef _BLE_MESH_DEMO_INIT_H_
#define _BLE_MESH_DEMO_INIT_H_
#define TAG "Server"
void ble_mesh_get_dev_uuid(uint8_t *dev_uuid);
esp_err_t bluetooth_init(void);
#endif

View file

@ -0,0 +1,641 @@
/* main.c - Application main entry point */
/*
* Copyright (c) 2017 Intel Corporation
* Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_ble_mesh_defs.h"
#include "esp_ble_mesh_common_api.h"
#include "esp_ble_mesh_networking_api.h"
#include "esp_ble_mesh_provisioning_api.h"
#include "esp_ble_mesh_config_model_api.h"
#include "esp_ble_mesh_sensor_model_api.h"
#include "ble_mesh_demo_init.h"
#include "board.h"
#define CID_ESP 0x02E5
#define CID_NVAL 0xFFFF
/* Sensor Property ID */
#define SENSOR_PROPERTY_ID_0 0x0056 /* Present Indoor Ambient Temperature */
#define SENSOR_PROPERTY_ID_1 0x005B /* Present Outdoor Ambient Temperature */
/* The characteristic of the two device properties is "Temperature 8", which is
* used to represent a measure of temperature with a unit of 0.5 degree Celsius.
* Minimum value: -64.0, maximum value: 63.5.
* A value of 0xFF represents 'value is not known'.
*/
static int8_t indoor_temp = 40; /* Indoor temperature is 20 Degrees Celsius */
static int8_t outdoor_temp = 60; /* Outdoor temperature is 30 Degrees Celsius */
#define SENSOR_POSITIVE_TOLERANCE ESP_BLE_MESH_SENSOR_UNSPECIFIED_POS_TOLERANCE
#define SENSOR_NEGATIVE_TOLERANCE ESP_BLE_MESH_SENSOR_UNSPECIFIED_NEG_TOLERANCE
#define SENSOR_SAMPLE_FUNCTION ESP_BLE_MESH_SAMPLE_FUNC_UNSPECIFIED
#define SENSOR_MEASURE_PERIOD ESP_BLE_MESH_SENSOR_NOT_APPL_MEASURE_PERIOD
#define SENSOR_UPDATE_INTERVAL ESP_BLE_MESH_SENSOR_NOT_APPL_UPDATE_INTERVAL
static uint8_t dev_uuid[ESP_BLE_MESH_OCTET16_LEN] = { 0x32, 0x10 };
static esp_ble_mesh_cfg_srv_t config_server = {
.relay = ESP_BLE_MESH_RELAY_ENABLED,
.beacon = ESP_BLE_MESH_BEACON_ENABLED,
#if defined(CONFIG_BLE_MESH_FRIEND)
.friend_state = ESP_BLE_MESH_FRIEND_ENABLED,
#else
.friend_state = ESP_BLE_MESH_FRIEND_NOT_SUPPORTED,
#endif
#if defined(CONFIG_BLE_MESH_GATT_PROXY_SERVER)
.gatt_proxy = ESP_BLE_MESH_GATT_PROXY_ENABLED,
#else
.gatt_proxy = ESP_BLE_MESH_GATT_PROXY_NOT_SUPPORTED,
#endif
.default_ttl = 7,
/* 3 transmissions with 20ms interval */
.net_transmit = ESP_BLE_MESH_TRANSMIT(2, 20),
.relay_retransmit = ESP_BLE_MESH_TRANSMIT(2, 20),
};
NET_BUF_SIMPLE_DEFINE_STATIC(sensor_data_0, 1);
NET_BUF_SIMPLE_DEFINE_STATIC(sensor_data_1, 1);
static esp_ble_mesh_sensor_state_t sensor_states[2] = {
/* Mesh Model Spec:
* Multiple instances of the Sensor states may be present within the same model,
* provided that each instance has a unique value of the Sensor Property ID to
* allow the instances to be differentiated. Such sensors are known as multisensors.
* In this example, two instances of the Sensor states within the same model are
* provided.
*/
[0] = {
/* Mesh Model Spec:
* Sensor Property ID is a 2-octet value referencing a device property
* that describes the meaning and format of data reported by a sensor.
* 0x0000 is prohibited.
*/
.sensor_property_id = SENSOR_PROPERTY_ID_0,
/* Mesh Model Spec:
* Sensor Descriptor state represents the attributes describing the sensor
* data. This state does not change throughout the lifetime of an element.
*/
.descriptor.positive_tolerance = SENSOR_POSITIVE_TOLERANCE,
.descriptor.negative_tolerance = SENSOR_NEGATIVE_TOLERANCE,
.descriptor.sampling_function = SENSOR_SAMPLE_FUNCTION,
.descriptor.measure_period = SENSOR_MEASURE_PERIOD,
.descriptor.update_interval = SENSOR_UPDATE_INTERVAL,
.sensor_data.format = ESP_BLE_MESH_SENSOR_DATA_FORMAT_A,
.sensor_data.length = 0, /* 0 represents the length is 1 */
.sensor_data.raw_value = &sensor_data_0,
},
[1] = {
.sensor_property_id = SENSOR_PROPERTY_ID_1,
.descriptor.positive_tolerance = SENSOR_POSITIVE_TOLERANCE,
.descriptor.negative_tolerance = SENSOR_NEGATIVE_TOLERANCE,
.descriptor.sampling_function = SENSOR_SAMPLE_FUNCTION,
.descriptor.measure_period = SENSOR_MEASURE_PERIOD,
.descriptor.update_interval = SENSOR_UPDATE_INTERVAL,
.sensor_data.format = ESP_BLE_MESH_SENSOR_DATA_FORMAT_A,
.sensor_data.length = 0, /* 0 represents the length is 1 */
.sensor_data.raw_value = &sensor_data_1,
},
};
/* 20 octets is large enough to hold two Sensor Descriptor state values. */
ESP_BLE_MESH_MODEL_PUB_DEFINE(sensor_pub, 20, ROLE_NODE);
static esp_ble_mesh_sensor_srv_t sensor_server = {
.rsp_ctrl.get_auto_rsp = ESP_BLE_MESH_SERVER_RSP_BY_APP,
.rsp_ctrl.set_auto_rsp = ESP_BLE_MESH_SERVER_RSP_BY_APP,
.state_count = ARRAY_SIZE(sensor_states),
.states = sensor_states,
};
ESP_BLE_MESH_MODEL_PUB_DEFINE(sensor_setup_pub, 20, ROLE_NODE);
static esp_ble_mesh_sensor_setup_srv_t sensor_setup_server = {
.rsp_ctrl.get_auto_rsp = ESP_BLE_MESH_SERVER_RSP_BY_APP,
.rsp_ctrl.set_auto_rsp = ESP_BLE_MESH_SERVER_RSP_BY_APP,
.state_count = ARRAY_SIZE(sensor_states),
.states = sensor_states,
};
static esp_ble_mesh_model_t root_models[] = {
ESP_BLE_MESH_MODEL_CFG_SRV(&config_server),
ESP_BLE_MESH_MODEL_SENSOR_SRV(&sensor_pub, &sensor_server),
ESP_BLE_MESH_MODEL_SENSOR_SETUP_SRV(&sensor_setup_pub, &sensor_setup_server),
};
static esp_ble_mesh_elem_t elements[] = {
ESP_BLE_MESH_ELEMENT(0, root_models, ESP_BLE_MESH_MODEL_NONE),
};
static esp_ble_mesh_comp_t composition = {
.cid = CID_ESP,
.elements = elements,
.element_count = ARRAY_SIZE(elements),
};
static esp_ble_mesh_prov_t provision = {
.uuid = dev_uuid,
};
static void prov_complete(uint16_t net_idx, uint16_t addr, uint8_t flags, uint32_t iv_index)
{
ESP_LOGI(TAG, "net_idx 0x%03x, addr 0x%04x", net_idx, addr);
ESP_LOGI(TAG, "flags 0x%02x, iv_index 0x%08x", flags, iv_index);
board_led_operation(LED_G, LED_OFF);
/* Initialize the indoor and outdoor temperatures for each sensor. */
net_buf_simple_add_u8(&sensor_data_0, indoor_temp);
net_buf_simple_add_u8(&sensor_data_1, outdoor_temp);
}
static void example_ble_mesh_provisioning_cb(esp_ble_mesh_prov_cb_event_t event,
esp_ble_mesh_prov_cb_param_t *param)
{
switch (event) {
case ESP_BLE_MESH_PROV_REGISTER_COMP_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_PROV_REGISTER_COMP_EVT, err_code %d", param->prov_register_comp.err_code);
break;
case ESP_BLE_MESH_NODE_PROV_ENABLE_COMP_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_PROV_ENABLE_COMP_EVT, err_code %d", param->node_prov_enable_comp.err_code);
break;
case ESP_BLE_MESH_NODE_PROV_LINK_OPEN_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_PROV_LINK_OPEN_EVT, bearer %s",
param->node_prov_link_open.bearer == ESP_BLE_MESH_PROV_ADV ? "PB-ADV" : "PB-GATT");
break;
case ESP_BLE_MESH_NODE_PROV_LINK_CLOSE_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_PROV_LINK_CLOSE_EVT, bearer %s",
param->node_prov_link_close.bearer == ESP_BLE_MESH_PROV_ADV ? "PB-ADV" : "PB-GATT");
break;
case ESP_BLE_MESH_NODE_PROV_COMPLETE_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_PROV_COMPLETE_EVT");
prov_complete(param->node_prov_complete.net_idx, param->node_prov_complete.addr,
param->node_prov_complete.flags, param->node_prov_complete.iv_index);
break;
case ESP_BLE_MESH_NODE_PROV_RESET_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_PROV_RESET_EVT");
break;
case ESP_BLE_MESH_NODE_SET_UNPROV_DEV_NAME_COMP_EVT:
ESP_LOGI(TAG, "ESP_BLE_MESH_NODE_SET_UNPROV_DEV_NAME_COMP_EVT, err_code %d", param->node_set_unprov_dev_name_comp.err_code);
break;
default:
break;
}
}
static void example_ble_mesh_config_server_cb(esp_ble_mesh_cfg_server_cb_event_t event,
esp_ble_mesh_cfg_server_cb_param_t *param)
{
if (event == ESP_BLE_MESH_CFG_SERVER_STATE_CHANGE_EVT) {
switch (param->ctx.recv_op) {
case ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD:
ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_APP_KEY_ADD");
ESP_LOGI(TAG, "net_idx 0x%04x, app_idx 0x%04x",
param->value.state_change.appkey_add.net_idx,
param->value.state_change.appkey_add.app_idx);
ESP_LOG_BUFFER_HEX("AppKey", param->value.state_change.appkey_add.app_key, 16);
break;
case ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND:
ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_MODEL_APP_BIND");
ESP_LOGI(TAG, "elem_addr 0x%04x, app_idx 0x%04x, cid 0x%04x, mod_id 0x%04x",
param->value.state_change.mod_app_bind.element_addr,
param->value.state_change.mod_app_bind.app_idx,
param->value.state_change.mod_app_bind.company_id,
param->value.state_change.mod_app_bind.model_id);
break;
case ESP_BLE_MESH_MODEL_OP_MODEL_SUB_ADD:
ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_MODEL_SUB_ADD");
ESP_LOGI(TAG, "elem_addr 0x%04x, sub_addr 0x%04x, cid 0x%04x, mod_id 0x%04x",
param->value.state_change.mod_sub_add.element_addr,
param->value.state_change.mod_sub_add.sub_addr,
param->value.state_change.mod_sub_add.company_id,
param->value.state_change.mod_sub_add.model_id);
break;
default:
break;
}
}
}
struct example_sensor_descriptor {
uint16_t sensor_prop_id;
uint32_t pos_tolerance:12,
neg_tolerance:12,
sample_func:8;
uint8_t measure_period;
uint8_t update_interval;
} __attribute__((packed));
static void example_ble_mesh_send_sensor_descriptor_status(esp_ble_mesh_sensor_server_cb_param_t *param)
{
struct example_sensor_descriptor descriptor = {0};
uint8_t *status = NULL;
uint16_t length = 0;
esp_err_t err;
int i;
status = calloc(1, ARRAY_SIZE(sensor_states) * ESP_BLE_MESH_SENSOR_DESCRIPTOR_LEN);
if (!status) {
ESP_LOGE(TAG, "No memory for sensor descriptor status!");
return;
}
if (param->value.get.sensor_descriptor.op_en == false) {
/* Mesh Model Spec:
* Upon receiving a Sensor Descriptor Get message with the Property ID field
* omitted, the Sensor Server shall respond with a Sensor Descriptor Status
* message containing the Sensor Descriptor states for all sensors within the
* Sensor Server.
*/
for (i = 0; i < ARRAY_SIZE(sensor_states); i++) {
descriptor.sensor_prop_id = sensor_states[i].sensor_property_id;
descriptor.pos_tolerance = sensor_states[i].descriptor.positive_tolerance;
descriptor.neg_tolerance = sensor_states[i].descriptor.negative_tolerance;
descriptor.sample_func = sensor_states[i].descriptor.sampling_function;
descriptor.measure_period = sensor_states[i].descriptor.measure_period;
descriptor.update_interval = sensor_states[i].descriptor.update_interval;
memcpy(status + length, &descriptor, ESP_BLE_MESH_SENSOR_DESCRIPTOR_LEN);
length += ESP_BLE_MESH_SENSOR_DESCRIPTOR_LEN;
}
goto send;
}
for (i = 0; i < ARRAY_SIZE(sensor_states); i++) {
if (param->value.get.sensor_descriptor.property_id == sensor_states[i].sensor_property_id) {
descriptor.sensor_prop_id = sensor_states[i].sensor_property_id;
descriptor.pos_tolerance = sensor_states[i].descriptor.positive_tolerance;
descriptor.neg_tolerance = sensor_states[i].descriptor.negative_tolerance;
descriptor.sample_func = sensor_states[i].descriptor.sampling_function;
descriptor.measure_period = sensor_states[i].descriptor.measure_period;
descriptor.update_interval = sensor_states[i].descriptor.update_interval;
memcpy(status, &descriptor, ESP_BLE_MESH_SENSOR_DESCRIPTOR_LEN);
length = ESP_BLE_MESH_SENSOR_DESCRIPTOR_LEN;
goto send;
}
}
/* Mesh Model Spec:
* When a Sensor Descriptor Get message that identifies a sensor descriptor
* property that does not exist on the element, the Descriptor field shall
* contain the requested Property ID value and the other fields of the Sensor
* Descriptor state shall be omitted.
*/
memcpy(status, &param->value.get.sensor_descriptor.property_id, ESP_BLE_MESH_SENSOR_PROPERTY_ID_LEN);
length = ESP_BLE_MESH_SENSOR_PROPERTY_ID_LEN;
send:
ESP_LOG_BUFFER_HEX("Sensor Descriptor", status, length);
err = esp_ble_mesh_server_model_send_msg(param->model, &param->ctx,
ESP_BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_STATUS, length, status);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send Sensor Descriptor Status");
}
free(status);
}
static void example_ble_mesh_send_sensor_cadence_status(esp_ble_mesh_sensor_server_cb_param_t *param)
{
esp_err_t err;
/* Sensor Cadence state is not supported currently. */
err = esp_ble_mesh_server_model_send_msg(param->model, &param->ctx,
ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_STATUS,
ESP_BLE_MESH_SENSOR_PROPERTY_ID_LEN,
(uint8_t *)&param->value.get.sensor_cadence.property_id);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send Sensor Cadence Status");
}
}
static void example_ble_mesh_send_sensor_settings_status(esp_ble_mesh_sensor_server_cb_param_t *param)
{
esp_err_t err;
/* Sensor Setting state is not supported currently. */
err = esp_ble_mesh_server_model_send_msg(param->model, &param->ctx,
ESP_BLE_MESH_MODEL_OP_SENSOR_SETTINGS_STATUS,
ESP_BLE_MESH_SENSOR_PROPERTY_ID_LEN,
(uint8_t *)&param->value.get.sensor_settings.property_id);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send Sensor Settings Status");
}
}
struct example_sensor_setting {
uint16_t sensor_prop_id;
uint16_t sensor_setting_prop_id;
} __attribute__((packed));
static void example_ble_mesh_send_sensor_setting_status(esp_ble_mesh_sensor_server_cb_param_t *param)
{
struct example_sensor_setting setting = {0};
esp_err_t err;
/* Mesh Model Spec:
* If the message is sent as a response to the Sensor Setting Get message or
* a Sensor Setting Set message with an unknown Sensor Property ID field or
* an unknown Sensor Setting Property ID field, the Sensor Setting Access
* field and the Sensor Setting Raw field shall be omitted.
*/
setting.sensor_prop_id = param->value.get.sensor_setting.property_id;
setting.sensor_setting_prop_id = param->value.get.sensor_setting.setting_property_id;
err = esp_ble_mesh_server_model_send_msg(param->model, &param->ctx,
ESP_BLE_MESH_MODEL_OP_SENSOR_SETTING_STATUS,
sizeof(setting), (uint8_t *)&setting);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send Sensor Setting Status");
}
}
static uint16_t example_ble_mesh_get_sensor_data(esp_ble_mesh_sensor_state_t *state, uint8_t *data)
{
uint8_t mpid_len = 0, data_len = 0;
uint32_t mpid = 0;
if (state == NULL || data == NULL) {
ESP_LOGE(TAG, "%s, Invalid parameter", __func__);
return 0;
}
if (state->sensor_data.length == ESP_BLE_MESH_SENSOR_DATA_ZERO_LEN) {
/* For zero-length sensor data, the length is 0x7F, and the format is Format B. */
mpid = ESP_BLE_MESH_SENSOR_DATA_FORMAT_B_MPID(state->sensor_data.length, state->sensor_property_id);
mpid_len = ESP_BLE_MESH_SENSOR_DATA_FORMAT_B_MPID_LEN;
data_len = 0;
} else {
if (state->sensor_data.format == ESP_BLE_MESH_SENSOR_DATA_FORMAT_A) {
mpid = ESP_BLE_MESH_SENSOR_DATA_FORMAT_A_MPID(state->sensor_data.length, state->sensor_property_id);
mpid_len = ESP_BLE_MESH_SENSOR_DATA_FORMAT_A_MPID_LEN;
} else {
mpid = ESP_BLE_MESH_SENSOR_DATA_FORMAT_B_MPID(state->sensor_data.length, state->sensor_property_id);
mpid_len = ESP_BLE_MESH_SENSOR_DATA_FORMAT_B_MPID_LEN;
}
/* Use "state->sensor_data.length + 1" because the length of sensor data is zero-based. */
data_len = state->sensor_data.length + 1;
}
memcpy(data, &mpid, mpid_len);
memcpy(data + mpid_len, state->sensor_data.raw_value->data, data_len);
return (mpid_len + data_len);
}
static void example_ble_mesh_send_sensor_status(esp_ble_mesh_sensor_server_cb_param_t *param)
{
uint8_t *status = NULL;
uint16_t buf_size = 0;
uint16_t length = 0;
uint32_t mpid = 0;
esp_err_t err;
int i;
/**
* Sensor Data state from Mesh Model Spec
* |--------Field--------|-Size (octets)-|------------------------Notes-------------------------|
* |----Property ID 1----|-------2-------|--ID of the 1st device property of the sensor---------|
* |-----Raw Value 1-----|----variable---|--Raw Value field defined by the 1st device property--|
* |----Property ID 2----|-------2-------|--ID of the 2nd device property of the sensor---------|
* |-----Raw Value 2-----|----variable---|--Raw Value field defined by the 2nd device property--|
* | ...... |
* |----Property ID n----|-------2-------|--ID of the nth device property of the sensor---------|
* |-----Raw Value n-----|----variable---|--Raw Value field defined by the nth device property--|
*/
for (i = 0; i < ARRAY_SIZE(sensor_states); i++) {
esp_ble_mesh_sensor_state_t *state = &sensor_states[i];
if (state->sensor_data.length == ESP_BLE_MESH_SENSOR_DATA_ZERO_LEN) {
buf_size += ESP_BLE_MESH_SENSOR_DATA_FORMAT_B_MPID_LEN;
} else {
/* Use "state->sensor_data.length + 1" because the length of sensor data is zero-based. */
if (state->sensor_data.format == ESP_BLE_MESH_SENSOR_DATA_FORMAT_A) {
buf_size += ESP_BLE_MESH_SENSOR_DATA_FORMAT_A_MPID_LEN + state->sensor_data.length + 1;
} else {
buf_size += ESP_BLE_MESH_SENSOR_DATA_FORMAT_B_MPID_LEN + state->sensor_data.length + 1;
}
}
}
status = calloc(1, buf_size);
if (!status) {
ESP_LOGE(TAG, "No memory for sensor status!");
return;
}
if (param->value.get.sensor_data.op_en == false) {
/* Mesh Model Spec:
* If the message is sent as a response to the Sensor Get message, and if the
* Property ID field of the incoming message is omitted, the Marshalled Sensor
* Data field shall contain data for all device properties within a sensor.
*/
for (i = 0; i < ARRAY_SIZE(sensor_states); i++) {
length += example_ble_mesh_get_sensor_data(&sensor_states[i], status + length);
}
goto send;
}
/* Mesh Model Spec:
* Otherwise, the Marshalled Sensor Data field shall contain data for the requested
* device property only.
*/
for (i = 0; i < ARRAY_SIZE(sensor_states); i++) {
if (param->value.get.sensor_data.property_id == sensor_states[i].sensor_property_id) {
length = example_ble_mesh_get_sensor_data(&sensor_states[i], status);
goto send;
}
}
/* Mesh Model Spec:
* Or the Length shall represent the value of zero and the Raw Value field shall
* contain only the Property ID if the requested device property is not recognized
* by the Sensor Server.
*/
mpid = ESP_BLE_MESH_SENSOR_DATA_FORMAT_B_MPID(ESP_BLE_MESH_SENSOR_DATA_ZERO_LEN,
param->value.get.sensor_data.property_id);
memcpy(status, &mpid, ESP_BLE_MESH_SENSOR_DATA_FORMAT_B_MPID_LEN);
length = ESP_BLE_MESH_SENSOR_DATA_FORMAT_B_MPID_LEN;
send:
ESP_LOG_BUFFER_HEX("Sensor Data", status, length);
err = esp_ble_mesh_server_model_send_msg(param->model, &param->ctx,
ESP_BLE_MESH_MODEL_OP_SENSOR_STATUS, length, status);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send Sensor Status");
}
free(status);
}
static void example_ble_mesh_send_sensor_column_status(esp_ble_mesh_sensor_server_cb_param_t *param)
{
uint8_t *status = NULL;
uint16_t length = 0;
esp_err_t err;
length = ESP_BLE_MESH_SENSOR_PROPERTY_ID_LEN +param->value.get.sensor_column.raw_value_x->len;
status = calloc(1, length);
if (!status) {
ESP_LOGE(TAG, "No memory for sensor column status!");
return;
}
memcpy(status, &param->value.get.sensor_column.property_id, ESP_BLE_MESH_SENSOR_PROPERTY_ID_LEN);
memcpy(status + ESP_BLE_MESH_SENSOR_PROPERTY_ID_LEN, param->value.get.sensor_column.raw_value_x->data,
param->value.get.sensor_column.raw_value_x->len);
err = esp_ble_mesh_server_model_send_msg(param->model, &param->ctx,
ESP_BLE_MESH_MODEL_OP_SENSOR_COLUMN_STATUS, length, status);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send Sensor Column Status");
}
free(status);
}
static void example_ble_mesh_send_sensor_series_status(esp_ble_mesh_sensor_server_cb_param_t *param)
{
esp_err_t err;
err = esp_ble_mesh_server_model_send_msg(param->model, &param->ctx,
ESP_BLE_MESH_MODEL_OP_SENSOR_SERIES_STATUS,
ESP_BLE_MESH_SENSOR_PROPERTY_ID_LEN,
(uint8_t *)&param->value.get.sensor_series.property_id);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to send Sensor Column Status");
}
}
static void example_ble_mesh_sensor_server_cb(esp_ble_mesh_sensor_server_cb_event_t event,
esp_ble_mesh_sensor_server_cb_param_t *param)
{
ESP_LOGI(TAG, "Sensor server, event %d, src 0x%04x, dst 0x%04x, model_id 0x%04x",
event, param->ctx.addr, param->ctx.recv_dst, param->model->model_id);
switch (event) {
case ESP_BLE_MESH_SENSOR_SERVER_RECV_GET_MSG_EVT:
switch (param->ctx.recv_op) {
case ESP_BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_GET:
ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_SENSOR_DESCRIPTOR_GET");
example_ble_mesh_send_sensor_descriptor_status(param);
break;
case ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_GET:
ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_GET");
example_ble_mesh_send_sensor_cadence_status(param);
break;
case ESP_BLE_MESH_MODEL_OP_SENSOR_SETTINGS_GET:
ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_SENSOR_SETTINGS_GET");
example_ble_mesh_send_sensor_settings_status(param);
break;
case ESP_BLE_MESH_MODEL_OP_SENSOR_SETTING_GET:
ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_SENSOR_SETTINGS_GET");
example_ble_mesh_send_sensor_setting_status(param);
break;
case ESP_BLE_MESH_MODEL_OP_SENSOR_GET:
ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_SENSOR_GET");
example_ble_mesh_send_sensor_status(param);
break;
case ESP_BLE_MESH_MODEL_OP_SENSOR_COLUMN_GET:
ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_SENSOR_COLUMN_GET");
example_ble_mesh_send_sensor_column_status(param);
break;
case ESP_BLE_MESH_MODEL_OP_SENSOR_SERIES_GET:
ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_SENSOR_SERIES_GET");
example_ble_mesh_send_sensor_series_status(param);
break;
default:
ESP_LOGE(TAG, "Unknown Sensor Get opcode 0x%04x", param->ctx.recv_op);
return;
}
break;
case ESP_BLE_MESH_SENSOR_SERVER_RECV_SET_MSG_EVT:
switch (param->ctx.recv_op) {
case ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET:
ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET");
example_ble_mesh_send_sensor_cadence_status(param);
break;
case ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET_UNACK:
ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_SENSOR_CADENCE_SET_UNACK");
break;
case ESP_BLE_MESH_MODEL_OP_SENSOR_SETTING_SET:
ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_SENSOR_SETTING_SET");
example_ble_mesh_send_sensor_setting_status(param);
break;
case ESP_BLE_MESH_MODEL_OP_SENSOR_SETTING_SET_UNACK:
ESP_LOGI(TAG, "ESP_BLE_MESH_MODEL_OP_SENSOR_SETTING_SET_UNACK");
break;
default:
ESP_LOGE(TAG, "Unknown Sensor Set opcode 0x%04x", param->ctx.recv_op);
break;
}
break;
default:
ESP_LOGE(TAG, "Unknown Sensor Server event %d", event);
break;
}
}
static esp_err_t ble_mesh_init(void)
{
esp_err_t err;
esp_ble_mesh_register_prov_callback(example_ble_mesh_provisioning_cb);
esp_ble_mesh_register_config_server_callback(example_ble_mesh_config_server_cb);
esp_ble_mesh_register_sensor_server_callback(example_ble_mesh_sensor_server_cb);
err = esp_ble_mesh_init(&provision, &composition);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize mesh stack");
return err;
}
err = esp_ble_mesh_node_prov_enable(ESP_BLE_MESH_PROV_ADV | ESP_BLE_MESH_PROV_GATT);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to enable mesh node");
return err;
}
board_led_operation(LED_G, LED_ON);
ESP_LOGI(TAG, "BLE Mesh sensor server initialized");
return ESP_OK;
}
void app_main(void)
{
esp_err_t err;
ESP_LOGI(TAG, "Initializing...");
err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES) {
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK(err);
board_init();
err = bluetooth_init();
if (err) {
ESP_LOGE(TAG, "esp32_bluetooth_init failed (err %d)", err);
return;
}
ble_mesh_get_dev_uuid(dev_uuid);
/* Initialize the Bluetooth Mesh Subsystem */
err = ble_mesh_init();
if (err) {
ESP_LOGE(TAG, "Bluetooth mesh init failed (err %d)", err);
}
}

View file

@ -0,0 +1,55 @@
/* board.c - Board-specific hooks */
/*
* Copyright (c) 2017 Intel Corporation
* Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include "driver/gpio.h"
#include "esp_log.h"
#include "board.h"
#define TAG "BOARD"
struct _led_state led_state[3] = {
{ LED_OFF, LED_OFF, LED_R, "red" },
{ LED_OFF, LED_OFF, LED_G, "green" },
{ LED_OFF, LED_OFF, LED_B, "blue" },
};
void board_led_operation(uint8_t pin, uint8_t onoff)
{
for (int i = 0; i < 3; i++) {
if (led_state[i].pin != pin) {
continue;
}
if (onoff == led_state[i].previous) {
ESP_LOGW(TAG, "led %s is already %s",
led_state[i].name, (onoff ? "on" : "off"));
return;
}
gpio_set_level(pin, onoff);
led_state[i].previous = onoff;
return;
}
ESP_LOGE(TAG, "LED is not found!");
}
static void board_led_init(void)
{
for (int i = 0; i < 3; i++) {
gpio_pad_select_gpio(led_state[i].pin);
gpio_set_direction(led_state[i].pin, GPIO_MODE_OUTPUT);
gpio_set_level(led_state[i].pin, LED_OFF);
led_state[i].previous = LED_OFF;
}
}
void board_init(void)
{
board_led_init();
}

View file

@ -0,0 +1,38 @@
/* board.h - Board-specific hooks */
/*
* Copyright (c) 2017 Intel Corporation
* Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _BOARD_H_
#define _BOARD_H_
#include "driver/gpio.h"
#if defined(CONFIG_BLE_MESH_ESP_WROOM_32)
#define LED_R GPIO_NUM_25
#define LED_G GPIO_NUM_26
#define LED_B GPIO_NUM_27
#elif defined(CONFIG_BLE_MESH_ESP_WROVER)
#define LED_R GPIO_NUM_0
#define LED_G GPIO_NUM_2
#define LED_B GPIO_NUM_4
#endif
#define LED_ON 1
#define LED_OFF 0
struct _led_state {
uint8_t current;
uint8_t previous;
uint8_t pin;
char *name;
};
void board_led_operation(uint8_t pin, uint8_t onoff);
void board_init(void);
#endif

View file

@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -0,0 +1,13 @@
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y
CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CONTROLLER_MODE_BTDM=n
CONFIG_BTDM_CONTROLLER_MODEM_SLEEP=n
CONFIG_SCAN_DUPLICATE_BY_ADV_DATA_AND_DEVICE_ADDR=y
CONFIG_BLE_MESH_SCAN_DUPLICATE_EN=y
CONFIG_GATTS_SEND_SERVICE_CHANGE_MANUAL=y
CONFIG_BTU_TASK_STACK_SIZE=4512
CONFIG_BLE_MESH=y
CONFIG_BLE_MESH_NODE=y
CONFIG_BLE_MESH_PB_GATT=y

View file

@ -0,0 +1,12 @@
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y
CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CONTROLLER_MODE_BTDM=n
CONFIG_BTDM_CONTROLLER_MODEM_SLEEP=n
CONFIG_SCAN_DUPLICATE_BY_ADV_DATA_AND_DEVICE_ADDR=y
CONFIG_BLE_MESH_SCAN_DUPLICATE_EN=y
CONFIG_NIMBLE_ENABLED=y
CONFIG_BLE_MESH=y
CONFIG_BLE_MESH_NODE=y
CONFIG_BLE_MESH_PB_GATT=y

View file

@ -0,0 +1,18 @@
# Override some defaults so BT stack is enabled
# by default in this example
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY=y
CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CONTROLLER_MODE_BTDM=n
CONFIG_BTDM_CONTROLLER_MODEM_SLEEP=n
CONFIG_SCAN_DUPLICATE_BY_ADV_DATA_AND_DEVICE_ADDR=y
CONFIG_BLE_MESH_SCAN_DUPLICATE_EN=y
CONFIG_GATTS_SEND_SERVICE_CHANGE_MANUAL=y
CONFIG_BTU_TASK_STACK_SIZE=4512
# Override some defaults of ESP BLE Mesh
CONFIG_BLE_MESH=y
CONFIG_BLE_MESH_NODE=y
CONFIG_BLE_MESH_PB_GATT=y
CONFIG_BLE_MESH_TX_SEG_MSG_COUNT=10
CONFIG_BLE_MESH_RX_SEG_MSG_COUNT=10

View file

@ -3,5 +3,4 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32)
project(vendor_client)

View file

@ -3,5 +3,4 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(SUPPORTED_TARGETS esp32)
project(vendor_server)