This is an automated email from the ASF dual-hosted git repository.
xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git
The following commit(s) were added to refs/heads/master by this push:
new c6d7c896c examples/nimble_bleess: Add Environmental Sensing peripheral
c6d7c896c is described below
commit c6d7c896c2e0a9a34f2d432fbf9d90fdccd6e01a
Author: Jackson-gjh <[email protected]>
AuthorDate: Sat Nov 7 15:24:50 2026 +0800
examples/nimble_bleess: Add Environmental Sensing peripheral
Add a reusable NimBLE peripheral example for the standard Bluetooth
Environmental Sensing service. The example exposes readable and
notifiable Temperature and Humidity characteristics using their standard
0.01-unit encodings.
Generate slowly changing sample values so the example can be exercised
without sensor hardware, while keeping the GATT update function available
for integration with a real sensor.
The example was built and run on ESP32-C6. A Bleak client discovered the
service, read both characteristics, and received notifications from both.
Assisted-by: Codex:gpt-5
Signed-off-by: Jackson-gjh <[email protected]>
---
examples/nimble_bleess/CMakeLists.txt | 37 +++++
examples/nimble_bleess/Kconfig | 30 ++++
examples/nimble_bleess/Make.defs | 25 ++++
examples/nimble_bleess/Makefile | 34 +++++
examples/nimble_bleess/bleess.h | 59 ++++++++
examples/nimble_bleess/bleess_gap.c | 195 ++++++++++++++++++++++++++
examples/nimble_bleess/bleess_gatt.c | 210 ++++++++++++++++++++++++++++
examples/nimble_bleess/nimble_bleess_main.c | 148 ++++++++++++++++++++
8 files changed, 738 insertions(+)
diff --git a/examples/nimble_bleess/CMakeLists.txt
b/examples/nimble_bleess/CMakeLists.txt
new file mode 100644
index 000000000..192e4bb20
--- /dev/null
+++ b/examples/nimble_bleess/CMakeLists.txt
@@ -0,0 +1,37 @@
+#
##############################################################################
+# apps/examples/nimble_bleess/CMakeLists.txt
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
contributor
+# license agreements. See the NOTICE file distributed with this work for
+# additional information regarding copyright ownership. The ASF licenses this
+# file to you under the Apache License, Version 2.0 (the "License"); you may
not
+# use this file except in compliance with the License. You may obtain a copy
of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+#
+#
##############################################################################
+
+if(CONFIG_EXAMPLES_NIMBLE_BLEESS)
+ nuttx_add_application(
+ NAME
+ ${CONFIG_EXAMPLES_NIMBLE_BLEESS_PROGNAME}
+ PRIORITY
+ ${CONFIG_EXAMPLES_NIMBLE_BLEESS_PRIORITY}
+ STACKSIZE
+ ${CONFIG_EXAMPLES_NIMBLE_BLEESS_STACKSIZE}
+ SRCS
+ ${CMAKE_CURRENT_LIST_DIR}/nimble_bleess_main.c
+ ${CMAKE_CURRENT_LIST_DIR}/bleess_gap.c
+ ${CMAKE_CURRENT_LIST_DIR}/bleess_gatt.c
+ DEPENDS
+ nimble)
+endif()
diff --git a/examples/nimble_bleess/Kconfig b/examples/nimble_bleess/Kconfig
new file mode 100644
index 000000000..013d9a91f
--- /dev/null
+++ b/examples/nimble_bleess/Kconfig
@@ -0,0 +1,30 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+config EXAMPLES_NIMBLE_BLEESS
+ tristate "NimBLE Environmental Sensing peripheral"
+ default n
+ depends on NIMBLE && NIMBLE_ROLE_PERIPHERAL
+ ---help---
+ Enable the NimBLE Environmental Sensing peripheral example.
+
+if EXAMPLES_NIMBLE_BLEESS
+
+config EXAMPLES_NIMBLE_BLEESS_PROGNAME
+ string "Program name"
+ default "bleess"
+ ---help---
+ This is the name of the program that will be used when the NSH
ELF
+ program is installed.
+
+config EXAMPLES_NIMBLE_BLEESS_PRIORITY
+ int "NimBLE task priority"
+ default 100
+
+config EXAMPLES_NIMBLE_BLEESS_STACKSIZE
+ int "NimBLE stack size"
+ default DEFAULT_TASK_STACKSIZE
+
+endif # EXAMPLES_NIMBLE_BLEESS
diff --git a/examples/nimble_bleess/Make.defs b/examples/nimble_bleess/Make.defs
new file mode 100644
index 000000000..510f48140
--- /dev/null
+++ b/examples/nimble_bleess/Make.defs
@@ -0,0 +1,25 @@
+############################################################################
+# apps/examples/nimble_bleess/Make.defs
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership. The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################
+
+ifneq ($(CONFIG_EXAMPLES_NIMBLE_BLEESS),)
+CONFIGURED_APPS += $(APPDIR)/examples/nimble_bleess
+endif
diff --git a/examples/nimble_bleess/Makefile b/examples/nimble_bleess/Makefile
new file mode 100644
index 000000000..1fb3d4edb
--- /dev/null
+++ b/examples/nimble_bleess/Makefile
@@ -0,0 +1,34 @@
+############################################################################
+# apps/examples/nimble_bleess/Makefile
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership. The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+############################################################################
+
+include $(APPDIR)/Make.defs
+
+PROGNAME = $(CONFIG_EXAMPLES_NIMBLE_BLEESS_PROGNAME)
+PRIORITY = $(CONFIG_EXAMPLES_NIMBLE_BLEESS_PRIORITY)
+STACKSIZE = $(CONFIG_EXAMPLES_NIMBLE_BLEESS_STACKSIZE)
+MODULE = $(CONFIG_EXAMPLES_NIMBLE_BLEESS)
+
+CSRCS = bleess_gap.c bleess_gatt.c
+MAINSRC = nimble_bleess_main.c
+
+include $(APPDIR)/wireless/bluetooth/nimble/Makefile.nimble
+include $(APPDIR)/Application.mk
diff --git a/examples/nimble_bleess/bleess.h b/examples/nimble_bleess/bleess.h
new file mode 100644
index 000000000..cf60a8be6
--- /dev/null
+++ b/examples/nimble_bleess/bleess.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+ * apps/examples/nimble_bleess/bleess.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __APPS_EXAMPLES_NIMBLE_BLEESS_BLEESS_H
+#define __APPS_EXAMPLES_NIMBLE_BLEESS_BLEESS_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+
+#include "host/ble_gatt.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define BLEESS_DEVICE_NAME "NuttX NimBLE ESS"
+#define BLEESS_SERVICE_UUID 0x181a
+#define BLEESS_TEMPERATURE_UUID 0x2a6e
+#define BLEESS_HUMIDITY_UUID 0x2a6f
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+int bleess_gap_init(void);
+void bleess_gap_on_reset(int reason);
+void bleess_gap_on_sync(void);
+
+int bleess_gatt_init(void);
+void bleess_gatt_register_cb(FAR struct ble_gatt_register_ctxt *ctxt,
+ FAR void *arg);
+void bleess_gatt_update(int16_t temperature_centi,
+ uint16_t humidity_centi);
+
+#endif /* __APPS_EXAMPLES_NIMBLE_BLEESS_BLEESS_H */
diff --git a/examples/nimble_bleess/bleess_gap.c
b/examples/nimble_bleess/bleess_gap.c
new file mode 100644
index 000000000..1fabe6472
--- /dev/null
+++ b/examples/nimble_bleess/bleess_gap.c
@@ -0,0 +1,195 @@
+/****************************************************************************
+ * apps/examples/nimble_bleess/bleess_gap.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "host/ble_hs.h"
+#include "host/util/util.h"
+#include "services/gap/ble_svc_gap.h"
+
+#include "bleess.h"
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static void bleess_gap_start_advertising(void);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static void bleess_gap_print_address(FAR const uint8_t *address)
+{
+ printf("%02x:%02x:%02x:%02x:%02x:%02x",
+ address[5], address[4], address[3],
+ address[2], address[1], address[0]);
+}
+
+static void
+bleess_gap_print_connection(FAR const struct ble_gap_conn_desc *desc)
+{
+ printf("handle=%u peer=", desc->conn_handle);
+ bleess_gap_print_address(desc->peer_ota_addr.val);
+ printf(" interval=%u latency=%u timeout=%u encrypted=%u bonded=%u\n",
+ desc->conn_itvl, desc->conn_latency, desc->supervision_timeout,
+ desc->sec_state.encrypted, desc->sec_state.bonded);
+}
+
+static int bleess_gap_event(FAR struct ble_gap_event *event, FAR void *arg)
+{
+ struct ble_gap_conn_desc desc;
+ int ret;
+
+ switch (event->type)
+ {
+ case BLE_GAP_EVENT_CONNECT:
+ printf("GAP connect: status=%d\n", event->connect.status);
+ if (event->connect.status == 0)
+ {
+ ret = ble_gap_conn_find(event->connect.conn_handle, &desc);
+ if (ret == 0)
+ {
+ bleess_gap_print_connection(&desc);
+ }
+ }
+ else
+ {
+ bleess_gap_start_advertising();
+ }
+
+ return 0;
+
+ case BLE_GAP_EVENT_DISCONNECT:
+ printf("GAP disconnect: reason=%d\n", event->disconnect.reason);
+ bleess_gap_start_advertising();
+ return 0;
+
+ case BLE_GAP_EVENT_CONN_UPDATE:
+ printf("GAP connection update: status=%d\n",
+ event->conn_update.status);
+ return 0;
+
+ case BLE_GAP_EVENT_ADV_COMPLETE:
+ printf("GAP advertising complete: reason=%d\n",
+ event->adv_complete.reason);
+ bleess_gap_start_advertising();
+ return 0;
+
+ case BLE_GAP_EVENT_SUBSCRIBE:
+ printf("GATT subscribe: attr=%u notify=%u indicate=%u\n",
+ event->subscribe.attr_handle,
+ event->subscribe.cur_notify,
+ event->subscribe.cur_indicate);
+ return 0;
+
+ case BLE_GAP_EVENT_MTU:
+ printf("ATT MTU: conn=%u mtu=%u\n",
+ event->mtu.conn_handle, event->mtu.value);
+ return 0;
+
+ default:
+ return 0;
+ }
+}
+
+static void bleess_gap_start_advertising(void)
+{
+ struct ble_gap_adv_params parameters;
+ struct ble_hs_adv_fields fields;
+ ble_uuid16_t service_uuid = BLE_UUID16_INIT(BLEESS_SERVICE_UUID);
+ FAR const char *name;
+ uint8_t own_address_type;
+ int ret;
+
+ ret = ble_hs_id_infer_auto(0, &own_address_type);
+ if (ret != 0)
+ {
+ printf("GAP address selection failed: %d\n", ret);
+ return;
+ }
+
+ memset(&fields, 0, sizeof(fields));
+ fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
+ fields.tx_pwr_lvl_is_present = 1;
+ fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
+
+ name = ble_svc_gap_device_name();
+ fields.name = (FAR uint8_t *)name;
+ fields.name_len = strlen(name);
+ fields.name_is_complete = 1;
+ fields.uuids16 = &service_uuid;
+ fields.num_uuids16 = 1;
+ fields.uuids16_is_complete = 1;
+
+ ret = ble_gap_adv_set_fields(&fields);
+ if (ret != 0)
+ {
+ printf("GAP advertising data failed: %d\n", ret);
+ return;
+ }
+
+ memset(¶meters, 0, sizeof(parameters));
+ parameters.conn_mode = BLE_GAP_CONN_MODE_UND;
+ parameters.disc_mode = BLE_GAP_DISC_MODE_GEN;
+
+ ret = ble_gap_adv_start(own_address_type, NULL, BLE_HS_FOREVER,
+ ¶meters, bleess_gap_event, NULL);
+ printf("GAP advertising start: ret=%d name=%s service=0x%04x\n",
+ ret, name, BLEESS_SERVICE_UUID);
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int bleess_gap_init(void)
+{
+ ble_svc_gap_init();
+ return ble_svc_gap_device_name_set(BLEESS_DEVICE_NAME);
+}
+
+void bleess_gap_on_reset(int reason)
+{
+ printf("NimBLE reset: reason=%d\n", reason);
+}
+
+void bleess_gap_on_sync(void)
+{
+ int ret;
+
+ ret = ble_hs_util_ensure_addr(0);
+ if (ret != 0)
+ {
+ printf("NimBLE identity address failed: %d\n", ret);
+ return;
+ }
+
+ bleess_gap_start_advertising();
+}
diff --git a/examples/nimble_bleess/bleess_gatt.c
b/examples/nimble_bleess/bleess_gatt.c
new file mode 100644
index 000000000..b82aae3bb
--- /dev/null
+++ b/examples/nimble_bleess/bleess_gatt.c
@@ -0,0 +1,210 @@
+/****************************************************************************
+ * apps/examples/nimble_bleess/bleess_gatt.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdio.h>
+
+#include "host/ble_hs.h"
+#include "host/ble_uuid.h"
+#include "services/gatt/ble_svc_gatt.h"
+
+#include "bleess.h"
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static int bleess_gatt_access(uint16_t connection_handle,
+ uint16_t attribute_handle,
+ FAR struct ble_gatt_access_ctxt *context,
+ FAR void *arg);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const ble_uuid16_t g_bleess_service_uuid =
+ BLE_UUID16_INIT(BLEESS_SERVICE_UUID);
+static const ble_uuid16_t g_bleess_temperature_uuid =
+ BLE_UUID16_INIT(BLEESS_TEMPERATURE_UUID);
+static const ble_uuid16_t g_bleess_humidity_uuid =
+ BLE_UUID16_INIT(BLEESS_HUMIDITY_UUID);
+
+static int16_t g_bleess_temperature_centi = 2400;
+static uint16_t g_bleess_humidity_centi = 5000;
+static uint16_t g_bleess_temperature_handle;
+static uint16_t g_bleess_humidity_handle;
+
+static const struct ble_gatt_svc_def g_bleess_services[] =
+{
+ {
+ /* Service: Environmental Sensing. */
+
+ .type = BLE_GATT_SVC_TYPE_PRIMARY,
+ .uuid = &g_bleess_service_uuid.u,
+ .characteristics = (struct ble_gatt_chr_def[])
+ {
+ {
+ /* Characteristic: Temperature. */
+
+ .uuid = &g_bleess_temperature_uuid.u,
+ .access_cb = bleess_gatt_access,
+ .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY,
+ },
+
+ {
+ /* Characteristic: Humidity. */
+
+ .uuid = &g_bleess_humidity_uuid.u,
+ .access_cb = bleess_gatt_access,
+ .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_NOTIFY,
+ },
+
+ {
+ 0, /* No more characteristics in this service. */
+ }
+ },
+ },
+
+ {
+ 0, /* No more services. */
+ },
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static int bleess_gatt_access(uint16_t connection_handle,
+ uint16_t attribute_handle,
+ FAR struct ble_gatt_access_ctxt *context,
+ FAR void *arg)
+{
+ FAR const ble_uuid_t *uuid = context->chr->uuid;
+ uint16_t encoded_temperature;
+ uint8_t value[2];
+
+ UNUSED(connection_handle);
+ UNUSED(attribute_handle);
+ UNUSED(arg);
+
+ if (context->op != BLE_GATT_ACCESS_OP_READ_CHR)
+ {
+ return BLE_ATT_ERR_WRITE_NOT_PERMITTED;
+ }
+
+ if (ble_uuid_cmp(uuid, &g_bleess_temperature_uuid.u) == 0)
+ {
+ encoded_temperature = (uint16_t)g_bleess_temperature_centi;
+ value[0] = (uint8_t)encoded_temperature;
+ value[1] = (uint8_t)(encoded_temperature >> 8);
+ }
+ else if (ble_uuid_cmp(uuid, &g_bleess_humidity_uuid.u) == 0)
+ {
+ value[0] = (uint8_t)g_bleess_humidity_centi;
+ value[1] = (uint8_t)(g_bleess_humidity_centi >> 8);
+ }
+ else
+ {
+ return BLE_ATT_ERR_UNLIKELY;
+ }
+
+ if (os_mbuf_append(context->om, value, sizeof(value)) != 0)
+ {
+ return BLE_ATT_ERR_INSUFFICIENT_RES;
+ }
+
+ return 0;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+void bleess_gatt_register_cb(FAR struct ble_gatt_register_ctxt *context,
+ FAR void *arg)
+{
+ char uuid_string[BLE_UUID_STR_LEN];
+
+ UNUSED(arg);
+
+ if (context->op == BLE_GATT_REGISTER_OP_SVC)
+ {
+ printf("GATT service: uuid=%s handle=%u\n",
+ ble_uuid_to_str(context->svc.svc_def->uuid, uuid_string),
+ context->svc.handle);
+ }
+ else if (context->op == BLE_GATT_REGISTER_OP_CHR)
+ {
+ printf("GATT characteristic: uuid=%s value_handle=%u\n",
+ ble_uuid_to_str(context->chr.chr_def->uuid, uuid_string),
+ context->chr.val_handle);
+
+ if (ble_uuid_cmp(context->chr.chr_def->uuid,
+ &g_bleess_temperature_uuid.u) == 0)
+ {
+ g_bleess_temperature_handle = context->chr.val_handle;
+ }
+ else if (ble_uuid_cmp(context->chr.chr_def->uuid,
+ &g_bleess_humidity_uuid.u) == 0)
+ {
+ g_bleess_humidity_handle = context->chr.val_handle;
+ }
+ }
+}
+
+int bleess_gatt_init(void)
+{
+ int ret;
+
+ ble_svc_gatt_init();
+
+ ret = ble_gatts_count_cfg(g_bleess_services);
+ if (ret != 0)
+ {
+ return ret;
+ }
+
+ return ble_gatts_add_svcs(g_bleess_services);
+}
+
+void bleess_gatt_update(int16_t temperature_centi,
+ uint16_t humidity_centi)
+{
+ g_bleess_temperature_centi = temperature_centi;
+ g_bleess_humidity_centi = humidity_centi;
+
+ if (g_bleess_temperature_handle != 0)
+ {
+ ble_gatts_chr_updated(g_bleess_temperature_handle);
+ }
+
+ if (g_bleess_humidity_handle != 0)
+ {
+ ble_gatts_chr_updated(g_bleess_humidity_handle);
+ }
+}
diff --git a/examples/nimble_bleess/nimble_bleess_main.c
b/examples/nimble_bleess/nimble_bleess_main.c
new file mode 100644
index 000000000..bb04d35ff
--- /dev/null
+++ b/examples/nimble_bleess/nimble_bleess_main.c
@@ -0,0 +1,148 @@
+/****************************************************************************
+ * apps/examples/nimble_bleess/nimble_bleess_main.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "host/ble_hs.h"
+#include "host/util/util.h"
+#include "netutils/netinit.h"
+#include "nimble/nimble_port.h"
+
+#include "bleess.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define BLEESS_TASK_PRIORITY CONFIG_EXAMPLES_NIMBLE_BLEESS_PRIORITY
+
+/****************************************************************************
+ * External Function Prototypes
+ ****************************************************************************/
+
+void ble_hci_sock_ack_handler(FAR void *param);
+void ble_hci_sock_set_device(int dev);
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+static FAR void *bleess_hci_task(FAR void *param)
+{
+ ble_hci_sock_ack_handler(param);
+ return NULL;
+}
+
+static FAR void *bleess_host_task(FAR void *param)
+{
+ nimble_port_run();
+ return NULL;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int main(int argc, FAR char *argv[])
+{
+ struct ble_npl_task host_task;
+ struct ble_npl_task hci_task;
+ int16_t temperature_centi = 2400;
+ uint16_t humidity_centi = 5000;
+ int direction = 1;
+ int ret;
+
+ if (argc > 1)
+ {
+ ble_hci_sock_set_device(atoi(argv[1]));
+ }
+
+#ifndef CONFIG_NSH_NETINIT
+ netinit_bringup();
+#endif
+
+ nimble_port_init();
+
+ ble_hs_cfg.reset_cb = bleess_gap_on_reset;
+ ble_hs_cfg.sync_cb = bleess_gap_on_sync;
+ ble_hs_cfg.gatts_register_cb = bleess_gatt_register_cb;
+ ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
+
+ ret = bleess_gap_init();
+ if (ret != 0)
+ {
+ printf("ERROR: GAP initialization failed: %d\n", ret);
+ return EXIT_FAILURE;
+ }
+
+ ret = bleess_gatt_init();
+ if (ret != 0)
+ {
+ printf("ERROR: GATT initialization failed: %d\n", ret);
+ return EXIT_FAILURE;
+ }
+
+ ret = ble_npl_task_init(&hci_task, "hci_sock", bleess_hci_task, NULL,
+ BLEESS_TASK_PRIORITY, BLE_NPL_TIME_FOREVER,
+ NULL, 0);
+ if (ret != 0)
+ {
+ printf("ERROR: HCI task creation failed: %d\n", ret);
+ return EXIT_FAILURE;
+ }
+
+ ret = ble_npl_task_init(&host_task, "ble_host", bleess_host_task, NULL,
+ BLEESS_TASK_PRIORITY, BLE_NPL_TIME_FOREVER,
+ NULL, 0);
+ if (ret != 0)
+ {
+ printf("ERROR: Host task creation failed: %d\n", ret);
+ return EXIT_FAILURE;
+ }
+
+ while (true)
+ {
+ temperature_centi += direction;
+ humidity_centi += direction * 2;
+
+ if (temperature_centi >= 2410 || temperature_centi <= 2390)
+ {
+ direction = -direction;
+ }
+
+ bleess_gatt_update(temperature_centi, humidity_centi);
+ printf("Temperature=%d.%02d C Humidity=%u.%02u %%\n",
+ temperature_centi / 100, temperature_centi % 100,
+ humidity_centi / 100, humidity_centi % 100);
+ sleep(2);
+ }
+
+ return EXIT_SUCCESS;
+}