diff --git a/.properties b/.properties
index 2d1467d5cea..77eab581acd 100644
--- a/.properties
+++ b/.properties
@@ -1,6 +1,6 @@
id=com.silabs.sdk.stack.super
-version=4.3.1
+version=4.3.2
label=Gecko SDK Suite
description=Gecko SDK Suite
diff --git a/app/amazon/amazon_platform_experimental_templates.xml b/app/amazon/amazon_platform_experimental_templates.xml
index a0344093023..41f8900b59a 100644
--- a/app/amazon/amazon_platform_experimental_templates.xml
+++ b/app/amazon/amazon_platform_experimental_templates.xml
@@ -8,7 +8,7 @@
-
+
diff --git a/app/bluetooth/bluetooth_experimental_demos.xml b/app/bluetooth/bluetooth_experimental_demos.xml
index 91d8ecb6768..d16c1e92a02 100644
--- a/app/bluetooth/bluetooth_experimental_demos.xml
+++ b/app/bluetooth/bluetooth_experimental_demos.xml
@@ -6,11 +6,11 @@
-
+
-
+
ABR initiator for Bluetooth. The example connects to an ABR reflector and starts distance measurement. Results are printed on the display of the WSTK.
@@ -18,11 +18,11 @@
-
+
-
+
ABR reflector for Bluetooth. The example sends measurement results to the initiator via GATT. The application starts advertising after boot and restarts advertising after a connection is closed. It also supports Over-the-Air Device Firmware Upgrade (OTA DFU).
@@ -30,10 +30,10 @@
-
+
-
+
diff --git a/app/bluetooth/bluetooth_experimental_templates.xml b/app/bluetooth/bluetooth_experimental_templates.xml
index c9fc79c35e1..dc7341f474b 100644
--- a/app/bluetooth/bluetooth_experimental_templates.xml
+++ b/app/bluetooth/bluetooth_experimental_templates.xml
@@ -83,9 +83,9 @@
-
+
-
+
@@ -100,7 +100,7 @@
-
+
@@ -113,9 +113,9 @@
-
+
-
+
@@ -130,7 +130,7 @@
-
+
@@ -145,7 +145,7 @@
-
+
diff --git a/app/bluetooth/bluetooth_internal_demos.xml b/app/bluetooth/bluetooth_internal_demos.xml
index 98f06f1df98..40d1423ebbc 100644
--- a/app/bluetooth/bluetooth_internal_demos.xml
+++ b/app/bluetooth/bluetooth_internal_demos.xml
@@ -6,11 +6,11 @@
-
+
-
+
This example is an evaluation showcase for a Bluetooth Electronic Shelf Label (ESL) Tag application with the ESL Tag Demo component. The example includes image and display capabilities of an ESL Tag, utilizing the memory LCD display on the WSTK board while images are stored in RAM, only. Instead of the Silicon Labs logo, the Demo example will ask the user to run the QRCode generator on startup using the WSTK's display. After configuration the display will show ESL related information.
@@ -18,11 +18,11 @@
-
+
-
+
This example is an evaluation showcase for a Bluetooth Electronic Shelf Label (ESL) Tag application with the ESL Tag Demo component. The example includes image and display capabilities of an ESL Tag, utilizing the memory LCD display on the WSTK board while images are stored in RAM, only. Instead of the Silicon Labs logo, the Demo example will ask the user to run the QRCode generator on startup using the WSTK's display. After configuration the display will show ESL related information.
@@ -30,11 +30,11 @@
-
+
-
+
This example is an evaluation showcase for a Bluetooth Electronic Shelf Label (ESL) Tag application with the ESL Tag Demo component. The example includes image and display capabilities of an ESL Tag, utilizing the memory LCD display on the WSTK board while images are stored in RAM, only. Instead of the Silicon Labs logo, the Demo example will ask the user to run the QRCode generator on startup using the WSTK's display. After configuration the display will show ESL related information.
@@ -42,11 +42,11 @@
-
+
-
+
This example is an evaluation showcase for a Bluetooth Electronic Shelf Label (ESL) Tag application with the ESL Tag Demo component. The example includes image and display capabilities of an ESL Tag, utilizing the memory LCD display on the WSTK board while images are stored in RAM, only. Instead of the Silicon Labs logo, the Demo example will ask the user to run the QRCode generator on startup using the WSTK's display. After configuration the display will show ESL related information.
@@ -54,11 +54,11 @@
-
+
-
+
This example is an evaluation showcase for a Bluetooth Electronic Shelf Label (ESL) Tag application with the ESL Tag Demo component. The example includes image and display capabilities of an ESL Tag, utilizing the memory LCD display on the WSTK board while images are stored in RAM, only. Instead of the Silicon Labs logo, the Demo example will ask the user to run the QRCode generator on startup using the WSTK's display. After configuration the display will show ESL related information.
@@ -66,11 +66,11 @@
-
+
-
+
This example is an evaluation showcase for a Bluetooth Electronic Shelf Label (ESL) Tag application with the ESL Tag Demo component. The example includes image and display capabilities of an ESL Tag, utilizing the memory LCD display on the WSTK board while images are stored in RAM, only. Instead of the Silicon Labs logo, the Demo example will ask the user to run the QRCode generator on startup using the WSTK's display. After configuration the display will show ESL related information.
@@ -78,11 +78,11 @@
-
+
-
+
This example is an evaluation showcase for a Bluetooth Electronic Shelf Label (ESL) Tag application with the ESL Tag Demo component. The example includes image and display capabilities of an ESL Tag, utilizing the memory LCD display on the WSTK board while images are stored in RAM, only. Instead of the Silicon Labs logo, the Demo example will ask the user to run the QRCode generator on startup using the WSTK's display. After configuration the display will show ESL related information.
@@ -90,11 +90,11 @@
-
+
-
+
This example is an evaluation showcase for a Bluetooth Electronic Shelf Label (ESL) Tag application with the ESL Tag Demo component. The example includes image and display capabilities of an ESL Tag, utilizing the memory LCD display on the WSTK board while images are stored in RAM, only. Instead of the Silicon Labs logo, the Demo example will ask the user to run the QRCode generator on startup using the WSTK's display. After configuration the display will show ESL related information.
@@ -102,11 +102,11 @@
-
+
-
+
This example is an evaluation showcase for a Bluetooth Electronic Shelf Label (ESL) Tag application with the ESL Tag Demo component. The example includes image and display capabilities of an ESL Tag, utilizing the memory LCD display on the WSTK board while images are stored in RAM, only. Instead of the Silicon Labs logo, the Demo example will ask the user to run the QRCode generator on startup using the WSTK's display. After configuration the display will show ESL related information.
@@ -114,11 +114,11 @@
-
+
-
+
This example is an evaluation showcase for a Bluetooth Electronic Shelf Label (ESL) Tag application with the ESL Tag Demo component. The example includes image and display capabilities of an ESL Tag, utilizing the memory LCD display on the WSTK board while images are stored in RAM, only. Instead of the Silicon Labs logo, the Demo example will ask the user to run the QRCode generator on startup using the WSTK's display. After configuration the display will show ESL related information.
@@ -126,11 +126,11 @@
-
+
-
+
This example is an evaluation showcase for a Bluetooth Electronic Shelf Label (ESL) Tag application with the ESL Tag Demo component. The example includes image and display capabilities of an ESL Tag, utilizing the memory LCD display on the WSTK board while images are stored in RAM, only. Instead of the Silicon Labs logo, the Demo example will ask the user to run the QRCode generator on startup using the WSTK's display. After configuration the display will show ESL related information.
@@ -138,11 +138,11 @@
-
+
-
+
This example is an evaluation showcase for a Bluetooth Electronic Shelf Label (ESL) Tag application with the ESL Tag Demo component. The example includes image and display capabilities of an ESL Tag, utilizing the memory LCD display on the WSTK board while images are stored in RAM, only. Instead of the Silicon Labs logo, the Demo example will ask the user to run the QRCode generator on startup using the WSTK's display. After configuration the display will show ESL related information.
@@ -150,11 +150,11 @@
-
+
-
+
This example is an evaluation showcase for a Bluetooth Electronic Shelf Label (ESL) Tag application with the ESL Tag Demo component. The example includes image and display capabilities of an ESL Tag, utilizing the memory LCD display on the WSTK board while images are stored in RAM, only. Instead of the Silicon Labs logo, the Demo example will ask the user to run the QRCode generator on startup using the WSTK's display. After configuration the display will show ESL related information.
@@ -162,11 +162,11 @@
-
+
-
+
This example is an evaluation showcase for a Bluetooth Electronic Shelf Label (ESL) Tag application with the ESL Tag Demo component. The example includes image and display capabilities of an ESL Tag, utilizing the memory LCD display on the WSTK board while images are stored in RAM, only. Instead of the Silicon Labs logo, the Demo example will ask the user to run the QRCode generator on startup using the WSTK's display. After configuration the display will show ESL related information.
@@ -174,10 +174,10 @@
-
+
-
+
diff --git a/app/bluetooth/bluetooth_production_demos.xml b/app/bluetooth/bluetooth_production_demos.xml
index 099d64efde6..5e83d839994 100644
--- a/app/bluetooth/bluetooth_production_demos.xml
+++ b/app/bluetooth/bluetooth_production_demos.xml
@@ -6,11 +6,11 @@
-
+
-
+
Network Co-Processor (NCP) target application with additional features to support the Electronic Shelf Label Profile ESL Access Point role. Note: Some BLE features unused by the ESL Access Point are removed compared to the NCP target application.
@@ -18,11 +18,11 @@
-
+
-
+
Network Co-Processor (NCP) target application with additional features to support the Electronic Shelf Label Profile ESL Access Point role. Note: Some BLE features unused by the ESL Access Point are removed compared to the NCP target application.
@@ -30,11 +30,11 @@
-
+
-
+
Network Co-Processor (NCP) target application with additional features to support the Electronic Shelf Label Profile ESL Access Point role. Note: Some BLE features unused by the ESL Access Point are removed compared to the NCP target application.
@@ -42,11 +42,11 @@
-
+
-
+
Network Co-Processor (NCP) target application with additional features to support the Electronic Shelf Label Profile ESL Access Point role. Note: Some BLE features unused by the ESL Access Point are removed compared to the NCP target application.
@@ -54,23 +54,11 @@
-
+
-
-
-
- Network Co-Processor (NCP) target application with additional features to support the Electronic Shelf Label Profile ESL Access Point role. Note: Some BLE features unused by the ESL Access Point are removed compared to the NCP target application.
-
-
-
-
-
-
-
-
-
+
Network Co-Processor (NCP) target application with additional features to support the Electronic Shelf Label Profile ESL Access Point role. Note: Some BLE features unused by the ESL Access Point are removed compared to the NCP target application.
@@ -78,11 +66,11 @@
-
+
-
+
Network Co-Processor (NCP) target application with additional features to support the Electronic Shelf Label Profile ESL Access Point role. Note: Some BLE features unused by the ESL Access Point are removed compared to the NCP target application.
@@ -90,11 +78,11 @@
-
+
-
+
Network Co-Processor (NCP) target application with additional features to support the Electronic Shelf Label Profile ESL Access Point role. Note: Some BLE features unused by the ESL Access Point are removed compared to the NCP target application.
@@ -102,11 +90,11 @@
-
+
-
+
Network Co-Processor (NCP) target application with additional features to support the Electronic Shelf Label Profile ESL Access Point role. Note: Some BLE features unused by the ESL Access Point are removed compared to the NCP target application.
@@ -114,11 +102,11 @@
-
+
-
+
Network Co-Processor (NCP) target application with additional features to support the Electronic Shelf Label Profile ESL Access Point role. Note: Some BLE features unused by the ESL Access Point are removed compared to the NCP target application.
@@ -126,11 +114,11 @@
-
+
-
+
Network Co-Processor (NCP) target application with additional features to support the Electronic Shelf Label Profile ESL Access Point role. Note: Some BLE features unused by the ESL Access Point are removed compared to the NCP target application.
@@ -138,11 +126,11 @@
-
+
-
+
Network Co-Processor (NCP) target application with additional features to support the Electronic Shelf Label Profile ESL Access Point role. Note: Some BLE features unused by the ESL Access Point are removed compared to the NCP target application.
@@ -150,11 +138,11 @@
-
+
-
+
Network Co-Processor (NCP) target application with additional features to support the Electronic Shelf Label Profile ESL Access Point role. Note: Some BLE features unused by the ESL Access Point are removed compared to the NCP target application.
@@ -162,11 +150,11 @@
-
+
-
+
Network Co-Processor (NCP) target application with additional features to support the Electronic Shelf Label Profile ESL Access Point role. Note: Some BLE features unused by the ESL Access Point are removed compared to the NCP target application.
@@ -174,11 +162,11 @@
-
+
-
+
Network Co-Processor (NCP) target application with additional features to support the Electronic Shelf Label Profile ESL Access Point role. Note: Some BLE features unused by the ESL Access Point are removed compared to the NCP target application.
@@ -186,11 +174,11 @@
-
+
-
+
Network Co-Processor (NCP) target application with additional features to support the Electronic Shelf Label Profile ESL Access Point role. Note: Some BLE features unused by the ESL Access Point are removed compared to the NCP target application.
@@ -198,11 +186,11 @@
-
+
-
+
Network Co-Processor (NCP) target application with additional features to support the Electronic Shelf Label Profile ESL Access Point role. Note: Some BLE features unused by the ESL Access Point are removed compared to the NCP target application.
@@ -210,11 +198,11 @@
-
+
-
+
Network Co-Processor (NCP) target application with additional features to support the Electronic Shelf Label Profile ESL Access Point role. Note: Some BLE features unused by the ESL Access Point are removed compared to the NCP target application.
@@ -222,11 +210,11 @@
-
+
-
+
Network Co-Processor (NCP) target application with additional features to support the Electronic Shelf Label Profile ESL Access Point role. Note: Some BLE features unused by the ESL Access Point are removed compared to the NCP target application.
@@ -234,11 +222,11 @@
-
+
-
+
Network Co-Processor (NCP) target application with additional features to support the Electronic Shelf Label Profile ESL Access Point role. Note: Some BLE features unused by the ESL Access Point are removed compared to the NCP target application.
@@ -246,11 +234,11 @@
-
+
-
+
Network Co-Processor (NCP) target application. Runs the Bluetooth stack dynamically and provides access to it via Bluetooth API (BGAPI) using UART connection. NCP mode makes it possible to run your application on a host controller or PC.
@@ -258,11 +246,11 @@
-
+
-
+
Network Co-Processor (NCP) target application. Runs the Bluetooth stack dynamically and provides access to it via Bluetooth API (BGAPI) using UART connection. NCP mode makes it possible to run your application on a host controller or PC.
@@ -270,11 +258,11 @@
-
+
-
+
Network Co-Processor (NCP) target application. Runs the Bluetooth stack dynamically and provides access to it via Bluetooth API (BGAPI) using UART connection. NCP mode makes it possible to run your application on a host controller or PC.
@@ -282,11 +270,11 @@
-
+
-
+
Network Co-Processor (NCP) target application. Runs the Bluetooth stack dynamically and provides access to it via Bluetooth API (BGAPI) using UART connection. NCP mode makes it possible to run your application on a host controller or PC.
@@ -294,11 +282,11 @@
-
+
-
+
Network Co-Processor (NCP) target application. Runs the Bluetooth stack dynamically and provides access to it via Bluetooth API (BGAPI) using UART connection. NCP mode makes it possible to run your application on a host controller or PC.
@@ -306,11 +294,11 @@
-
+
-
+
Network Co-Processor (NCP) target application. Runs the Bluetooth stack dynamically and provides access to it via Bluetooth API (BGAPI) using UART connection. NCP mode makes it possible to run your application on a host controller or PC.
@@ -318,11 +306,11 @@
-
+
-
+
Network Co-Processor (NCP) target application. Runs the Bluetooth stack dynamically and provides access to it via Bluetooth API (BGAPI) using UART connection. NCP mode makes it possible to run your application on a host controller or PC.
@@ -330,11 +318,11 @@
-
+
-
+
Network Co-Processor (NCP) target application. Runs the Bluetooth stack dynamically and provides access to it via Bluetooth API (BGAPI) using UART connection. NCP mode makes it possible to run your application on a host controller or PC.
@@ -342,11 +330,11 @@
-
+
-
+
Network Co-Processor (NCP) target application. Runs the Bluetooth stack dynamically and provides access to it via Bluetooth API (BGAPI) using UART connection. NCP mode makes it possible to run your application on a host controller or PC.
@@ -354,11 +342,11 @@
-
+
-
+
Network Co-Processor (NCP) target application. Runs the Bluetooth stack dynamically and provides access to it via Bluetooth API (BGAPI) using UART connection. NCP mode makes it possible to run your application on a host controller or PC.
@@ -366,11 +354,11 @@
-
+
-
+
Network Co-Processor (NCP) target application. Runs the Bluetooth stack dynamically and provides access to it via Bluetooth API (BGAPI) using UART connection. NCP mode makes it possible to run your application on a host controller or PC.
@@ -378,11 +366,11 @@
-
+
-
+
Network Co-Processor (NCP) target application. Runs the Bluetooth stack dynamically and provides access to it via Bluetooth API (BGAPI) using UART connection. NCP mode makes it possible to run your application on a host controller or PC.
@@ -390,11 +378,11 @@
-
+
-
+
Network Co-Processor (NCP) target application. Runs the Bluetooth stack dynamically and provides access to it via Bluetooth API (BGAPI) using UART connection. NCP mode makes it possible to run your application on a host controller or PC.
@@ -402,11 +390,11 @@
-
+
-
+
Network Co-Processor (NCP) target application. Runs the Bluetooth stack dynamically and provides access to it via Bluetooth API (BGAPI) using UART connection. NCP mode makes it possible to run your application on a host controller or PC.
@@ -414,11 +402,11 @@
-
+
-
+
Network Co-Processor (NCP) target application. Runs the Bluetooth stack dynamically and provides access to it via Bluetooth API (BGAPI) using UART connection. NCP mode makes it possible to run your application on a host controller or PC.
@@ -426,11 +414,11 @@
-
+
-
+
Network Co-Processor (NCP) target application. Runs the Bluetooth stack dynamically and provides access to it via Bluetooth API (BGAPI) using UART connection. NCP mode makes it possible to run your application on a host controller or PC.
@@ -438,11 +426,11 @@
-
+
-
+
Network Co-Processor (NCP) target application. Runs the Bluetooth stack dynamically and provides access to it via Bluetooth API (BGAPI) using UART connection. NCP mode makes it possible to run your application on a host controller or PC.
@@ -450,11 +438,11 @@
-
+
-
+
Network Co-Processor (NCP) target application. Runs the Bluetooth stack dynamically and provides access to it via Bluetooth API (BGAPI) using UART connection. NCP mode makes it possible to run your application on a host controller or PC.
@@ -462,11 +450,11 @@
-
+
-
+
Network Co-Processor (NCP) target application. Runs the Bluetooth stack dynamically and provides access to it via Bluetooth API (BGAPI) using UART connection. NCP mode makes it possible to run your application on a host controller or PC.
@@ -474,11 +462,11 @@
-
+
-
+
Network Co-Processor (NCP) target application. Runs the Bluetooth stack dynamically and provides access to it via Bluetooth API (BGAPI) using UART connection. NCP mode makes it possible to run your application on a host controller or PC.
@@ -486,11 +474,11 @@
-
+
-
+
Network Co-Processor (NCP) target application. Runs the Bluetooth stack dynamically and provides access to it via Bluetooth API (BGAPI) using UART connection. NCP mode makes it possible to run your application on a host controller or PC.
@@ -498,11 +486,11 @@
-
+
-
+
Network Co-Processor (NCP) target application. Runs the Bluetooth stack dynamically and provides access to it via Bluetooth API (BGAPI) using UART connection. NCP mode makes it possible to run your application on a host controller or PC.
@@ -510,11 +498,11 @@
-
+
-
+
Network Co-Processor (NCP) target application. Runs the Bluetooth stack dynamically and provides access to it via Bluetooth API (BGAPI) using UART connection. NCP mode makes it possible to run your application on a host controller or PC.
@@ -522,11 +510,11 @@
-
+
-
+
Network Co-Processor (NCP) target application. Runs the Bluetooth stack dynamically and provides access to it via Bluetooth API (BGAPI) using UART connection. NCP mode makes it possible to run your application on a host controller or PC.
@@ -534,11 +522,11 @@
-
+
-
+
Network Co-Processor (NCP) target application. Runs the Bluetooth stack dynamically and provides access to it via Bluetooth API (BGAPI) using UART connection. NCP mode makes it possible to run your application on a host controller or PC.
@@ -546,11 +534,11 @@
-
+
-
+
Network Co-Processor (NCP) target application. Runs the Bluetooth stack dynamically and provides access to it via Bluetooth API (BGAPI) using UART connection. NCP mode makes it possible to run your application on a host controller or PC.
@@ -558,11 +546,11 @@
-
+
-
+
The classic blinky example using Bluetooth communication. Demonstrates a simple two-way data exchange over GATT. This can be tested with the EFR Connect mobile app.
@@ -570,11 +558,11 @@
-
+
-
+
The classic blinky example using Bluetooth communication. Demonstrates a simple two-way data exchange over GATT. This can be tested with the EFR Connect mobile app.
@@ -582,11 +570,11 @@
-
+
-
+
The classic blinky example using Bluetooth communication. Demonstrates a simple two-way data exchange over GATT. This can be tested with the EFR Connect mobile app.
@@ -594,11 +582,11 @@
-
+
-
+
The classic blinky example using Bluetooth communication. Demonstrates a simple two-way data exchange over GATT. This can be tested with the EFR Connect mobile app.
@@ -606,11 +594,11 @@
-
+
-
+
The classic blinky example using Bluetooth communication. Demonstrates a simple two-way data exchange over GATT. This can be tested with the EFR Connect mobile app.
@@ -618,11 +606,11 @@
-
+
-
+
The classic blinky example using Bluetooth communication. Demonstrates a simple two-way data exchange over GATT. This can be tested with the EFR Connect mobile app.
@@ -630,11 +618,11 @@
-
+
-
+
The classic blinky example using Bluetooth communication. Demonstrates a simple two-way data exchange over GATT. This can be tested with the EFR Connect mobile app.
@@ -642,11 +630,11 @@
-
+
-
+
The classic blinky example using Bluetooth communication. Demonstrates a simple two-way data exchange over GATT. This can be tested with the EFR Connect mobile app.
@@ -654,11 +642,11 @@
-
+
-
+
The classic blinky example using Bluetooth communication. Demonstrates a simple two-way data exchange over GATT. This can be tested with the EFR Connect mobile app.
@@ -666,11 +654,11 @@
-
+
-
+
The classic blinky example using Bluetooth communication. Demonstrates a simple two-way data exchange over GATT. This can be tested with the EFR Connect mobile app.
@@ -678,11 +666,11 @@
-
+
-
+
The classic blinky example using Bluetooth communication. Demonstrates a simple two-way data exchange over GATT. This can be tested with the EFR Connect mobile app.
@@ -690,11 +678,11 @@
-
+
-
+
The classic blinky example using Bluetooth communication. Demonstrates a simple two-way data exchange over GATT. This can be tested with the EFR Connect mobile app.
@@ -702,11 +690,11 @@
-
+
-
+
The classic blinky example using Bluetooth communication. Demonstrates a simple two-way data exchange over GATT. This can be tested with the EFR Connect mobile app.
@@ -714,11 +702,11 @@
-
+
-
+
The classic blinky example using Bluetooth communication. Demonstrates a simple two-way data exchange over GATT. This can be tested with the EFR Connect mobile app.
@@ -726,11 +714,11 @@
-
+
-
+
The classic blinky example using Bluetooth communication. Demonstrates a simple two-way data exchange over GATT. This can be tested with the EFR Connect mobile app.
@@ -738,11 +726,11 @@
-
+
-
+
The classic blinky example using Bluetooth communication. Demonstrates a simple two-way data exchange over GATT. This can be tested with the EFR Connect mobile app.
@@ -750,11 +738,11 @@
-
+
-
+
The classic blinky example using Bluetooth communication. Demonstrates a simple two-way data exchange over GATT. This can be tested with the EFR Connect mobile app.
@@ -762,11 +750,11 @@
-
+
-
+
The classic blinky example using Bluetooth communication. Demonstrates a simple two-way data exchange over GATT. This can be tested with the EFR Connect mobile app.
@@ -774,11 +762,11 @@
-
+
-
+
The classic blinky example using Bluetooth communication. Demonstrates a simple two-way data exchange over GATT. This can be tested with the EFR Connect mobile app.
@@ -786,11 +774,11 @@
-
+
-
+
The classic blinky example using Bluetooth communication. Demonstrates a simple two-way data exchange over GATT. This can be tested with the EFR Connect mobile app.
@@ -798,11 +786,11 @@
-
+
-
+
The classic blinky example using Bluetooth communication. Demonstrates a simple two-way data exchange over GATT. This can be tested with the EFR Connect mobile app.
@@ -810,11 +798,11 @@
-
+
-
+
The classic blinky example using Bluetooth communication. Demonstrates a simple two-way data exchange over GATT. This can be tested with the EFR Connect mobile app.
@@ -822,11 +810,11 @@
-
+
-
+
The classic blinky example using Bluetooth communication. Demonstrates a simple two-way data exchange over GATT. This can be tested with the EFR Connect mobile app.
@@ -834,11 +822,11 @@
-
+
-
+
The classic blinky example using Bluetooth communication. Demonstrates a simple two-way data exchange over GATT. This can be tested with the EFR Connect mobile app.
@@ -846,11 +834,11 @@
-
+
-
+
The classic blinky example using Bluetooth communication. Demonstrates a simple two-way data exchange over GATT. This can be tested with the EFR Connect mobile app.
@@ -858,11 +846,11 @@
-
+
-
+
The classic blinky example using Bluetooth communication. Demonstrates a simple two-way data exchange over GATT. This can be tested with the EFR Connect mobile app.
@@ -870,11 +858,11 @@
-
+
-
+
Demonstrates the features of the EFR32xG24 Dev Kit Board. This can be tested with the EFR Connect mobile app.
@@ -882,11 +870,11 @@
-
+
-
+
Demonstrates the features of the EFR32xG27 DevKit board. Features can be evaluated with the EFR Connect mobile app.
@@ -894,11 +882,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the mock relative humidity and temperature sensor.
@@ -906,11 +894,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the mock relative humidity and temperature sensor.
@@ -918,11 +906,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the mock relative humidity and temperature sensor.
@@ -930,11 +918,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the mock relative humidity and temperature sensor.
@@ -942,11 +930,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the mock relative humidity and temperature sensor.
@@ -954,11 +942,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the mock relative humidity and temperature sensor.
@@ -966,11 +954,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the mock relative humidity and temperature sensor.
@@ -978,11 +966,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the mock relative humidity and temperature sensor.
@@ -990,11 +978,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the mock relative humidity and temperature sensor.
@@ -1002,11 +990,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the mock relative humidity and temperature sensor.
@@ -1014,11 +1002,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the mock relative humidity and temperature sensor.
@@ -1026,11 +1014,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the mock relative humidity and temperature sensor.
@@ -1038,11 +1026,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the mock relative humidity and temperature sensor.
@@ -1050,11 +1038,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the Si7021 digital relative humidity and temperature sensor of the WSTK or of the Thunderboard.
@@ -1062,11 +1050,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the Si7021 digital relative humidity and temperature sensor of the WSTK or of the Thunderboard.
@@ -1074,11 +1062,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the Si7021 digital relative humidity and temperature sensor of the WSTK or of the Thunderboard.
@@ -1086,11 +1074,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the Si7021 digital relative humidity and temperature sensor of the WSTK or of the Thunderboard.
@@ -1098,11 +1086,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the Si7021 digital relative humidity and temperature sensor of the WSTK or of the Thunderboard.
@@ -1110,11 +1098,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the Si7021 digital relative humidity and temperature sensor of the WSTK or of the Thunderboard.
@@ -1122,11 +1110,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the Si7021 digital relative humidity and temperature sensor of the WSTK or of the Thunderboard.
@@ -1134,11 +1122,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the Si7021 digital relative humidity and temperature sensor of the WSTK or of the Thunderboard.
@@ -1146,11 +1134,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the Si7021 digital relative humidity and temperature sensor of the WSTK or of the Thunderboard.
@@ -1158,11 +1146,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the Si7021 digital relative humidity and temperature sensor of the WSTK or of the Thunderboard.
@@ -1170,11 +1158,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the Si7021 digital relative humidity and temperature sensor of the WSTK or of the Thunderboard.
@@ -1182,11 +1170,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the Si7021 digital relative humidity and temperature sensor of the WSTK or of the Thunderboard.
@@ -1194,11 +1182,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the Si7021 digital relative humidity and temperature sensor of the WSTK or of the Thunderboard.
@@ -1206,11 +1194,11 @@
-
+
-
+
Implements a GATT Server with the Health Thermometer Profile, which enables a Client device to connect and get temperature data. Temperature is read from the Si7021 digital relative humidity and temperature sensor of the WSTK or of the Thunderboard.
@@ -1218,11 +1206,11 @@
-
+
-
+
This example tests the throughput capabilities of the device and can be used to measure throughput between 2 *EFR32* devices, as well as between a device and a smartphone using EFR Connect mobile app, through the Throughput demo tile.
@@ -1230,11 +1218,11 @@
-
+
-
+
This example tests the throughput capabilities of the device and can be used to measure throughput between 2 *EFR32* devices, as well as between a device and a smartphone using EFR Connect mobile app, through the Throughput demo tile.
@@ -1242,11 +1230,11 @@
-
+
-
+
This example tests the throughput capabilities of the device and can be used to measure throughput between 2 *EFR32* devices, as well as between a device and a smartphone using EFR Connect mobile app, through the Throughput demo tile.
@@ -1254,11 +1242,11 @@
-
+
-
+
This example tests the throughput capabilities of the device and can be used to measure throughput between 2 *EFR32* devices, as well as between a device and a smartphone using EFR Connect mobile app, through the Throughput demo tile.
@@ -1266,11 +1254,11 @@
-
+
-
+
This example tests the throughput capabilities of the device and can be used to measure throughput between 2 *EFR32* devices, as well as between a device and a smartphone using EFR Connect mobile app, through the Throughput demo tile.
@@ -1278,11 +1266,11 @@
-
+
-
+
This example tests the throughput capabilities of the device and can be used to measure throughput between 2 *EFR32* devices, as well as between a device and a smartphone using EFR Connect mobile app, through the Throughput demo tile.
@@ -1290,11 +1278,11 @@
-
+
-
+
This example tests the throughput capabilities of the device and can be used to measure throughput between 2 *EFR32* devices, as well as between a device and a smartphone using EFR Connect mobile app, through the Throughput demo tile.
@@ -1302,11 +1290,11 @@
-
+
-
+
This example tests the throughput capabilities of the device and can be used to measure throughput between 2 *EFR32* devices, as well as between a device and a smartphone using EFR Connect mobile app, through the Throughput demo tile.
@@ -1314,11 +1302,11 @@
-
+
-
+
This example tests the throughput capabilities of the device and can be used to measure throughput between 2 *EFR32* devices, as well as between a device and a smartphone using EFR Connect mobile app, through the Throughput demo tile.
@@ -1326,11 +1314,11 @@
-
+
-
+
This example tests the throughput capabilities of the device and can be used to measure throughput between 2 *EFR32* devices, as well as between a device and a smartphone using EFR Connect mobile app, through the Throughput demo tile.
@@ -1338,11 +1326,11 @@
-
+
-
+
This example tests the throughput capabilities of the device and can be used to measure throughput between 2 *EFR32* devices, as well as between a device and a smartphone using EFR Connect mobile app, through the Throughput demo tile.
@@ -1350,11 +1338,11 @@
-
+
-
+
This example tests the throughput capabilities of the device and can be used to measure throughput between 2 *EFR32* devices, as well as between a device and a smartphone using EFR Connect mobile app, through the Throughput demo tile.
@@ -1362,11 +1350,11 @@
-
+
-
+
This example tests the throughput capabilities of the device and can be used to measure throughput between 2 *EFR32* devices, as well as between a device and a smartphone using EFR Connect mobile app, through the Throughput demo tile.
@@ -1374,11 +1362,11 @@
-
+
-
+
This example tests the throughput capabilities of the device and can be used to measure throughput between 2 *EFR32* devices, as well as between a device and a smartphone using EFR Connect mobile app, through the Throughput demo tile.
@@ -1386,11 +1374,11 @@
-
+
-
+
This example tests the throughput capabilities of the device and can be used to measure throughput between 2 *EFR32* devices, as well as between a device and a smartphone using EFR Connect mobile app, through the Throughput demo tile.
@@ -1398,11 +1386,11 @@
-
+
-
+
This example tests the throughput capabilities of the device and can be used to measure throughput between 2 *EFR32* devices, as well as between a device and a smartphone using EFR Connect mobile app, through the Throughput demo tile.
@@ -1410,11 +1398,11 @@
-
+
-
+
This example tests the throughput capabilities of the device and can be used to measure throughput between 2 *EFR32* devices, as well as between a device and a smartphone using EFR Connect mobile app, through the Throughput demo tile.
@@ -1422,11 +1410,11 @@
-
+
-
+
This example tests the throughput capabilities of the device and can be used to measure throughput between 2 *EFR32* devices, as well as between a device and a smartphone using EFR Connect mobile app, through the Throughput demo tile.
@@ -1434,11 +1422,11 @@
-
+
-
+
This example tests the throughput capabilities of the device and can be used to measure throughput between 2 *EFR32* devices, as well as between a device and a smartphone using EFR Connect mobile app, through the Throughput demo tile.
@@ -1446,11 +1434,11 @@
-
+
-
+
This example tests the throughput capabilities of the device and can be used to measure throughput between 2 *EFR32* devices, as well as between a device and a smartphone using EFR Connect mobile app, through the Throughput demo tile.
@@ -1458,11 +1446,11 @@
-
+
-
+
This example tests the throughput capabilities of the device and can be used to measure throughput between 2 *EFR32* devices, as well as between a device and a smartphone using EFR Connect mobile app, through the Throughput demo tile.
@@ -1470,11 +1458,11 @@
-
+
-
+
This example tests the throughput capabilities of the device and can be used to measure throughput between 2 *EFR32* devices, as well as between a device and a smartphone using EFR Connect mobile app, through the Throughput demo tile.
@@ -1482,11 +1470,11 @@
-
+
-
+
This example tests the throughput capabilities of the device and can be used to measure throughput between 2 *EFR32* devices, as well as between a device and a smartphone using EFR Connect mobile app, through the Throughput demo tile.
@@ -1494,11 +1482,11 @@
-
+
-
+
This example tests the throughput capabilities of the device and can be used to measure throughput between 2 *EFR32* devices, as well as between a device and a smartphone using EFR Connect mobile app, through the Throughput demo tile.
@@ -1506,11 +1494,11 @@
-
+
-
+
This example tests the throughput capabilities of the device and can be used to measure throughput between 2 *EFR32* devices, as well as between a device and a smartphone using EFR Connect mobile app, through the Throughput demo tile.
@@ -1518,11 +1506,11 @@
-
+
-
+
Demonstrates the features of the Thunderboard EFR32BG22 Kit. This can be tested with the EFR Connect mobile app.
@@ -1530,11 +1518,11 @@
-
+
-
+
Demonstrates the features of the Thunderboard EFR32BG22 Kit. This can be tested with the EFR Connect mobile app.
@@ -1542,11 +1530,11 @@
-
+
-
+
Demonstrates the features of the Thunderboard Sense 2 Kit. This can be tested with the EFR Connect mobile app.
@@ -1554,11 +1542,11 @@
-
+
-
+
Voice over Bluetooth Low Energy sample application. It is supported by Thunderboard Sense 2 and Thunderboard EFR32BG22 boards and demonstrates how to send voice data over GATT, which is acquired from the on-board microphones.
@@ -1566,11 +1554,11 @@
-
+
-
+
Voice over Bluetooth Low Energy sample application. It is supported by Thunderboard Sense 2 and Thunderboard EFR32BG22 boards and demonstrates how to send voice data over GATT, which is acquired from the on-board microphones.
@@ -1578,11 +1566,11 @@
-
+
-
+
Voice over Bluetooth Low Energy sample application. It is supported by Thunderboard Sense 2 and Thunderboard EFR32BG22 boards and demonstrates how to send voice data over GATT, which is acquired from the on-board microphones.
@@ -1590,11 +1578,11 @@
-
+
-
+
Sends non-connectable advertisements in iBeacon format. The iBeacon Service gives Bluetooth accessories a simple and convenient way to send iBeacons to smartphones. This example can be tested together with the EFR Connect mobile app.
@@ -1602,7 +1590,7 @@
-
+
@@ -1613,7 +1601,51 @@
-
+
+
+
+
+
+
+ Sends non-connectable advertisements in iBeacon format. The iBeacon Service gives Bluetooth accessories a simple and convenient way to send iBeacons to smartphones. This example can be tested together with the EFR Connect mobile app.
+
+
+
+
+
+
+
+
+
+
+ Sends non-connectable advertisements in iBeacon format. The iBeacon Service gives Bluetooth accessories a simple and convenient way to send iBeacons to smartphones. This example can be tested together with the EFR Connect mobile app.
+
+
+
+
+
+
+
+
+
+
+ Sends non-connectable advertisements in iBeacon format. The iBeacon Service gives Bluetooth accessories a simple and convenient way to send iBeacons to smartphones. This example can be tested together with the EFR Connect mobile app.
+
+
+
+
+
+
+
+
+
+
+ Sends non-connectable advertisements in iBeacon format. The iBeacon Service gives Bluetooth accessories a simple and convenient way to send iBeacons to smartphones. This example can be tested together with the EFR Connect mobile app.
+
+
+
+
+
@@ -1624,7 +1656,7 @@
-
+
@@ -1635,7 +1667,7 @@
-
+
@@ -1646,7 +1678,7 @@
-
+
@@ -1657,7 +1689,7 @@
-
+
@@ -1668,7 +1700,7 @@
-
+
@@ -1679,7 +1711,7 @@
-
+
@@ -1690,7 +1722,7 @@
-
+
@@ -1701,7 +1733,7 @@
-
+
@@ -1712,7 +1744,7 @@
-
+
@@ -1723,7 +1755,7 @@
-
+
@@ -1734,7 +1766,7 @@
-
+
@@ -1745,7 +1777,7 @@
-
+
@@ -1756,7 +1788,7 @@
-
+
@@ -1767,7 +1799,7 @@
-
+
@@ -1778,7 +1810,7 @@
-
+
@@ -1789,7 +1821,7 @@
-
+
@@ -1800,7 +1832,7 @@
-
+
@@ -1811,7 +1843,7 @@
-
+
@@ -1822,7 +1854,7 @@
-
+
@@ -1833,7 +1865,7 @@
-
+
@@ -1844,7 +1876,7 @@
-
+
@@ -1855,7 +1887,7 @@
-
+
@@ -1866,7 +1898,7 @@
-
+
@@ -1877,7 +1909,7 @@
-
+
@@ -1888,7 +1920,7 @@
-
+
@@ -1899,7 +1931,7 @@
-
+
@@ -1910,7 +1942,7 @@
-
+
@@ -1921,11 +1953,11 @@
-
+
-
+
Network Co-Processor (NCP) target application extended with CTE Receiver support. It enables Angle of Arrival (AoA) calculation. Use this application with Direction Finding host examples.
@@ -1933,11 +1965,11 @@
-
+
-
+
This sample app demonstrates a CTE (Constant Tone Extension) transmitter that can be used as an asset tag in a Direction Finding setup estimating Angle of Arrival (AoA).
@@ -1945,11 +1977,11 @@
-
+
-
+
This sample app demonstrates a CTE (Constant Tone Extension) transmitter that can be used as an asset tag in a Direction Finding setup estimating Angle of Arrival (AoA).
@@ -1957,11 +1989,11 @@
-
+
-
+
This sample app demonstrates a CTE (Constant Tone Extension) transmitter that can be used as an asset tag in a Direction Finding setup estimating Angle of Arrival (AoA).
@@ -1969,11 +2001,11 @@
-
+
-
+
Network Co-Processor (NCP) target application extended with CTE Receiver support. It enables Angle of Departure (AoD) calculation. Use this application with Direction Finding Studio tools.
@@ -1981,11 +2013,11 @@
-
+
-
+
Network Co-Processor (NCP) target application extended with CTE Receiver support. It enables Angle of Departure (AoD) calculation. Use this application with Direction Finding Studio tools.
@@ -1993,11 +2025,11 @@
-
+
-
+
Network Co-Processor (NCP) target application extended with CTE Receiver support. It enables Angle of Departure (AoD) calculation. Use this application with Direction Finding Studio tools.
@@ -2005,11 +2037,11 @@
-
+
-
+
This sample app demonstrates a CTE (Constant Tone Extension) transmitter that can be used as a locator beacon in a Direction Finding setup estimating Angle of Departure (AoD).
@@ -2017,11 +2049,11 @@
-
+
-
+
This sample app demonstrates a CTE (Constant Tone Extension) transmitter that can be used as a locator beacon in a Direction Finding setup estimating Angle of Departure (AoD).
@@ -2029,11 +2061,11 @@
-
+
-
+
This is a Dynamic Multiprotocol reference application demonstrating a light bulb that can be switched both via Bluetooth and via a Proprietary protocol. Can be tested with the EFR Connect mobile app and Flex (RAIL) Switch sample app.
@@ -2041,10 +2073,10 @@
-
+
-
+
diff --git a/app/bluetooth/bluetooth_production_templates.xml b/app/bluetooth/bluetooth_production_templates.xml
index d122940a58a..12e2a484c33 100644
--- a/app/bluetooth/bluetooth_production_templates.xml
+++ b/app/bluetooth/bluetooth_production_templates.xml
@@ -8,7 +8,7 @@
-
+
@@ -21,9 +21,9 @@
-
+
-
+
@@ -36,9 +36,9 @@
-
+
-
+
@@ -53,7 +53,7 @@
-
+
@@ -66,9 +66,9 @@
-
+
-
+
@@ -81,9 +81,9 @@
-
-
-
+
+
+
@@ -98,7 +98,7 @@
-
+
@@ -111,9 +111,9 @@
-
+
-
+
@@ -128,7 +128,7 @@
-
+
@@ -143,7 +143,7 @@
-
+
@@ -158,7 +158,7 @@
-
+
@@ -173,7 +173,7 @@
-
+
@@ -188,7 +188,7 @@
-
+
@@ -203,7 +203,7 @@
-
+
@@ -218,7 +218,7 @@
-
+
@@ -233,7 +233,7 @@
-
+
@@ -246,9 +246,9 @@
-
+
-
+
@@ -261,9 +261,9 @@
-
+
-
+
@@ -278,7 +278,7 @@
-
+
@@ -293,7 +293,7 @@
-
+
@@ -308,7 +308,7 @@
-
+
@@ -321,9 +321,9 @@
-
+
-
+
@@ -338,7 +338,7 @@
-
+
@@ -351,9 +351,9 @@
-
+
-
+
@@ -366,9 +366,9 @@
-
+
-
+
@@ -381,9 +381,9 @@
-
-
-
+
+
+
@@ -396,9 +396,9 @@
-
+
-
+
@@ -411,9 +411,9 @@
-
+
-
+
@@ -426,9 +426,9 @@
-
+
-
+
@@ -441,9 +441,9 @@
-
+
-
+
@@ -456,9 +456,9 @@
-
+
-
+
@@ -471,9 +471,9 @@
-
+
-
+
@@ -486,9 +486,9 @@
-
+
-
+
@@ -501,9 +501,9 @@
-
+
-
+
@@ -516,9 +516,9 @@
-
+
-
+
@@ -531,9 +531,9 @@
-
+
-
+
@@ -546,9 +546,9 @@
-
+
-
+
@@ -563,7 +563,7 @@
-
+
@@ -576,9 +576,9 @@
-
+
-
+
@@ -593,7 +593,7 @@
-
+
@@ -608,7 +608,7 @@
-
+
@@ -623,7 +623,7 @@
-
+
@@ -638,7 +638,7 @@
-
+
@@ -653,7 +653,7 @@
-
+
@@ -668,7 +668,7 @@
-
+
diff --git a/app/bluetooth/common/cbap_lib/lib/cbap_CM33_gcc.a b/app/bluetooth/common/cbap_lib/lib/cbap_CM33_gcc.a
index 8f401a3931d..3e1d0bd24a3 100644
--- a/app/bluetooth/common/cbap_lib/lib/cbap_CM33_gcc.a
+++ b/app/bluetooth/common/cbap_lib/lib/cbap_CM33_gcc.a
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:69e8f57ae14405f4a6b59f3093977336230f1f4fa236dc7f772e649e54ddd4f1
+oid sha256:75a34f80eb418a94ccf93098e1df79358960ee76bfce1bac7f09da7575976562
size 5322
diff --git a/app/bluetooth/common/cbap_lib/lib/cbap_CM33_iar.a b/app/bluetooth/common/cbap_lib/lib/cbap_CM33_iar.a
index 4fc12d913f6..e18455a4c19 100644
--- a/app/bluetooth/common/cbap_lib/lib/cbap_CM33_iar.a
+++ b/app/bluetooth/common/cbap_lib/lib/cbap_CM33_iar.a
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:188f152be66e33c56f37acfacafd3e245638597a85c1ee335a60e1e80eed0b06
+oid sha256:7921d5fcead0ec8a6188ec6db88bac262522671e20db812ad08633d182508526
size 10542
diff --git a/app/bluetooth/common/ead_core/sl_bt_ead_core.c b/app/bluetooth/common/ead_core/sl_bt_ead_core.c
index ddce442ca35..b1d31c8596f 100644
--- a/app/bluetooth/common/ead_core/sl_bt_ead_core.c
+++ b/app/bluetooth/common/ead_core/sl_bt_ead_core.c
@@ -101,30 +101,33 @@ sl_status_t sl_bt_ead_session_init(sl_bt_ead_key_material_p key_material,
uint8_t tmp[SL_BT_EAD_SESSION_KEY_SIZE];
sl_status_t result = SL_STATUS_INITIALIZATION;
- assert(key_material != NULL);
+ if (key_material != NULL) {
+ // Make a copy of the session key, first
+ memcpy(tmp, key_material->key, SL_BT_EAD_SESSION_KEY_SIZE);
- // Make a copy of the session key, first
- memcpy(tmp, key_material->key, SL_BT_EAD_SESSION_KEY_SIZE);
+ // Swap key endianness to make the sli_ccm_[crypto] functions work as expected
+ for (unsigned int t = 0; t < SL_BT_EAD_SESSION_KEY_SIZE; t++) {
+ key_material->key[(SL_BT_EAD_SESSION_KEY_SIZE - 1) - t] = tmp[t];
+ }
- // Swap key endianness to make the sli_ccm_[crypto] functions work as expected
- for (unsigned int t = 0; t < SL_BT_EAD_SESSION_KEY_SIZE; t++) {
- key_material->key[(SL_BT_EAD_SESSION_KEY_SIZE - 1) - t] = tmp[t];
- }
+ if (nonce != NULL) {
+ memcpy((void *)nonce->iv,
+ (const void *)key_material->iv,
+ SL_BT_EAD_IV_SIZE);
- if (nonce != NULL) {
- memcpy((void *)nonce->iv,
- (const void *)key_material->iv,
- SL_BT_EAD_IV_SIZE);
+ result = sl_bt_ead_randomizer_set(randomizer, nonce);
+ } else {
+ result = SL_STATUS_OK;
+ }
- result = sl_bt_ead_randomizer_set(randomizer, nonce);
+ #if (SL_BT_EAD_CORE_ACCELERATOR == SL_BT_EAD_CORE_USE_PSA_ACC)
+ // Prepare key attributes
+ psa_set_key_algorithm(&attributes, PSA_ALG_BLE_CCM);
+ psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
+ psa_set_key_bits(&attributes, SL_BT_EAD_SESSION_KEY_SIZE * 8);
+ #endif // (SL_BT_EAD_CORE_ACCELERATOR == SL_BT_EAD_CORE_USE_PSA_ACC)
}
- #if (SL_BT_EAD_CORE_ACCELERATOR == SL_BT_EAD_CORE_USE_PSA_ACC)
- // Prepare key attributes
- psa_set_key_algorithm(&attributes, PSA_ALG_BLE_CCM);
- psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
- psa_set_key_bits(&attributes, SL_BT_EAD_SESSION_KEY_SIZE * 8);
- #endif // (SL_BT_EAD_CORE_ACCELERATOR == SL_BT_EAD_CORE_USE_PSA_ACC)
return result;
}
diff --git a/app/bluetooth/common/ead_core/sl_bt_ead_core.h b/app/bluetooth/common/ead_core/sl_bt_ead_core.h
index b4c8a815da4..781ab39af2b 100644
--- a/app/bluetooth/common/ead_core/sl_bt_ead_core.h
+++ b/app/bluetooth/common/ead_core/sl_bt_ead_core.h
@@ -151,7 +151,7 @@ sl_status_t sl_bt_ead_randomizer_set(sl_bt_ead_randomizer_t randomizer,
/**************************************************************************//**
* (Re)initialize the entire Nonce value with the new key material provided
- * @note According to the Supplement to the Bluetooth Core Specification (d11)
+ * @note According to the Supplement to the Bluetooth Core Specification v11
* Part A, Section 1.23.3: the session key shall be set to a value
* determined by a higher layer specification or otherwise negotiated
* between the devices that are sending and receiving the encrypted AD
@@ -163,11 +163,12 @@ sl_status_t sl_bt_ead_randomizer_set(sl_bt_ead_randomizer_t randomizer,
* @param[in] randomizer - Pointer to the desired Randomizer value type or
* NULL. The Nonce will get a new random value during
* the invocation if NULL is passed.
- * @param[out] nonce - Pointer to the complete EAD Nonce structure. Can be
- * also omitted by passing NULL, in which case only the
- * session key will be prepared. This is useful for in-place
- * decryption with @ref sl_bt_ead_unpack_decrypt(), and not
- * meant to be used this way, otherwise.
+ * @param[out] nonce - Pointer to the complete EAD Nonce structure. This can be
+ * omitted by advanced users by passing it as NULL, in which
+ * case only the session key is prepared. Although passing
+ * the nonce is the recommended use case, omitting it can
+ * still be useful for efficient in-place decryption when
+ * used with @ref sl_bt_ead_unpack_decrypt().
* @return sl_status_t
*****************************************************************************/
sl_status_t sl_bt_ead_session_init(sl_bt_ead_key_material_p key_material,
diff --git a/app/bluetooth/common/esl_tag_core/inc/esl_tag_core.h b/app/bluetooth/common/esl_tag_core/inc/esl_tag_core.h
index eba847c00fa..494b5e1a4a0 100644
--- a/app/bluetooth/common/esl_tag_core/inc/esl_tag_core.h
+++ b/app/bluetooth/common/esl_tag_core/inc/esl_tag_core.h
@@ -145,19 +145,6 @@ typedef void(*esl_void_func_p)(void);
esl_core_get_group_id(addr_word) ((esl_group_id_t) \
((addr_word & ESL_GROUP_ID_MASK) >> 8))
-/**************************************************************************//**
- * ESL Tag core init function. Initializes additional components also, if any.
- *****************************************************************************/
-void esl_core_init(void);
-
-/**************************************************************************//**
- * ESL core's bluetooth stack event handler.
- * This one runs by the user implementation (usually in app.c) in parallel.
- *
- * @param[in] evt Event coming from the Bluetooth stack.
- *****************************************************************************/
-void esl_core_bt_on_event(sl_bt_msg_t *evt);
-
/**************************************************************************//**
* ESL Tag internal status getter. Can be used to check whether an action is
* allowed to process or not.
diff --git a/app/bluetooth/common/esl_tag_core/inc/esl_tag_internal.h b/app/bluetooth/common/esl_tag_core/inc/esl_tag_internal.h
index 8f1fdbc74f1..cf4204382fa 100644
--- a/app/bluetooth/common/esl_tag_core/inc/esl_tag_internal.h
+++ b/app/bluetooth/common/esl_tag_core/inc/esl_tag_internal.h
@@ -1,4 +1,4 @@
-/***************************************************************************//**
+/*******************************************************************************
* @file
* @brief ESL Tag core interface declarations for internal use, only.
*******************************************************************************
@@ -30,15 +30,16 @@
#ifndef ESL_TAG_INTERNAL_H
#define ESL_TAG_INTERNAL_H
-/**************************************************************************//**
+/******************************************************************************
* @addtogroup esl_tag_core
* @{
*****************************************************************************/
#include
#include "sl_status.h"
+#include "sl_bt_api.h"
#include "esl_tag_display_core.h"
-/**************************************************************************//**
+/******************************************************************************
* Get a display width based on display index.
* @param[in] display_index Selects the display to get width of [pixels].
* @param[out] width Returns width if given display exists.
@@ -48,7 +49,7 @@
extern sl_status_t esl_display_get_width(uint8_t display_index,
uint16_t *width);
-/**************************************************************************//**
+/******************************************************************************
* Get a display height based on display index.
* @param[in] display_index Selects the display to get height of [pixels].
* @param[out] height Returns height if given display exists.
@@ -58,7 +59,7 @@ extern sl_status_t esl_display_get_width(uint8_t display_index,
extern sl_status_t esl_display_get_height(uint8_t display_index,
uint16_t *height);
-/**************************************************************************//**
+/******************************************************************************
* Get a display type based on display index.
* @param[in] display_index The display to get the type of [Assigned Number].
* @param[out] type Returns type if given display exists.
@@ -68,7 +69,7 @@ extern sl_status_t esl_display_get_height(uint8_t display_index,
extern sl_status_t esl_display_get_type(uint8_t display_index,
esl_display_type_t *type);
-/**************************************************************************//**
+/******************************************************************************
* Select an image storage OTS object.
* @param[in] data Pointer to ESL OTS object ID 48 bit value in little endian.
* @param[in] length Must be sizeof(esl_image_object_id_t)
@@ -78,7 +79,7 @@ extern sl_status_t esl_display_get_type(uint8_t display_index,
extern sl_status_t esl_image_select_object(void const *data,
uint16_t length);
-/**************************************************************************//**
+/******************************************************************************
* Callback on image chunk received event.
* @param[in] data Pointer to image chunk data.
* @param[in] offset Start offset of received chunk.
@@ -92,26 +93,26 @@ extern sl_status_t esl_image_chunk_received(uint8_t const *data,
uint32_t offset,
uint16_t length);
-/**************************************************************************//**
+/******************************************************************************
* Request ESL to return the Synchronized state from a connection if it's
* already synchronized to the AP's PAwR train.
* @note: Defined in esl_core but not exposed to the public ESL API
*****************************************************************************/
extern sl_status_t esl_core_update_complete(void);
-/**************************************************************************//**
+/******************************************************************************
* Start ESL service advertisement.
* @note: Defined in esl_core but not exposed to the public ESL API
*****************************************************************************/
extern sl_status_t esl_core_start_advertising(void);
-/**************************************************************************//**
+/******************************************************************************
* Purge any pending responses that aren't sent yet.
* @note: Defined in esl_tag_response.c but not exposed to the public ESL API
*****************************************************************************/
extern void esl_core_purge_responses(void);
-/**************************************************************************//**
+/******************************************************************************
* Re-schedule pending commands if there're any.
* @param[in] uint32_t current_absolute_time New ESL Current Absolute Time value
after the clock drift adjustment.
@@ -120,7 +121,7 @@ extern void esl_core_purge_responses(void);
*****************************************************************************/
extern void esl_reschedule_delayed_commands(uint32_t current_absolute_time);
-/**************************************************************************//**
+/******************************************************************************
* Get sync handle.
* @return The PAwR sync hanle or @ref SL_BT_INVALID_SYNC_HANDLE if ESL is
* out of sync.
@@ -129,7 +130,7 @@ extern void esl_reschedule_delayed_commands(uint32_t current_absolute_time);
*****************************************************************************/
extern uint16_t esl_core_get_sync_handle(void);
-/**************************************************************************//**
+/******************************************************************************
* Get request event value for PAwR response.
* @return The most recent PAwR sync request event value.
* @note: Called by esl core internally on opcodes parsing.
@@ -137,7 +138,7 @@ extern uint16_t esl_core_get_sync_handle(void);
*****************************************************************************/
extern uint16_t esl_core_get_request_event(void);
-/**************************************************************************//**
+/******************************************************************************
* Get request subevent value for PAwR response.
* @return The most recent PAwR sync request subevent value.
* @note: Called by esl core internally on opcodes parsing.
@@ -145,12 +146,30 @@ extern uint16_t esl_core_get_request_event(void);
*****************************************************************************/
extern uint8_t esl_core_get_request_subevent(void);
-/**************************************************************************//**
+/******************************************************************************
* Invalidate entire ESL configuration to prevent later opcode processing.
* @note: Called by esl core internally on factory reset opcode execution.
* The function is defined in esl_tag_core.c
*****************************************************************************/
extern void esl_core_invalidate_config(void);
+/******************************************************************************
+ * ESL Tag core init function. Initializes additional components also, if any.
+ *****************************************************************************/
+extern void esl_core_init(void);
+
+/******************************************************************************
+ * ESL core's bluetooth stack event handler.
+ * This one runs by the user implementation (usually in app.c) in parallel.
+ *
+ * @param[in] evt Event coming from the Bluetooth stack.
+ *****************************************************************************/
+extern void esl_core_bt_on_event(sl_bt_msg_t *evt);
+
+/******************************************************************************
+ * Cyclic process of ESL Tag Core.
+ *****************************************************************************/
+extern void esl_core_step(void);
+
/** @} (end addtogroup esl_tag_core) */
#endif // ESL_TAG_INTERNAL_H
diff --git a/app/bluetooth/common/esl_tag_core/src/esl_tag_core.c b/app/bluetooth/common/esl_tag_core/src/esl_tag_core.c
index 2655cfb8b7d..5370e93c4c2 100644
--- a/app/bluetooth/common/esl_tag_core/src/esl_tag_core.c
+++ b/app/bluetooth/common/esl_tag_core/src/esl_tag_core.c
@@ -31,6 +31,7 @@
#include
#include
#include
+#include
#include "em_core.h"
#include "em_emu.h"
#include "gatt_db.h"
@@ -147,6 +148,8 @@ typedef struct {
typedef struct {
// Security timeout task handle
sl_sleeptimer_timer_handle_t watchdog_handle;
+ // Security timeout trigger flag
+ bool watchdog_triggered;
// PAwR evt_pawr_sync_subevent_report.event_counter backup
uint16_t request_event;
// PAwR evt_pawr_sync_subevent_report.subevent field backup
@@ -191,6 +194,7 @@ static esl_struct_t esl_tag = {
static esl_persistent_struct_t esl_tag_persistent = {
.watchdog_handle = { 0 },
+ .watchdog_triggered = false,
.request_event = 0,
.request_subevent = 0,
.advertising_set_handle = SL_BT_INVALID_ADVERTISING_SET_HANDLE
@@ -269,43 +273,50 @@ void esl_security_timeout(sl_sleeptimer_timer_handle_t *timer, void *data)
// suppress compiler warnings
(void)timer;
(void)data;
+ esl_tag_persistent.watchdog_triggered = true;
+}
- if (esl_state_unsynchronized == esl_tag.status) {
- // ESLS d095r13 2.7.3.5 Unsynchronized state watchdog requirement
- esl_core_unassociate();
- sl_bt_esl_log(ESL_LOG_COMPONENT_CORE,
- ESL_LOG_LEVEL_INFO,
- "Unassociated by watchdog timeout.");
- } else if (esl_state_unassociated == esl_tag.status) {
- // shutdown after one or two hours of advertising in vain
- sl_bt_esl_log(ESL_LOG_COMPONENT_CORE,
- ESL_LOG_LEVEL_INFO,
- "Shutdown by watchdog timeout.");
- (void)sl_sleeptimer_stop_timer(&esl_tag_persistent.watchdog_handle);
- EMU_EM4Init_TypeDef em4init;
+void esl_core_step(void)
+{
+ if (esl_tag_persistent.watchdog_triggered) {
+ if (esl_state_unsynchronized == esl_tag.status) {
+ // ESLS d095r13 2.7.3.5 Unsynchronized state watchdog requirement
+ esl_core_unassociate();
+ sl_bt_esl_log(ESL_LOG_COMPONENT_CORE,
+ ESL_LOG_LEVEL_INFO,
+ "Unassociated by watchdog timeout.");
+ } else if (esl_state_unassociated == esl_tag.status) {
+ // shutdown after one or two hours of advertising in vain
+ sl_bt_esl_log(ESL_LOG_COMPONENT_CORE,
+ ESL_LOG_LEVEL_INFO,
+ "Shutdown by watchdog timeout.");
+ (void)sl_sleeptimer_stop_timer(&esl_tag_persistent.watchdog_handle);
+ EMU_EM4Init_TypeDef em4init;
- esl_core_shutdown_hook();
+ esl_core_shutdown_hook();
- em4init.em4State = emuEM4Shutoff;
- em4init.retainLfxo = false;
- em4init.retainLfrco = false;
- em4init.retainUlfrco = false;
- em4init.pinRetentionMode = emuPinRetentionDisable;
+ em4init.em4State = emuEM4Shutoff;
+ em4init.retainLfxo = false;
+ em4init.retainLfrco = false;
+ em4init.retainUlfrco = false;
+ em4init.pinRetentionMode = emuPinRetentionDisable;
- EMU_EM4Init(&em4init);
- CORE_CRITICAL_SECTION(
+ EMU_EM4Init(&em4init);
+ CORE_CRITICAL_SECTION(
#if defined(_SILICON_LABS_32B_SERIES_2)
- GPIO_IntClear(GPIO_IntGet());
+ GPIO_IntClear(GPIO_IntGet());
#endif // defined(_SILICON_LABS_32B_SERIES_2)
- EMU_EnterEM4S();
- );
- NVIC_SystemReset(); // just in case, we should never get here, anyway.
- } else if (esl_tag.sync_handle != SL_BT_INVALID_SYNC_HANDLE) {
- // implement ESLS d095r13 2.7.3.3 Synchronized state watchdog requirement
- sl_bt_sync_close(esl_tag.sync_handle);
- sl_bt_esl_log(ESL_LOG_COMPONENT_CORE,
- ESL_LOG_LEVEL_INFO,
- "Unsynchronized by watchdog timeout.");
+ EMU_EnterEM4S();
+ );
+ NVIC_SystemReset(); // just in case, we should never get here, anyway.
+ } else if (esl_tag.sync_handle != SL_BT_INVALID_SYNC_HANDLE) {
+ // implement ESLS d095r13 2.7.3.3 Synchronized state watchdog requirement
+ sl_bt_sync_close(esl_tag.sync_handle);
+ sl_bt_esl_log(ESL_LOG_COMPONENT_CORE,
+ ESL_LOG_LEVEL_INFO,
+ "Unsynchronized by watchdog timeout.");
+ }
+ esl_tag_persistent.watchdog_triggered = false;
}
}
@@ -565,6 +576,7 @@ static void esl_state_connectable_handler(sl_bt_msg_t *evt)
// force-stop security watchdog if running
(void)sl_sleeptimer_stop_timer(&esl_tag_persistent.watchdog_handle);
+ esl_tag_persistent.watchdog_triggered = false;
break;
case sl_bt_evt_connection_closed_id:
@@ -901,8 +913,7 @@ static void esl_state_configuring_handler(sl_bt_msg_t *evt)
} else {
// set synchronized state if sync exists and Update Complete received
if (esl_tag.sync_handle != SL_BT_INVALID_SYNC_HANDLE
- && is_esl_configured_for(ESL_CONFIG_FLAG_UPDATE_COMPLETE)
- && evt->data.evt_connection_closed.reason == SL_STATUS_BT_CTRL_CONNECTION_TERMINATED_BY_LOCAL_HOST) {
+ && is_esl_configured_for(ESL_CONFIG_FLAG_UPDATE_COMPLETE)) {
esl_tag.status = esl_state_synchronized;
sl_bt_esl_log(ESL_LOG_COMPONENT_CORE,
ESL_LOG_LEVEL_INFO,
diff --git a/app/bluetooth/common/esl_tag_core/src/esl_tag_opcodes.c b/app/bluetooth/common/esl_tag_core/src/esl_tag_opcodes.c
index 567b125ffc3..141755e1ea7 100644
--- a/app/bluetooth/common/esl_tag_core/src/esl_tag_opcodes.c
+++ b/app/bluetooth/common/esl_tag_core/src/esl_tag_opcodes.c
@@ -775,12 +775,10 @@ sl_status_t esl_core_process_opcode(esl_id_t self_id,
break;
case ESL_TLV_OPCODE_UNASSOCIATE: {
- esl_basic_state_t basic_state = esl_core_get_basic_state();
-
// remove any pending delayed command immediately
esl_core_purge_delayed_commands();
- // Close the connection, if opened
+ // close the connection, if opened
(void)app_scheduler_add(&esl_core_async_disconnect, 0, 0, NULL);
result = app_scheduler_add_delayed(&esl_delayed_unassociate,
@@ -788,9 +786,16 @@ sl_status_t esl_core_process_opcode(esl_id_t self_id,
NULL, 0, NULL);
if (result != SL_STATUS_OK) {
esl_core_set_last_error(ESL_ERROR_INSUFFICIENT_RESOURCES);
- } else if (needs_response) {
- result = esl_core_build_response(ESL_TLV_RESPONSE_BASIC_STATE,
- &basic_state);
+ } else {
+ // predict the imminent loss of synchronization
+ esl_core_set_basic_state_bit(ESL_BASIC_STATE_SYNCHRONIZED_BIT,
+ ESL_CLEAR);
+
+ if (needs_response) {
+ esl_basic_state_t basic_state = esl_core_get_basic_state();
+ result = esl_core_build_response(ESL_TLV_RESPONSE_BASIC_STATE,
+ &basic_state);
+ }
}
} break;
diff --git a/app/bluetooth/common/hci_coex/src/sli_bt_hci_coex.c b/app/bluetooth/common/hci_coex/src/sli_bt_hci_coex.c
index 55d30345eb7..3e9d47e0171 100644
--- a/app/bluetooth/common/hci_coex/src/sli_bt_hci_coex.c
+++ b/app/bluetooth/common/hci_coex/src/sli_bt_hci_coex.c
@@ -52,7 +52,6 @@ bool sli_bt_hci_handle_coex_command(struct sl_btctrl_hci_message * hcimsg)
//Invalid parameters or some other error
sl_btctrl_hci_message_set_response(hcimsg, BT_ERR_INVALID, NULL, 0);
return true;
-#if 0 //Depends on BG-12346
case SL_BT_HCI_COEX_GET_PARAMETERS_OPCODE:
if (length == 0) {
sl_bt_ll_coex_config_t params;
@@ -63,7 +62,6 @@ bool sli_bt_hci_handle_coex_command(struct sl_btctrl_hci_message * hcimsg)
//Invalid parameters or some other error
sl_btctrl_hci_message_set_response(hcimsg, BT_ERR_INVALID, NULL, 0);
return true;
-#endif
case SL_BT_HCI_COEX_SET_DIRECTIONAL_PRIORITY_PULSE_OPCODE:
if (length == sizeof(uint8_t)) {
uint8_t params;
diff --git a/app/bluetooth/common/ncp/sl_ncp.c b/app/bluetooth/common/ncp/sl_ncp.c
index 49136825fec..10f3fcd6a6d 100644
--- a/app/bluetooth/common/ncp/sl_ncp.c
+++ b/app/bluetooth/common/ncp/sl_ncp.c
@@ -595,7 +595,6 @@ static void cmd_dequeue(void)
{
CORE_DECLARE_IRQ_STATE;
CORE_ENTER_ATOMIC();
- memset((void *)cmd.buf, 0, (size_t)cmd.len);
cmd.len = 0;
cmd_clr_available();
CORE_EXIT_ATOMIC();
@@ -627,7 +626,6 @@ static void evt_dequeue(void)
{
CORE_DECLARE_IRQ_STATE;
CORE_ENTER_ATOMIC();
- memset((void *)evt.buf, 0, sizeof(evt.buf));
evt.len = 0;
evt_clr_available();
CORE_EXIT_ATOMIC();
diff --git a/app/bluetooth/common/ots/src/sl_bt_ots_client.c b/app/bluetooth/common/ots/src/sl_bt_ots_client.c
index d1bce80256c..177e6ab5185 100644
--- a/app/bluetooth/common/ots/src/sl_bt_ots_client.c
+++ b/app/bluetooth/common/ots/src/sl_bt_ots_client.c
@@ -1027,10 +1027,17 @@ sl_status_t sl_bt_ots_client_abort(sl_bt_ots_client_handle_t client)
void sli_bt_ots_client_init(void)
{
+ CORE_DECLARE_IRQ_STATE;
+
+ CORE_ENTER_CRITICAL();
// Clear connection handle table
memset(connection_handle_table,
INVALID_CONNECTION_HANDLE,
sizeof(connection_handle_table));
+ // Clear active clients handle table
+ memset(active_client,
+ 0,
+ sizeof(active_client));
for (uint8_t connection_index = 0; connection_index < SL_BT_CONFIG_MAX_CONNECTIONS; connection_index++) {
// Read queue
app_queue_init(&read_queue[connection_index],
@@ -1043,6 +1050,7 @@ void sli_bt_ots_client_init(void)
sizeof(write_queue_item_t),
(uint8_t *)write_data[connection_index]);
}
+ CORE_EXIT_CRITICAL();
}
void sli_bt_ots_client_step(void)
@@ -1142,8 +1150,14 @@ void sli_bt_ots_client_on_bt_event(sl_bt_msg_t *evt)
uint8_t active_handle_index = 0;
switch (SL_BT_MSG_ID(evt->header)) {
- case sl_bt_evt_system_boot_id:
- break;
+ case sl_bt_evt_system_boot_id: {
+ sl_bt_ots_client_t *handle = NULL;
+ while ((handle = (sl_bt_ots_client_t *)sl_slist_pop(&client_list)) != NULL) {
+ // reset status
+ handle->status = CLIENT_STATUS_DISCONNECTED;
+ }
+ sli_bt_ots_client_init();
+ } break;
case sl_bt_evt_connection_opened_id:
for (uint8_t i = 0; i < SL_BT_CONFIG_MAX_CONNECTIONS; i++ ) {
if (connection_handle_table[i] == INVALID_CONNECTION_HANDLE) {
@@ -1173,11 +1187,12 @@ void sli_bt_ots_client_on_bt_event(sl_bt_msg_t *evt)
// Set status
handle->status = CLIENT_STATUS_DISCONNECTED;
- // Remove client from the list
- sl_slist_remove(&client_list, &handle->node);
-
// Do callback
CALL_SAFE(handle, on_disconnect, handle);
+
+ // Remove client from the list
+ sl_slist_remove(&client_list, &handle->node);
+ break;
}
}
// Clean index
diff --git a/app/bluetooth/common/simple_com/config/sl_simple_com_config.h b/app/bluetooth/common/simple_com/config/sl_simple_com_config.h
index 8af7e4b25a5..b742d1ed339 100644
--- a/app/bluetooth/common/simple_com/config/sl_simple_com_config.h
+++ b/app/bluetooth/common/simple_com/config/sl_simple_com_config.h
@@ -48,6 +48,21 @@
// Define the size of the transmit buffer in bytes.
#define SL_SIMPLE_COM_TX_BUF_SIZE (260)
+// Robust
+// Message header
+// Robustify the communication by adding a message header and filter out invalid messages.
+// Note: This configuration should match on the sender and receiver side.
+// Default: Off
+#define SL_SIMPLE_COM_ROBUST 0
+
+// CRC
+// Add payload CRC and perform checking.
+// Note: This configuration should match on the sender and receiver side.
+// Default: On
+#define SL_SIMPLE_COM_ROBUST_CRC 1
+//
+//
+
// <<< end of configuration section >>>
/** @} (end addtogroup simple_com) */
diff --git a/app/bluetooth/common/simple_com/sl_simple_com_robust.c b/app/bluetooth/common/simple_com/sl_simple_com_robust.c
new file mode 100644
index 00000000000..ae93a2d1639
--- /dev/null
+++ b/app/bluetooth/common/simple_com/sl_simple_com_robust.c
@@ -0,0 +1,211 @@
+/***************************************************************************//**
+ * @file
+ * @brief Robust Communication
+ *******************************************************************************
+ * # License
+ * Copyright 2023 Silicon Laboratories Inc. www.silabs.com
+ *******************************************************************************
+ *
+ * SPDX-License-Identifier: Zlib
+ *
+ * The licensor of this software is Silicon Laboratories Inc.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ ******************************************************************************/
+// -----------------------------------------------------------------------------
+// Includes
+#include
+#include
+#include "sl_simple_com_robust.h"
+#ifndef HOST_TOOLCHAIN
+#include "sl_simple_com_config.h"
+#else // HOST_TOOLCHAIN
+#include "host_comm_config.h"
+#endif // HOST_TOOLCHAIN
+
+#define PREAMBLE_BYTE 0b01011010 // 0x5A
+#define MAX_PAYLOAD_LENGTH 2047
+#define PAYLOAD_LENGTH_MASK 0b11100000
+#define CRC_PRESENT_FLAG 0b00010000
+
+// -----------------------------------------------------------------------------
+// Private function declarations
+
+/**************************************************************************//**
+ * Calculate CRC-4 checksum using the x^4 + x + 1 polynomial
+ *
+ * @param[in] data pointer to the input data
+ * @param[in] len length of the input data in nibbles
+ * @return CRC-4 checksum
+ *****************************************************************************/
+static uint8_t crc4(const uint8_t *data, size_t len);
+
+#if defined(SL_SIMPLE_COM_ROBUST_CRC) && SL_SIMPLE_COM_ROBUST_CRC == 1
+/**************************************************************************//**
+ * Calculate CRC-8 checksum using the x^8 + x^2 + x + 1 polynomial
+ *
+ * @param[in] data pointer to the input data
+ * @param[in] len size of the input data in bytes
+ * @return CRC-8 checksum
+ *****************************************************************************/
+static uint8_t crc8(const uint8_t *data, size_t len);
+#endif
+
+// -----------------------------------------------------------------------------
+// Private function definitions
+
+// Calculate CRC-4 checksum using the x^4 + x + 1 polynomial
+uint8_t crc4(const uint8_t *data, size_t len)
+{
+ static const uint8_t table[] = {
+ 0x0, 0x7, 0xe, 0x9, 0x5, 0x2, 0xb, 0xc, 0xa, 0xd, 0x4, 0x3, 0xf, 0x8, 0x1, 0x6
+ };
+
+ uint8_t crc = 0xa; // CRC value of the preamble 0x5A
+ for (size_t i = 0; i < len; i++) {
+ uint8_t shift = i % 2 == 0 ? 4 : 0; // high nibbles have even index
+ uint8_t nibble = (data[i / 2] >> shift) & 0x0F;
+ crc = table[crc ^ nibble];
+ }
+ return crc;
+}
+
+#if defined(SL_SIMPLE_COM_ROBUST_CRC) && SL_SIMPLE_COM_ROBUST_CRC == 1
+// Calculate CRC-8 checksum using the x^8 + x^2 + x + 1 polynomial
+static uint8_t crc8(const uint8_t *data, size_t len)
+{
+ uint32_t crc = 0; // initial value
+ for (size_t i = 0; i < len; i++) {
+ crc ^= data[i] << 8;
+ for (size_t j = 8; j > 0; j--) {
+ if (crc & 0x8000) {
+ crc ^= (0x1070 << 3);
+ }
+ crc <<= 1;
+ }
+ }
+ return (uint8_t)(crc >> 8);
+}
+#endif
+
+// -----------------------------------------------------------------------------
+// Public function definitions
+
+// Pack data between preamble byte and (if turned on) CRC checksum
+size_t sl_simple_com_robust_pack_data(uint8_t *packed_data_ptr,
+ const uint8_t *data,
+ uint16_t len)
+{
+ if (packed_data_ptr == NULL || data == NULL || len > MAX_PAYLOAD_LENGTH) {
+ return 0;
+ }
+ size_t packed_data_size = sl_simple_com_robust_get_pack_buffer_size(len);
+
+ // Building the 3 header bytes: preamble (1 byte),
+ // Payload length (11 bits), CRC present flag (1 bit), header CRC-4 (4 bits)
+ packed_data_ptr[0] = PREAMBLE_BYTE;
+ // 2nd byte of the header contains the lower 8 bits of payload length
+ packed_data_ptr[1] = (uint8_t)len;
+ // Upper 3 bits of the 3rd byte of the header contains the higher 3 bits of payload length
+ packed_data_ptr[2] = (uint8_t)((len >> 3) & PAYLOAD_LENGTH_MASK);
+#if defined(SL_SIMPLE_COM_ROBUST_CRC) && SL_SIMPLE_COM_ROBUST_CRC == 1
+ packed_data_ptr[2] |= CRC_PRESENT_FLAG;
+#endif
+ // Calculate CRC value for header, exclude preamble
+ packed_data_ptr[2] |= crc4(packed_data_ptr + 1, 3);
+
+ // Payload
+ memcpy(&packed_data_ptr[SL_SIMPLE_COM_ROBUST_HEADER_SIZE], data, len);
+
+ // CRC-8
+#if defined(SL_SIMPLE_COM_ROBUST_CRC) && SL_SIMPLE_COM_ROBUST_CRC == 1
+ packed_data_ptr[packed_data_size - 1] = crc8(data, len);
+#endif
+ return packed_data_size;
+}
+
+// Unpack packets from byte stream looking for valid headers
+sl_simple_com_robust_result_t sl_simple_com_robust_unpack_data(uint8_t *data,
+ size_t len)
+{
+ sl_simple_com_robust_result_t result = {
+ .status = SL_STATUS_FAIL,
+ .payload = NULL,
+ .payload_size = 0,
+ .processed = 0
+ };
+
+ if (data == NULL) {
+ result.status = SL_STATUS_NULL_POINTER;
+ return result;
+ }
+ if (len <= SL_SIMPLE_COM_ROBUST_HEADER_SIZE) {
+ result.status = SL_STATUS_EMPTY;
+ return result;
+ }
+
+ // Find and validate the message header
+ size_t margin = len - SL_SIMPLE_COM_ROBUST_HEADER_SIZE;
+ while ((data[result.processed] != PREAMBLE_BYTE
+ || crc4(data + result.processed + 1, 4) != 0)
+ && result.processed < margin) {
+ result.processed++;
+ }
+ // No valid frame found
+ if (result.processed >= margin) {
+ result.status = SL_STATUS_NOT_FOUND;
+ return result;
+ }
+
+ // Shift input data to the beginning of the frame
+ data += result.processed;
+ len -= result.processed;
+
+ // Check payload CRC flag
+ bool crc_present = (data[2] & CRC_PRESENT_FLAG) != 0;
+
+ // Get payload length from header (11 bits)
+ result.payload_size = data[1] | ((data[2] & PAYLOAD_LENGTH_MASK) << 3);
+
+ // Check if the incoming data contains the whole payload
+ size_t message_size = SL_SIMPLE_COM_ROBUST_HEADER_SIZE + result.payload_size;
+ if (crc_present) {
+ message_size++;
+ }
+
+ if (len < message_size) {
+ result.status = SL_STATUS_FAIL;
+ return result;
+ }
+
+ result.processed += message_size;
+ result.payload = data + SL_SIMPLE_COM_ROBUST_HEADER_SIZE;
+
+ // If payload CRC is present, validate
+#if defined(SL_SIMPLE_COM_ROBUST_CRC) && SL_SIMPLE_COM_ROBUST_CRC == 1
+ if (crc_present) {
+ if (crc8(result.payload, result.payload_size + 1) != 0) {
+ result.status = SL_STATUS_FAIL;
+ return result;
+ }
+ }
+#endif // SL_SIMPLE_COM_ROBUST_CRC
+
+ result.status = SL_STATUS_OK;
+ return result;
+}
diff --git a/app/bluetooth/common/simple_com/sl_simple_com_robust.h b/app/bluetooth/common/simple_com/sl_simple_com_robust.h
new file mode 100644
index 00000000000..9713673c630
--- /dev/null
+++ b/app/bluetooth/common/simple_com/sl_simple_com_robust.h
@@ -0,0 +1,90 @@
+/***************************************************************************//**
+ * @file
+ * @brief Robust Communication Interface
+ * Packeting layer providing reliable communication with guaranteeing data
+ * integrity. This module can be used to pack bytes between a header and a CRC
+ * value and unpack and validate packets from a data stream.
+ *******************************************************************************
+ * # License
+ * Copyright 2023 Silicon Laboratories Inc. www.silabs.com
+ *******************************************************************************
+ *
+ * SPDX-License-Identifier: Zlib
+ *
+ * The licensor of this software is Silicon Laboratories Inc.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ ******************************************************************************/
+
+#ifndef SL_SIMPLE_COM_ROBUST_H
+#define SL_SIMPLE_COM_ROBUST_H
+
+#include
+#include
+#include "sl_status.h"
+#ifndef HOST_TOOLCHAIN
+#include "sl_simple_com_config.h"
+#else // HOST_TOOLCHAIN
+#include "host_comm_config.h"
+#endif // HOST_TOOLCHAIN
+
+#define SL_SIMPLE_COM_ROBUST_HEADER_SIZE 3
+
+// Get required buffer size for packed data
+#if defined(SL_SIMPLE_COM_ROBUST_CRC) && SL_SIMPLE_COM_ROBUST_CRC == 1
+#define sl_simple_com_robust_get_pack_buffer_size(len) (len + SL_SIMPLE_COM_ROBUST_HEADER_SIZE + 1)
+#else // SL_SIMPLE_COM_ROBUST_CRC
+#define sl_simple_com_robust_get_pack_buffer_size(len) (len + SL_SIMPLE_COM_ROBUST_HEADER_SIZE)
+#endif // SL_SIMPLE_COM_ROBUST_CRC
+
+typedef struct {
+ sl_status_t status; // SL_STATUS_OK if a packet was found and validated with success
+ uint8_t *payload; // Pointer to the beginning of the payload
+ size_t payload_size; // Payload length in bytes
+ size_t processed; // Number of bytes processed from the input buffer
+} sl_simple_com_robust_result_t;
+
+/**************************************************************************//**
+ * Pack data between preamble byte and (if turned on) CRC checksum
+ * This function adds a 3 byte header containing preamble byte, payload
+ * length, CRC flag and CRC value to the header. If CRC is required an 8 bit
+ * CRC value will be calculated for the payload and added to the end of packet.
+ *
+ * @param[out] packed_data_ptr pointer where the packed data should be loaded
+ * @param[in] data pointer to the data to be packed
+ * @param[in] len size of the data to be packed
+ * @return the size of packed data
+ *****************************************************************************/
+size_t sl_simple_com_robust_pack_data(uint8_t *packed_data_ptr,
+ const uint8_t *data,
+ uint16_t len);
+
+/**************************************************************************//**
+ * Unpack packets from byte stream looking for valid headers.
+ * This function searches valid headers and extracts payload after the header
+ * into destination buffer. Header is validated by 4 bit header CRC and payload
+ * can be also validated by optional CRC check.
+ *
+ * @param[in] data pointer to the data to be unpacked
+ * @param[in] len size of the data to be unpacked
+ * @return the result of the action
+ *****************************************************************************/
+sl_simple_com_robust_result_t sl_simple_com_robust_unpack_data(uint8_t *data,
+ size_t len);
+
+#endif // SL_SIMPLE_COM_ROBUST_H
diff --git a/app/bluetooth/common/simple_com/sl_simple_com_usart.c b/app/bluetooth/common/simple_com/sl_simple_com_usart.c
index 95ece138665..be714c4245f 100644
--- a/app/bluetooth/common/simple_com/sl_simple_com_usart.c
+++ b/app/bluetooth/common/simple_com/sl_simple_com_usart.c
@@ -38,6 +38,9 @@
#include "app_assert.h"
#include "sl_simple_com.h"
#include "sl_simple_com_config.h"
+#if defined(SL_SIMPLE_COM_ROBUST) && SL_SIMPLE_COM_ROBUST == 1
+#include "sl_simple_com_robust.h"
+#endif // SL_SIMPLE_COM_ROBUST
#ifdef EFR32BG1_USART_E202_WORKAROUND
#include "sl_sleeptimer.h"
#endif // EFR32BG1_USART_E202_WORKAROUND
@@ -128,7 +131,6 @@ void sl_simple_com_step(void)
sl_simple_com_os_task_proceed();
} else {
// Clear TX buffer
- memset(tx_buf, 0, sizeof(tx_buf));
tx_cb_signal.timeout = 0;
tx_cb_signal.finished = false;
// Call public callback API
@@ -143,7 +145,7 @@ void sl_simple_com_step(void)
*
* Transmits len bytes of data through the UART interface using DMA.
*
- * @param[out] len Message lenght
+ * @param[out] len Message length
* @param[out] data Message data
*****************************************************************************/
void sl_simple_com_transmit(uint32_t len, uint8_t *data)
@@ -153,7 +155,12 @@ void sl_simple_com_transmit(uint32_t len, uint8_t *data)
// transmission completes
app_assert(len <= SL_SIMPLE_COM_TX_BUF_SIZE,
"TX length is bigger than allocated buffer\n");
+#if defined(SL_SIMPLE_COM_ROBUST) && SL_SIMPLE_COM_ROBUST == 1
+ len = sl_simple_com_robust_pack_data(tx_buf, data, (size_t)len);
+#else // SL_SIMPLE_COM_ROBUST
memcpy((void *)tx_buf, (void *)data, (size_t)len);
+#endif // SL_SIMPLE_COM_ROBUST
+
// Transmit data using a non-blocking transmit function
ec = UARTDRV_Transmit(uartdrv_handle,
tx_buf,
@@ -366,14 +373,42 @@ static void receive_cb(UARTDRV_Handle_t handle,
UARTDRV_Count_t transferCount)
{
(void)handle;
- // Call public callback API
- sl_simple_com_receive_cb(ECODE_EMDRV_UARTDRV_OK == transferStatus
- ? SL_STATUS_OK : SL_STATUS_FAIL,
- transferCount,
- data);
- // Clear RX buffer
- memset(rx_buf, 0, sizeof(rx_buf));
- sl_simple_com_os_task_proceed();
+
+#if defined(SL_SIMPLE_COM_ROBUST) && SL_SIMPLE_COM_ROBUST == 1
+ if (transferCount > 0) {
+ sl_simple_com_robust_result_t result = sl_simple_com_robust_unpack_data(data,
+ transferCount);
+
+ // Call public callback API
+ sl_simple_com_receive_cb((ECODE_EMDRV_UARTDRV_OK == transferStatus
+ ? SL_STATUS_OK : SL_STATUS_FAIL | result.status),
+ result.payload_size,
+ result.payload);
+
+ // Calculate processed data from buf_in_packed. (Payload + overhead)
+ int32_t processed = result.payload_size + sl_simple_com_robust_get_pack_buffer_size(0);
+
+ // Clear processed data from RX buffer
+ memmove(rx_buf, &rx_buf[processed], transferCount - processed);
+ memset(&rx_buf[transferCount - processed],
+ 0,
+ sizeof(rx_buf) - (transferCount - processed));
+
+ sl_simple_com_os_task_proceed();
+ } else {
+#else // SL_SIMPLE_COM_ROBUST
+ {
+#endif // SL_SIMPLE_COM_ROBUST
+ // Call public callback API
+ sl_simple_com_receive_cb(ECODE_EMDRV_UARTDRV_OK == transferStatus
+ ? SL_STATUS_OK : SL_STATUS_FAIL,
+ transferCount,
+ data);
+
+ // Clear RX buffer
+ memset(rx_buf, 0, sizeof(rx_buf));
+ sl_simple_com_os_task_proceed();
+ }
}
/**************************************************************************//**
diff --git a/app/bluetooth/common_host/app_log/config/app_log_config.h b/app/bluetooth/common_host/app_log/config/app_log_config.h
index 9ad4eb1b855..9c67585a177 100644
--- a/app/bluetooth/common_host/app_log/config/app_log_config.h
+++ b/app/bluetooth/common_host/app_log/config/app_log_config.h
@@ -71,8 +71,9 @@
// Application Logging
// Enables Logging.
+#ifndef APP_LOG_ENABLE
#define APP_LOG_ENABLE 1
-
+#endif // APP_LOG_ENABLE
// General
// Trace
diff --git a/app/bluetooth/common_host/app_sleep/app_sleep.c b/app/bluetooth/common_host/app_sleep/app_sleep.c
index 6014a76855c..6236c94d73b 100644
--- a/app/bluetooth/common_host/app_sleep/app_sleep.c
+++ b/app/bluetooth/common_host/app_sleep/app_sleep.c
@@ -47,7 +47,7 @@ void app_sleep_us(uint64_t usec)
}
// Convert to 100 nanosec interval, negative value indicates relative time
- due_time.QuadPart = -(10 * usec);
+ due_time.QuadPart = -((10 * usec) - 1);
timer = CreateWaitableTimer(NULL, TRUE, NULL);
SetWaitableTimer(timer, &due_time, 0, NULL, NULL, 0);
diff --git a/app/bluetooth/common_host/app_timer/app_timer_mac.c b/app/bluetooth/common_host/app_timer/app_timer_mac.c
index 794d0a1c3dc..0a99eaf4732 100644
--- a/app/bluetooth/common_host/app_timer/app_timer_mac.c
+++ b/app/bluetooth/common_host/app_timer/app_timer_mac.c
@@ -119,11 +119,11 @@ void sli_app_timer_step(void)
while (NULL != tmp_timer_ptr) {
if (true == tmp_timer_ptr->app_timer_handle.triggered) {
- // Delete non-periodic timers
+ // Delete if timer is non-periodic
if (false == tmp_timer_ptr->periodic) {
status = delete_timer(tmp_timer_ptr);
if (SL_STATUS_OK != status) {
- app_log_error("Failed to delete timer after stopping it"
+ app_log_error("One-shot timer could not be cleared."
APP_LOG_NEW_LINE);
}
}
diff --git a/app/bluetooth/common_host/app_timer/app_timer_posix.c b/app/bluetooth/common_host/app_timer/app_timer_posix.c
index f3a303b6bdd..81315c79143 100644
--- a/app/bluetooth/common_host/app_timer/app_timer_posix.c
+++ b/app/bluetooth/common_host/app_timer/app_timer_posix.c
@@ -156,11 +156,11 @@ void sli_app_timer_step(void)
while (NULL != tmp_timer_ptr) {
if (true == tmp_timer_ptr->app_timer_handle.triggered) {
- // Delete non-periodic timers
+ // Delete if timer is non-periodic
if (false == tmp_timer_ptr->periodic) {
- status = delete_timer(&(tmp_timer_ptr->app_timer_handle.timer_id));
+ status = app_timer_stop(tmp_timer_ptr);
if (SL_STATUS_OK != status) {
- app_log_error("Failed to delete timer after stopping it"
+ app_log_error("One-shot timer could not be cleared."
APP_LOG_NL);
}
}
@@ -363,15 +363,26 @@ static bool contains_app_timer(app_timer_t *timer)
******************************************************************************/
static void handler(int sig, siginfo_t *si, void *uc)
{
+ timer_t *timer_id = (timer_t*)(si->si_value.sival_ptr);
app_timer_t *tmp_timer_ptr = NULL;
- tmp_timer_ptr = find_timer((timer_t*)(si->si_value.sival_ptr));
+ tmp_timer_ptr = find_timer(timer_id);
if (NULL != tmp_timer_ptr) {
tmp_timer_ptr->app_timer_handle.triggered = true;
+ // Delete if timer is non-periodic
+ if (false == tmp_timer_ptr->periodic) {
+ struct itimerspec ts = { { 0, 0 }, { 0, 0 } };
+ int status = timer_settime(tmp_timer_ptr->app_timer_handle.timer_id, 0, &ts, NULL);
+ int errsv = errno;
+ if (0 != status) {
+ app_log_error("Failed to stop non-periodic timer with error %d" APP_LOG_NL, errsv);
+ }
+ }
} else {
app_log_error("Timer was not found with the ID that was expired."
APP_LOG_NL);
+ delete_timer(timer_id);
}
}
diff --git a/app/bluetooth/common_host/app_timer/app_timer_win.c b/app/bluetooth/common_host/app_timer/app_timer_win.c
index 65ce0c961ae..66b290f507a 100644
--- a/app/bluetooth/common_host/app_timer/app_timer_win.c
+++ b/app/bluetooth/common_host/app_timer/app_timer_win.c
@@ -28,6 +28,7 @@
*
******************************************************************************/
+#include
#include "app_timer.h"
#include "app_log.h"
@@ -86,6 +87,15 @@ static void append_app_timer(app_timer_t *timer);
******************************************************************************/
static WINBOOL remove_app_timer(app_timer_t *timer);
+/***************************************************************************//**
+ * Check if timer is in the list
+ *
+ * @param[in] timer app_timer reference
+ *
+ * @returns true if the timer is in the list, flase if not
+ ******************************************************************************/
+static bool contains_app_timer(app_timer_t *timer);
+
// -----------------------------------------------------------------------------
// Private function definitions
@@ -107,9 +117,17 @@ __attribute__((stdcall)) static void app_timer_common_callback(HWND hwnd,
tmp_timer_ptr = tmp_timer_ptr->next;
}
if (NULL == tmp_timer_ptr) {
- app_log_error("Timer handle not found." APP_LOG_NL);
+ app_log_error("Timer handle %d not found." APP_LOG_NL, timer_id);
+ (void)KillTimer(NULL, timer_id);
+ (void)GetLastError();
} else {
tmp_timer_ptr->app_timer_handle.triggered = true;
+ // Delete non-periodic timers
+ if (false == tmp_timer_ptr->periodic) {
+ (void)KillTimer(NULL, timer_id);
+ (void)GetLastError();
+ tmp_timer_ptr->app_timer_handle.timer_id = 0;
+ }
}
}
@@ -165,6 +183,21 @@ static WINBOOL remove_app_timer(app_timer_t *timer)
return true;
}
+/***************************************************************************//**
+ * Check if app timer is in the list
+ ******************************************************************************/
+static bool contains_app_timer(app_timer_t *timer)
+{
+ app_timer_t *local_timer = app_timer_head;
+
+ while (NULL != local_timer) {
+ if (timer == local_timer) {
+ return true;
+ }
+ local_timer = local_timer->next;
+ }
+ return false;
+}
// -----------------------------------------------------------------------------
// Public function definitions
@@ -181,7 +214,6 @@ void app_timer_init(void)
******************************************************************************/
void sli_app_timer_step(void)
{
- sl_status_t status = SL_STATUS_FAIL;
app_timer_t *tmp_timer_ptr = app_timer_head;
MSG msg;
@@ -197,11 +229,11 @@ void sli_app_timer_step(void)
while (NULL != tmp_timer_ptr) {
if (true == tmp_timer_ptr->app_timer_handle.triggered) {
- // Delete non-periodic timers
+ // Delete if timer is non-periodic
if (false == tmp_timer_ptr->periodic) {
- status = app_timer_stop(tmp_timer_ptr);
+ sl_status_t status = app_timer_stop(tmp_timer_ptr);
if (SL_STATUS_OK != status) {
- app_log_error("Failed to stop oneshot timer." APP_LOG_NL);
+ app_log_error("One-shot timer could not be cleared." APP_LOG_NL);
}
}
if (NULL != tmp_timer_ptr->callback) {
@@ -258,31 +290,47 @@ sl_status_t app_timer_start(app_timer_t *timer,
******************************************************************************/
sl_status_t app_timer_stop(app_timer_t *timer)
{
+ sl_status_t result = SL_STATUS_NULL_POINTER;
WINBOOL status = false;
if (timer == NULL) {
- return SL_STATUS_NULL_POINTER;
+ return result;
}
- status = KillTimer(NULL, timer->app_timer_handle.timer_id);
- if (false == status) {
- DWORD LastError = GetLastError();
- if (!LastError) {
- // Special case on Windows
- // 0 a.k.a ERROR_SUCCESS means that the operation failed successfully.
- return SL_STATUS_OK;
+ if (contains_app_timer(timer)) {
+ if (timer->app_timer_handle.timer_id != 0) {
+ status = KillTimer(NULL, timer->app_timer_handle.timer_id);
} else {
- app_log_error("Stopping timer returned with error code %lu "
- APP_LOG_NL, LastError);
- return SL_STATUS_FAIL;
+ status = true; // Can be stopped already either because it's one-shot and expired or by users
+ result = SL_STATUS_OK;
}
- }
- status = remove_app_timer(timer);
- if (false == status) {
- app_log_error("Could not remove timer " APP_LOG_NL);
- return SL_STATUS_FAIL;
+ if (false == status) {
+ DWORD LastError = GetLastError();
+ if (!LastError || LastError == ERROR_INVALID_WINDOW_HANDLE) {
+ // Special cases on Windows
+ // 0 a.k.a ERROR_SUCCESS means that the operation failed successfully.
+ // ERROR_INVALID_WINDOW_HANDLE seems normal on some Windows versions if hWnd was passed as NULL
+ result = SL_STATUS_OK;
+ } else {
+ app_log_error("Stopping timer returned with error code %lu "
+ APP_LOG_NL, LastError);
+ result = SL_STATUS_FAIL;
+ }
+ } else {
+ result = SL_STATUS_OK;
+ }
+
+ if (result == SL_STATUS_OK) {
+ timer->app_timer_handle.timer_id = 0;
+ (void)remove_app_timer(timer);
+ }
} else {
- return SL_STATUS_OK;
+ if (timer->app_timer_handle.timer_id != 0) {
+ (void)KillTimer(NULL, timer->app_timer_handle.timer_id);
+ (void)GetLastError();
+ timer->app_timer_handle.timer_id = 0;
+ }
}
+ return result;
}
/** @} (end addtogroup timer) */
diff --git a/app/bluetooth/common_host/esl_key_lib/esl_key_lib.h b/app/bluetooth/common_host/esl_key_lib/esl_key_lib.h
index 0f637181471..0a4959bb7d0 100644
--- a/app/bluetooth/common_host/esl_key_lib/esl_key_lib.h
+++ b/app/bluetooth/common_host/esl_key_lib/esl_key_lib.h
@@ -475,5 +475,8 @@ sl_status_t DLL_EXPORT esl_key_lib_get_bind_address_by_ble_address(const db_hand
sl_status_t DLL_EXPORT esl_key_lib_get_bind_address_by_esl_address(const db_handle_p db_hnd,
const esl_address_t esl_address,
bd_addr *bind_addr_out);
+#ifdef __cplusplus
+};
+#endif
#endif // ESL_KEY_LIB_H
diff --git a/app/bluetooth/common_host/esl_key_lib/makefile b/app/bluetooth/common_host/esl_key_lib/makefile
index dbed404c96d..7f3604a8d6c 100644
--- a/app/bluetooth/common_host/esl_key_lib/makefile
+++ b/app/bluetooth/common_host/esl_key_lib/makefile
@@ -67,7 +67,7 @@ endif
################################################################################
.SUFFIXES: # ignore builtin rules
-.PHONY: all debug release clean clean_export export help
+.PHONY: all debug release clean clean_export clean_keep_sql export help
# Default directories
OBJ_DIR ?= obj
@@ -88,11 +88,12 @@ CFLAGS_RELEASE ?= -DNDEBUG \
HELP_MESSAGE += \
"Available build targets for $(PROJECTNAME)\n" \
-" debug - default target, optimized for debugging\n" \
-" release - build with defult compiler optimization\n" \
-" clean - remove build directories\n" \
-" export - copy all project resources into '$(EXPORT_DIR)' directory\n" \
-" help - print this help message\n"
+" debug - default target, optimized for debugging\n" \
+" release - build with defult compiler optimization\n" \
+" clean - remove build directories\n" \
+" clean_keep_sql - remove build directories but keep sqlite3 files\n" \
+" export - copy all project resources into '$(EXPORT_DIR)' directory\n" \
+" help - print this help message\n"
ifeq (, $(filter $(UNAME), darwin linux))
# Enable escape sequence
@@ -157,7 +158,7 @@ $(EXE_DIR)/$(PROJECTNAME): $(OBJS) $(LIBS)
$(PROJECTNAME)_wrapper.py: $(PROJECTNAME).h
@echo "Generating Python wrapper"
- ctypesgen --no-gnu-types $(INCFLAGS) $(PROJECTNAME).h -o $(PROJECTNAME)_wrapper.py
+ ctypesgen --no-gnu-types --allow-gnu-c --no-macro-warnings $(INCFLAGS) $(PROJECTNAME).h -o $(PROJECTNAME)_wrapper.py 2> /dev/null
@echo "Fixing up Python wrapper"
../../script/ctypesgen_wrapper_fix.py $(PROJECTNAME)_wrapper.py -v
@@ -167,6 +168,13 @@ ifeq ($(filter $(MAKECMDGOALS),all debug release export),)
rm -f $(PROJECTNAME)_wrapper.py
endif
+clean_keep_sql: clean_export
+ifeq ($(filter $(MAKECMDGOALS),all debug release export),)
+ find $(OBJ_DIR) ! \( -type f -and -iname "sqlite3.*" \) -delete || true
+ rm -rf $(EXE_DIR)
+ rm -f $(PROJECTNAME)_wrapper.py
+endif
+
clean_export:
@if [ -d $(EXPORT_DIR) ]; then \
read -p "Enter 'y' to remove '$(EXPORT_DIR)': " ans && if [ _$$ans = _y ]; then rm -rf $(EXPORT_DIR); fi \
diff --git a/app/bluetooth/common_host/esl_lib/esl_lib.c b/app/bluetooth/common_host/esl_lib/esl_lib.c
index ad0719a6660..a078b1d583d 100644
--- a/app/bluetooth/common_host/esl_lib/esl_lib.c
+++ b/app/bluetooth/common_host/esl_lib/esl_lib.c
@@ -66,7 +66,7 @@ sl_status_t esl_lib_start(char *config,
esl_lib_on_event_t event_handler,
esl_lib_log_callback_t log_fn)
{
- esl_lib_log_api_info("Requested: Start" APP_LOG_NL);
+ esl_lib_log_api_debug("Requested: Start" APP_LOG_NL);
CHECK_NULL(event_handler);
@@ -103,7 +103,7 @@ sl_status_t esl_lib_start(char *config,
sl_status_t esl_lib_stop(void)
{
- esl_lib_log_api_info("Requested: Stop" APP_LOG_NL);
+ esl_lib_log_api_debug("Requested: Stop" APP_LOG_NL);
run = false;
return SL_STATUS_OK;
@@ -129,7 +129,7 @@ sl_status_t esl_lib_connect(esl_lib_address_t address,
uint8_t *data_ptr = NULL;
esl_lib_connect_tlv_t *tlv = NULL;
- esl_lib_log_api_info("Requested: Connect" APP_LOG_NL);
+ esl_lib_log_api_debug("Requested: Connect" APP_LOG_NL);
// Check for NULL key
if (key_type != ESL_LIB_KEY_TYPE_NO_KEY && key == NULL) {
@@ -175,9 +175,11 @@ sl_status_t esl_lib_connect(esl_lib_address_t address,
// Allocate memory for the command
cmd = (esl_lib_command_list_cmd_t *)esl_lib_memory_allocate(size + tlv_size);
if (cmd != NULL) {
+ cmd->data.cmd_connect.retries_left = ESL_LIB_CONNECTION_RETRY_COUNT_MAX;
cmd->cmd_code = ESL_LIB_CMD_CONNECT;
+ cmd->data.cmd_connect.conn_hnd = ESL_LIB_INVALID_HANDLE;
// Set address and type
- cmd->data.cmd_connect.address.addr_type = address.addr_type;
+ cmd->data.cmd_connect.address.address_type = address.address_type;
memcpy(cmd->data.cmd_connect.address.addr,
address.addr,
sizeof(address.addr));
@@ -195,9 +197,9 @@ sl_status_t esl_lib_connect(esl_lib_address_t address,
tlv->data.len = sizeof(esl_lib_pawr_subevent_t);
memcpy(tlv->data.data, (uint8_t*)pawr, tlv->data.len);
data_ptr += sizeof(esl_lib_connect_tlv_t) + tlv->data.len;
+ // Point TLV
+ tlv = (esl_lib_connect_tlv_t *)data_ptr;
}
- // Point TLV
- tlv = (esl_lib_connect_tlv_t *)data_ptr;
// Add identity address
if (identity != NULL) {
@@ -216,11 +218,10 @@ sl_status_t esl_lib_connect(esl_lib_address_t address,
tlv->data.len = sizeof(esl_lib_gattdb_handles_t);
memcpy(tlv->data.data, (uint8_t*)gattdb, tlv->data.len);
data_ptr += sizeof(esl_lib_connect_tlv_t) + tlv->data.len;
+ // Point TLV
+ tlv = (esl_lib_connect_tlv_t *)data_ptr;
}
- // Point TLV
- tlv = (esl_lib_connect_tlv_t *)data_ptr;
-
// Add key
if (key != NULL) {
switch (key_type) {
@@ -257,10 +258,10 @@ sl_status_t esl_lib_close_connection(esl_lib_connection_handle_t connection_hand
sl_status_t sc = SL_STATUS_INVALID_PARAMETER;
esl_lib_command_list_cmd_t *cmd;
- esl_lib_log_api_info("Requested: Close connection" APP_LOG_NL);
+ esl_lib_log_api_debug("Requested: Close connection" APP_LOG_NL);
if (!esl_lib_connection_contains(connection_handle)) {
- return SL_STATUS_NOT_FOUND;
+ return SL_STATUS_BT_CTRL_UNKNOWN_CONNECTION_IDENTIFIER;
}
size_t size = ESL_LIB_COMMAND_LIST_HEADER_LEN + sizeof(esl_lib_connection_handle_t);
@@ -294,10 +295,10 @@ sl_status_t esl_lib_get_tag_info(esl_lib_connection_handle_t connection_handle)
sl_status_t sc = SL_STATUS_INVALID_PARAMETER;
esl_lib_command_list_cmd_t *cmd;
- esl_lib_log_api_info("Requested: Get Tag Info" APP_LOG_NL);
+ esl_lib_log_api_debug("Requested: Get Tag Info" APP_LOG_NL);
if (!esl_lib_connection_contains(connection_handle)) {
- return SL_STATUS_NOT_FOUND;
+ return SL_STATUS_BT_CTRL_UNKNOWN_CONNECTION_IDENTIFIER;
}
size_t size = ESL_LIB_COMMAND_LIST_HEADER_LEN + sizeof(esl_lib_connection_handle_t);
@@ -330,14 +331,14 @@ sl_status_t esl_lib_configure_tag(esl_lib_connection_handle_t connection_handle,
sl_status_t sc = SL_STATUS_INVALID_PARAMETER;
esl_lib_command_list_cmd_t *cmd;
- esl_lib_log_api_info("Requested: Configure Tag %s response" APP_LOG_NL,
- (att_response == ESL_LIB_TRUE) ? "with" : "without");
+ esl_lib_log_api_debug("Requested: Configure Tag %s response" APP_LOG_NL,
+ (att_response == ESL_LIB_TRUE) ? "with" : "without");
if (tlv_data == NULL) {
return SL_STATUS_NULL_POINTER;
}
if (!esl_lib_connection_contains(connection_handle)) {
- return SL_STATUS_NOT_FOUND;
+ return SL_STATUS_BT_CTRL_UNKNOWN_CONNECTION_IDENTIFIER;
}
size_t size = ESL_LIB_COMMAND_LIST_HEADER_LEN
@@ -376,13 +377,13 @@ sl_status_t esl_lib_write_control_point(esl_lib_connection_handle_t connection_h
sl_status_t sc = SL_STATUS_INVALID_PARAMETER;
esl_lib_command_list_cmd_t *cmd;
- esl_lib_log_api_info("Requested: Write Control Point" APP_LOG_NL);
-
+ esl_lib_log_api_debug("Requested: Write Control Point %s response" APP_LOG_NL,
+ (att_response == ESL_LIB_TRUE) ? "with" : "without");
if (data == NULL) {
return SL_STATUS_NULL_POINTER;
}
if (!esl_lib_connection_contains(connection_handle)) {
- return SL_STATUS_NOT_FOUND;
+ return SL_STATUS_BT_CTRL_UNKNOWN_CONNECTION_IDENTIFIER;
}
size_t size = ESL_LIB_COMMAND_LIST_HEADER_LEN
+ sizeof(esl_lib_command_list_cmd_write_control_point_t)
@@ -419,7 +420,7 @@ sl_status_t esl_lib_pawr_create(esl_lib_pawr_handle_t *handle_out)
esl_lib_pawr_t *pawr_ptr = ESL_LIB_INVALID_HANDLE;
sl_status_t sc = SL_STATUS_NULL_POINTER;
- esl_lib_log_api_info("Requested: PAwR Create" APP_LOG_NL);
+ esl_lib_log_api_debug("Requested: PAwR Create" APP_LOG_NL);
if (handle_out != NULL) {
// Add the set to the relationships and get the handle
@@ -438,7 +439,7 @@ sl_status_t esl_lib_pawr_remove(esl_lib_pawr_handle_t pawr_handle)
sl_status_t sc = SL_STATUS_INVALID_HANDLE;
esl_lib_pawr_t *pawr = (esl_lib_pawr_t *)pawr_handle;
- esl_lib_log_api_info("Requested: PAwR Remove" APP_LOG_NL);
+ esl_lib_log_api_debug("Requested: PAwR Remove" APP_LOG_NL);
if (pawr_handle != ESL_LIB_INVALID_HANDLE) {
// Check if it can be removed
@@ -459,7 +460,7 @@ sl_status_t esl_lib_pawr_enable(esl_lib_pawr_handle_t pawr_handle,
sl_status_t sc = SL_STATUS_INVALID_HANDLE;
esl_lib_command_list_cmd_t *cmd;
- esl_lib_log_api_info("Requested: PAwR Enable" APP_LOG_NL);
+ esl_lib_log_api_debug("Requested: PAwR Enable" APP_LOG_NL);
if (pawr_handle != ESL_LIB_INVALID_HANDLE) {
if (!esl_lib_pawr_contains(pawr_handle)) {
@@ -488,12 +489,13 @@ sl_status_t esl_lib_pawr_enable(esl_lib_pawr_handle_t pawr_handle,
sl_status_t esl_lib_pawr_set_data(esl_lib_pawr_handle_t pawr_handle,
uint8_t subevent,
+ uint8_t response_slot_max,
esl_lib_array_t *payload)
{
sl_status_t sc = SL_STATUS_INVALID_HANDLE;
esl_lib_command_list_cmd_t *cmd;
- esl_lib_log_api_info("Requested: PAwR Set Data" APP_LOG_NL);
+ esl_lib_log_api_debug("Requested: PAwR Set Data for subevent %u" APP_LOG_NL, subevent);
if (payload == NULL) {
return SL_STATUS_NULL_POINTER;
@@ -511,9 +513,11 @@ sl_status_t esl_lib_pawr_set_data(esl_lib_pawr_handle_t pawr_handle,
if (cmd != NULL) {
cmd->cmd_code = ESL_LIB_CMD_PAWR_SET_DATA;
// Copy data
- cmd->data.cmd_pawr_set_data.pawr_handle = pawr_handle;
- cmd->data.cmd_pawr_set_data.subevent = subevent;
- cmd->data.cmd_pawr_set_data.data.len = payload->len;
+ cmd->data.cmd_pawr_set_data.pawr_handle = pawr_handle;
+ cmd->data.cmd_pawr_set_data.subevent = subevent;
+ cmd->data.cmd_pawr_set_data.response_slot_max = response_slot_max;
+ cmd->data.cmd_pawr_set_data.retry = ESL_LIB_PAWR_SET_DATA_RETRY_COUNT_MAX;
+ cmd->data.cmd_pawr_set_data.data.len = payload->len;
// Copy payload array
memcpy(cmd->data.cmd_pawr_set_data.data.data,
payload->data,
@@ -536,7 +540,7 @@ sl_status_t esl_lib_pawr_configure(esl_lib_pawr_handle_t pawr_handle,
sl_status_t sc = SL_STATUS_INVALID_HANDLE;
esl_lib_command_list_cmd_t *cmd;
- esl_lib_log_api_info("Requested: PAwR Configure" APP_LOG_NL);
+ esl_lib_log_api_debug("Requested: PAwR Configure" APP_LOG_NL);
if (pawr_config == NULL) {
return SL_STATUS_NULL_POINTER;
@@ -575,7 +579,7 @@ sl_status_t esl_lib_get_pawr_status(esl_lib_pawr_handle_t pawr_handle)
sl_status_t sc;
esl_lib_command_list_cmd_t *cmd;
- esl_lib_log_api_info("Requested: Get PAwR status" APP_LOG_NL);
+ esl_lib_log_api_debug("Requested: Get PAwR status" APP_LOG_NL);
size_t size = ESL_LIB_COMMAND_LIST_HEADER_LEN + sizeof(esl_lib_pawr_handle_t);
// Allocate memory for the command
@@ -600,13 +604,13 @@ sl_status_t esl_lib_initiate_past(esl_lib_connection_handle_t connection_handle,
sl_status_t sc = SL_STATUS_INVALID_HANDLE;
esl_lib_command_list_cmd_t *cmd;
- esl_lib_log_api_info("Requested: Initiate PAST" APP_LOG_NL);
+ esl_lib_log_api_debug("Requested: Initiate PAST" APP_LOG_NL);
if ((connection_handle != ESL_LIB_INVALID_HANDLE)
&& (pawr_handle != ESL_LIB_INVALID_HANDLE)) {
if (!esl_lib_connection_contains(connection_handle)
|| !esl_lib_pawr_contains(pawr_handle)) {
- return SL_STATUS_NOT_FOUND;
+ return SL_STATUS_BT_CTRL_UNKNOWN_CONNECTION_IDENTIFIER;
}
size_t size = ESL_LIB_COMMAND_LIST_HEADER_LEN
+ sizeof(esl_lib_command_list_cmd_initiate_past_t);
@@ -641,11 +645,12 @@ sl_status_t esl_lib_write_image(esl_lib_connection_handle_t connection_handle,
sl_status_t sc = SL_STATUS_INVALID_HANDLE;
esl_lib_command_list_cmd_t *cmd;
- esl_lib_log_api_info("Requested: Write Image" APP_LOG_NL);
+ esl_lib_log_api_debug("Requested: Write %u bytes of image data to slot %u " APP_LOG_NL,
+ (uint32_t)img_size, img_index);
if (connection_handle != ESL_LIB_INVALID_HANDLE) {
if (!esl_lib_connection_contains(connection_handle)) {
- return SL_STATUS_NOT_FOUND;
+ return SL_STATUS_BT_CTRL_UNKNOWN_CONNECTION_IDENTIFIER;
}
size_t size = ESL_LIB_COMMAND_LIST_HEADER_LEN
+ sizeof(esl_lib_command_list_cmd_write_image_t);
@@ -691,11 +696,11 @@ sl_status_t esl_lib_get_image_type(esl_lib_connection_handle_t connection_handle
sl_status_t sc = SL_STATUS_INVALID_HANDLE;
esl_lib_command_list_cmd_t *cmd;
- esl_lib_log_api_info("Requested: Get Image Type" APP_LOG_NL);
+ esl_lib_log_api_debug("Requested: Get Image Type of index %u" APP_LOG_NL, img_index);
if (connection_handle != ESL_LIB_INVALID_HANDLE) {
if (!esl_lib_connection_contains(connection_handle)) {
- return SL_STATUS_NOT_FOUND;
+ return SL_STATUS_BT_CTRL_UNKNOWN_CONNECTION_IDENTIFIER;
}
size_t size = ESL_LIB_COMMAND_LIST_HEADER_LEN
+ sizeof(esl_lib_command_list_cmd_get_image_type_t);
@@ -729,7 +734,7 @@ sl_status_t esl_lib_scan_configure(esl_lib_scan_parameters_t *params)
sl_status_t sc = SL_STATUS_NULL_POINTER;
esl_lib_command_list_cmd_t *cmd;
- esl_lib_log_api_info("Requested: Scan configure" APP_LOG_NL);
+ esl_lib_log_api_debug("Requested: Scan configure" APP_LOG_NL);
if (params != NULL) {
size_t size = ESL_LIB_COMMAND_LIST_HEADER_LEN
@@ -759,7 +764,7 @@ sl_status_t esl_lib_scan_enable(esl_lib_bool_t enable)
sl_status_t sc;
esl_lib_command_list_cmd_t *cmd;
- esl_lib_log_api_info("Requested: Scan enable" APP_LOG_NL);
+ esl_lib_log_api_debug("Requested: Scan enable" APP_LOG_NL);
size_t size = ESL_LIB_COMMAND_LIST_HEADER_LEN + sizeof(esl_lib_command_list_cmd_scan_enable_t);
// Allocate memory for the command
@@ -784,7 +789,7 @@ sl_status_t esl_lib_get_scan_status(void)
sl_status_t sc;
esl_lib_command_list_cmd_t *cmd;
- esl_lib_log_api_info("Requested: Get scan status" APP_LOG_NL);
+ esl_lib_log_api_debug("Requested: Get scan status" APP_LOG_NL);
size_t size = ESL_LIB_COMMAND_LIST_HEADER_LEN;
// Allocate memory for the command
@@ -808,20 +813,10 @@ sl_status_t esl_lib_general_cmd(uint8_t cmd_code,
sl_status_t sc;
esl_lib_command_list_cmd_t *cmd;
- esl_lib_log_api_info("Requested: General CMD" APP_LOG_NL);
+ esl_lib_log_api_debug("Requested: General CMD 0x%02x" APP_LOG_NL, cmd_code);
size_t size = ESL_LIB_COMMAND_LIST_HEADER_LEN;
switch (cmd_code) {
- case ESL_LIB_CMD_AP_CONTROL_INIT_GATTDB:
- // Allocate memory for the command
- cmd = (esl_lib_command_list_cmd_t *)esl_lib_memory_allocate(size);
- cmd->cmd_code = cmd_code;
- // Send command
- sc = esl_lib_core_add_command(cmd);
- if (sc != SL_STATUS_OK) {
- esl_lib_memory_free(cmd);
- }
- break;
case ESL_LIB_CMD_AP_CONTROL_ADV_ENABLE:
case ESL_LIB_CMD_AP_CONTROL_CP_RESPONSE:
case ESL_LIB_CMD_AP_CONTROL_IT_RESPONSE:
@@ -874,18 +869,19 @@ static void event_handler_step(void)
{
esl_lib_evt_t *last_evt = esl_lib_event_list_get_first();
if (last_evt != NULL) {
- if (filter_event(last_evt)) {
- esl_lib_log_api_debug("EVT found, type = %u" APP_LOG_NL, last_evt->evt_code);
+ const bool log_event = filter_event(last_evt);
+ if (log_event) {
+ esl_lib_log_api_debug("EVT emitted, type = %u" APP_LOG_NL, last_evt->evt_code);
}
if (event_handler_cb != NULL) {
- if (filter_event(last_evt)) {
- esl_lib_log_api_debug("EVT callback, type = %u" APP_LOG_NL, last_evt->evt_code);
+ if (log_event) {
+ esl_lib_log_api_debug("Calling EVT callback for type %u" APP_LOG_NL, last_evt->evt_code);
}
event_handler_cb(last_evt->evt_code, &(last_evt->data));
- if (filter_event(last_evt)) {
- esl_lib_log_api_debug("EVT callback, finished type = %u" APP_LOG_NL, last_evt->evt_code);
+ if (log_event) {
+ esl_lib_log_api_debug("EVT callback finished for type %u" APP_LOG_NL, last_evt->evt_code);
}
- esl_lib_event_list_remove_first();
}
+ esl_lib_event_list_remove_first();
}
}
diff --git a/app/bluetooth/common_host/esl_lib/esl_lib.h b/app/bluetooth/common_host/esl_lib/esl_lib.h
index 86ed8264461..a03ed9b53df 100644
--- a/app/bluetooth/common_host/esl_lib/esl_lib.h
+++ b/app/bluetooth/common_host/esl_lib/esl_lib.h
@@ -99,6 +99,8 @@ typedef enum esl_lib_node_id_type_e {
typedef enum esl_lib_connection_state_e {
ESL_LIB_CONNECTION_STATE_OFF,
ESL_LIB_CONNECTION_STATE_CONNECTING,
+ ESL_LIB_CONNECTION_STATE_RECONNECTING,
+ ESL_LIB_CONNECTION_STATE_CONNECTION_OPENED,
ESL_LIB_CONNECTION_STATE_APPLYING_LTK,
ESL_LIB_CONNECTION_STATE_NEW_BOND_REQUIRED,
ESL_LIB_CONNECTION_STATE_BONDING,
@@ -139,8 +141,13 @@ typedef void *esl_lib_connection_handle_t;
/// Node address type
typedef struct esl_lib_address_s {
- uint8_t addr[6]; ///< @brief Bluetooth address in reverse byte order
- uint8_t addr_type; ///< @brief Bluetooth address type
+ union {
+ struct {
+ uint8_t addr[6]; ///< @brief Bluetooth address in reverse byte order */
+ } address;
+ uint8_t addr[6]; ///< @brief alternative access to the bd_addr address
+ };
+ uint8_t address_type; ///< @brief Bluetooth address type
} esl_lib_address_t;
typedef struct esl_lib_pawr_subevent_s {
@@ -215,6 +222,7 @@ typedef enum esl_lib_evt_type_e {
ESL_LIB_EVT_CONNECTION_CLOSED,
ESL_LIB_EVT_CONNECTION_OPENED,
ESL_LIB_EVT_BONDING_DATA,
+ ESL_LIB_EVT_BONDING_FINISHED,
ESL_LIB_EVT_PAWR_STATUS,
ESL_LIB_EVT_PAWR_RESPONSE,
ESL_LIB_EVT_PAWR_DATA_REQUEST,
@@ -230,6 +238,8 @@ typedef enum esl_lib_evt_type_e {
/// ESL host library event status codes
typedef enum esl_lib_status_e {
ESL_LIB_STATUS_NO_ERROR,
+ ESL_LIB_STATUS_UNSPECIFIED_ERROR,
+ ESL_LIB_STATUS_UNASSOCITED,
ESL_LIB_STATUS_GATT_TIMEOUT,
ESL_LIB_STATUS_BONDING_FAILED,
ESL_LIB_STATUS_FEATURE_NOT_SUPPORTED,
@@ -266,7 +276,8 @@ typedef enum esl_lib_status_e {
ESL_LIB_STATUS_PAST_INIT_FAILED,
ESL_LIB_STATUS_CONN_WRITE_CP_FAILED,
ESL_LIB_STATUS_CONN_TAG_CONFIG_FAILED,
- ESL_LIB_STATUS_CONTROL_FAILED
+ ESL_LIB_STATUS_CONTROL_FAILED,
+ ESL_LIB_STATUS_UNKNOWN_COMMAND,
} esl_lib_status_t;
// -------------------------------
@@ -414,6 +425,12 @@ typedef struct esl_lib_evt_bonding_data_s {
uint8_t ltk[ESL_LIB_LTK_SIZE]; ///< LTK data
} esl_lib_evt_bonding_data_t;
+/// Bonding finished event
+typedef struct esl_lib_evt_bonding_finished_s {
+ esl_lib_connection_handle_t connection_handle; ///< Connection handle
+ esl_lib_address_t address; ///< BLE address
+} esl_lib_evt_bonding_finished_t;
+
/// PAwR status event
typedef struct esl_lib_evt_pawr_status_s {
esl_lib_pawr_handle_t pawr_handle; ///< PAwR handle
@@ -485,6 +502,7 @@ typedef union esl_lib_evt_data_u {
esl_lib_evt_connection_closed_t evt_connection_closed; ///< Connection closed
esl_lib_evt_connection_opened_t evt_connection_opened; ///< Connection opened
esl_lib_evt_bonding_data_t evt_bonding_data; ///< Bonding data
+ esl_lib_evt_bonding_finished_t evt_bonding_finished; ///< Bonding finished
esl_lib_evt_image_transfer_finished_t evt_image_transfer_finished; ///< Image transfer finished
esl_lib_evt_image_type_t evt_image_type; ///< Image type received
esl_lib_evt_pawr_status_t evt_pawr_status; ///< PAwR status
@@ -678,6 +696,7 @@ sl_status_t esl_lib_pawr_enable(esl_lib_pawr_handle_t pawr_handle,
*****************************************************************************/
sl_status_t esl_lib_pawr_set_data(esl_lib_pawr_handle_t pawr_handle,
uint8_t subevent,
+ uint8_t response_slot_max,
esl_lib_array_t *payload);
/**************************************************************************//**
diff --git a/app/bluetooth/common_host/esl_lib/esl_lib_ap_control.c b/app/bluetooth/common_host/esl_lib/esl_lib_ap_control.c
index f000bb9fd6e..ffe67c5c080 100644
--- a/app/bluetooth/common_host/esl_lib/esl_lib_ap_control.c
+++ b/app/bluetooth/common_host/esl_lib/esl_lib_ap_control.c
@@ -62,10 +62,11 @@
return sc; \
}
-#define CHECK_IN_SESSION(sc) \
+#define CHECK_IN_SESSION(sc, ptr) \
if (sc != SL_STATUS_OK) { \
esl_lib_log_ap_control_error("AP control error: %04x" APP_LOG_NL, sc); \
(void)sl_bt_gattdb_abort(session); \
+ esl_lib_memory_free(ptr); \
return sc; \
}
@@ -113,8 +114,9 @@ sl_status_t esl_lib_ap_control_cleanup(void)
if (ap_control.conn_handle != SL_BT_INVALID_CONNECTION_HANDLE) {
(void)sl_bt_connection_close(ap_control.conn_handle);
}
- (void)esl_lib_storage_delete(ap_control.cp_storage);
+ (void)esl_lib_storage_delete(&ap_control.cp_storage);
(void)esl_lib_ap_control_adv_enable(false);
+ esl_lib_log_ap_control_debug("AP Control cleanup complete" APP_LOG_NL);
return SL_STATUS_OK;
}
@@ -131,7 +133,7 @@ sl_status_t esl_lib_ap_control_init(void)
uint16_t service_di;
uint16_t characteristic;
- esl_lib_log_ap_control_info("Initializing AP Control" APP_LOG_NL);
+ esl_lib_log_ap_control_debug("Initializing AP Control" APP_LOG_NL);
ap_control.conn_handle = SL_BT_INVALID_CONNECTION_HANDLE;
ap_control.cp_handle = ESL_LIB_INVALID_CHARACTERISTIC_HANDLE;
@@ -141,11 +143,17 @@ sl_status_t esl_lib_ap_control_init(void)
sc = esl_lib_storage_create(&ap_control.cp_storage);
CHECK(sc);
- esl_lib_log_ap_control_debug("Storage created" APP_LOG_NL);
+ esl_lib_log_ap_control_debug("AP control storage created" APP_LOG_NL);
sc = sl_bt_gattdb_new_session(&session);
- CHECK(sc);
- esl_lib_log_ap_control_debug("Session created" APP_LOG_NL);
+
+ if (sc != SL_STATUS_OK) {
+ esl_lib_log_ap_control_error("AP control GATT session create failed: %04x" APP_LOG_NL, sc);
+ esl_lib_memory_free(ap_control.cp_storage);
+ return sc;
+ }
+
+ esl_lib_log_ap_control_debug("AP control GATT session created" APP_LOG_NL);
// ESL AP control service
uint8_t service_uuid[] = ESL_LIB_AP_CONTROL_SERVICE_UUID;
@@ -155,7 +163,7 @@ sl_status_t esl_lib_ap_control_init(void)
16,
service_uuid,
&service_custom);
- CHECK_IN_SESSION(sc);
+ CHECK_IN_SESSION(sc, ap_control.cp_storage);
// ESL AP control point
uuid_128 ap_control_cp_uuid = {
@@ -174,7 +182,7 @@ sl_status_t esl_lib_ap_control_init(void)
0,
NULL,
&ap_control.cp_handle);
- CHECK_IN_SESSION(sc);
+ CHECK_IN_SESSION(sc, ap_control.cp_storage);
// ESL AP Control Image Transfer
uuid_128 ap_control_it_uuid = {
@@ -193,11 +201,11 @@ sl_status_t esl_lib_ap_control_init(void)
0,
NULL,
&ap_control.it_handle);
- CHECK_IN_SESSION(sc);
+ CHECK_IN_SESSION(sc, ap_control.cp_storage);
sc = sl_bt_gattdb_start_service(session, service_custom);
- CHECK_IN_SESSION(sc);
- esl_lib_log_ap_control_debug("Custom service created" APP_LOG_NL);
+ CHECK_IN_SESSION(sc, ap_control.cp_storage);
+ esl_lib_log_ap_control_debug("AP control custom service created" APP_LOG_NL);
// Generic Access
uint8_t ga_uuid[] = { 0x00, 0x18 };
@@ -207,7 +215,7 @@ sl_status_t esl_lib_ap_control_init(void)
2,
ga_uuid,
&service_ga);
- CHECK_IN_SESSION(sc);
+ CHECK_IN_SESSION(sc, ap_control.cp_storage);
// Device Name
sl_bt_uuid_16_t device_name_uuid = {
@@ -224,7 +232,7 @@ sl_status_t esl_lib_ap_control_init(void)
strlen(ESL_LIB_AP_CONTROL_DEVICE_NAME),
(uint8_t *)ESL_LIB_AP_CONTROL_DEVICE_NAME,
&characteristic);
- CHECK_IN_SESSION(sc);
+ CHECK_IN_SESSION(sc, ap_control.cp_storage);
// Appearance
sl_bt_uuid_16_t apperance_uuid = {
@@ -241,11 +249,11 @@ sl_status_t esl_lib_ap_control_init(void)
2,
(uint8_t *)"\x01\x05",
&characteristic);
- CHECK_IN_SESSION(sc);
+ CHECK_IN_SESSION(sc, ap_control.cp_storage);
sc = sl_bt_gattdb_start_service(session, service_ga);
- CHECK_IN_SESSION(sc);
- esl_lib_log_ap_control_debug("Generic Access service created" APP_LOG_NL);
+ CHECK_IN_SESSION(sc, ap_control.cp_storage);
+ esl_lib_log_ap_control_debug("AP control Generic Access Service created" APP_LOG_NL);
// Device Information
uint8_t di_uuid[] = { 0x0A, 0x18 };
@@ -255,7 +263,7 @@ sl_status_t esl_lib_ap_control_init(void)
2,
di_uuid,
&service_di);
- CHECK_IN_SESSION(sc);
+ CHECK_IN_SESSION(sc, ap_control.cp_storage);
// Manufacturer Name String
sl_bt_uuid_16_t mns_uuid = {
@@ -272,12 +280,12 @@ sl_status_t esl_lib_ap_control_init(void)
strlen(ESL_LIB_AP_CONTROL_MANUFACTURER_NAME_STRING),
(uint8_t *)ESL_LIB_AP_CONTROL_MANUFACTURER_NAME_STRING,
&characteristic);
- CHECK_IN_SESSION(sc);
+ CHECK_IN_SESSION(sc, ap_control.cp_storage);
// System ID
// Extract unique ID from BT Address.
sc = sl_bt_system_get_identity_address(&address, &address_type);
- CHECK_IN_SESSION(sc);
+ CHECK_IN_SESSION(sc, ap_control.cp_storage);
// Pad and reverse unique ID to get System ID.
system_id[0] = address.addr[5];
@@ -303,20 +311,20 @@ sl_status_t esl_lib_ap_control_init(void)
8,
system_id,
&characteristic);
- CHECK_IN_SESSION(sc);
+ CHECK_IN_SESSION(sc, ap_control.cp_storage);
sc = sl_bt_gattdb_start_service(session, service_di);
- CHECK_IN_SESSION(sc);
+ CHECK_IN_SESSION(sc, ap_control.cp_storage);
- esl_lib_log_ap_control_debug("Device Information service created" APP_LOG_NL);
+ esl_lib_log_ap_control_debug("AP control Device Information Service created" APP_LOG_NL);
sc = sl_bt_gattdb_commit(session);
if (sc == SL_STATUS_OK) {
ap_control.enabled = true;
- esl_lib_log_ap_control_info("AP control initialized " APP_LOG_NL);
+ esl_lib_log_ap_control_debug("AP control initialized " APP_LOG_NL);
} else {
- esl_lib_log_ap_control_error("AP control GATTDB commit failed = %04x" APP_LOG_NL, sc);
+ esl_lib_log_ap_control_error("AP control GATTDB commit failed = 0x%04x" APP_LOG_NL, sc);
}
return sc;
@@ -326,12 +334,14 @@ sl_status_t esl_lib_ap_control_adv_enable(bool enable)
{
sl_status_t sc = SL_STATUS_OK;
- if (!ap_control.enabled) {
- return SL_STATUS_NOT_INITIALIZED;
+ if (!ap_control.enabled && enable) {
+ sc = esl_lib_ap_control_init();
}
+ CHECK(sc);
+
if (enable) {
- esl_lib_log_ap_control_info("Enabling advertising = %d" APP_LOG_NL, enable);
+ esl_lib_log_ap_control_debug("Enabling advertising = %d" APP_LOG_NL, enable);
sc = sl_bt_advertiser_create_set(&ap_control.adv_handle);
CHECK(sc);
@@ -351,7 +361,7 @@ sl_status_t esl_lib_ap_control_adv_enable(bool enable)
sl_bt_legacy_advertiser_connectable);
CHECK(sc);
} else {
- esl_lib_log_ap_control_info("Disabling advertising = %d" APP_LOG_NL, enable);
+ esl_lib_log_ap_control_debug("Disabling advertising = %d" APP_LOG_NL, enable);
if (ap_control.adv_handle != SL_BT_INVALID_ADVERTISING_SET_HANDLE) {
sc = sl_bt_advertiser_stop(ap_control.adv_handle);
CHECK(sc);
@@ -360,7 +370,7 @@ sl_status_t esl_lib_ap_control_adv_enable(bool enable)
ap_control.adv_handle = SL_BT_INVALID_ADVERTISING_SET_HANDLE;
}
}
- esl_lib_log_ap_control_info("Advertising = %d" APP_LOG_NL, enable);
+ esl_lib_log_ap_control_debug("Advertising = %d" APP_LOG_NL, enable);
return sc;
}
@@ -497,8 +507,8 @@ void esl_lib_ap_control_on_bt_event(sl_bt_msg_t *evt)
case sl_bt_evt_connection_opened_id:
if (evt->data.evt_connection_opened.master == PERIPHERAL_ROLE) {
- esl_lib_log_ap_control_info("Connection opened as peripheral: %d" APP_LOG_NL,
- evt->data.evt_connection_opened.connection);
+ esl_lib_log_ap_control_debug("Connection opened as peripheral: %d" APP_LOG_NL,
+ evt->data.evt_connection_opened.connection);
if (ap_control.conn_handle != SL_BT_INVALID_CONNECTION_HANDLE) {
// Close second connection
esl_lib_log_ap_control_warning("Closing second connection" APP_LOG_NL);
@@ -510,8 +520,8 @@ void esl_lib_ap_control_on_bt_event(sl_bt_msg_t *evt)
(void)send_event(ESL_LIB_AP_CONTROL_EVT_STATUS,
sizeof(ap_control.state),
(uint8_t *)&ap_control.state);
- esl_lib_log_ap_control_info("Connection saved as AP controller: %d" APP_LOG_NL,
- evt->data.evt_connection_opened.connection);
+ esl_lib_log_ap_control_debug("Connection saved as AP controller: %d" APP_LOG_NL,
+ evt->data.evt_connection_opened.connection);
}
}
break;
@@ -546,8 +556,8 @@ void esl_lib_ap_control_on_bt_event(sl_bt_msg_t *evt)
case sl_bt_evt_connection_closed_id:
if (evt->data.evt_connection_closed.connection == ap_control.conn_handle) {
- esl_lib_log_ap_control_info("Connection closed: %d" APP_LOG_NL,
- ap_control.conn_handle);
+ esl_lib_log_ap_control_debug("Connection closed: %d" APP_LOG_NL,
+ ap_control.conn_handle);
ap_control.conn_handle = SL_BT_INVALID_CONNECTION_HANDLE;
ap_control.state = ESL_LIB_AP_CONTROL_STATE_DISCONNECTED;
(void)send_event(ESL_LIB_AP_CONTROL_EVT_STATUS,
diff --git a/app/bluetooth/common_host/esl_lib/esl_lib_command_list.c b/app/bluetooth/common_host/esl_lib/esl_lib_command_list.c
index d907431c0be..2adfde7e3f2 100644
--- a/app/bluetooth/common_host/esl_lib/esl_lib_command_list.c
+++ b/app/bluetooth/common_host/esl_lib/esl_lib_command_list.c
@@ -84,7 +84,7 @@ void esl_lib_command_list_cleanup(sl_slist_node_t **list)
sl_slist_remove(list, &cmd->node);
esl_lib_memory_free(cmd);
}
+ *list = NULL;
}
- *list = NULL;
}
}
diff --git a/app/bluetooth/common_host/esl_lib/esl_lib_command_list.h b/app/bluetooth/common_host/esl_lib/esl_lib_command_list.h
index a613d602cb6..e5cb4111940 100644
--- a/app/bluetooth/common_host/esl_lib/esl_lib_command_list.h
+++ b/app/bluetooth/common_host/esl_lib/esl_lib_command_list.h
@@ -65,7 +65,6 @@ typedef enum {
ESL_LIB_CMD_WRITE_CONTROL_POINT = 12, // Write ESL Control Point
ESL_LIB_CMD_GET_SCAN_STATUS = 13, // Get scan status
ESL_LIB_CMD_GET_PAWR_STATUS = 14, // Get PAwR status
- ESL_LIB_CMD_AP_CONTROL_INIT_GATTDB = 100, // Initialize AP control GATT DB
ESL_LIB_CMD_AP_CONTROL_ADV_ENABLE = 101, // Enable/disable AP control advertising
ESL_LIB_CMD_AP_CONTROL_CP_RESPONSE = 102, // Notify AP control control point
ESL_LIB_CMD_AP_CONTROL_IT_RESPONSE = 103 // Notify AP control image transfer
@@ -90,8 +89,10 @@ typedef struct {
/// Connect parameters
typedef struct {
- esl_lib_address_t address; ///< Bluetooth address
- esl_lib_long_array_t tlv_data; ///< Data containing Connect TLVs
+ esl_lib_address_t address; ///< Bluetooth address
+ esl_lib_connection_handle_t conn_hnd; ///< Connection handle
+ uint8_t retries_left; ///< Number of remaining retries
+ esl_lib_long_array_t tlv_data; ///< Data containing Connect TLVs
} esl_lib_command_list_cmd_connect_t;
/// Write ESL Control Point
@@ -116,9 +117,11 @@ typedef struct {
/// Set PAwR data
typedef struct {
- esl_lib_pawr_handle_t pawr_handle; ///< PAwR handle
- uint8_t subevent; ///< Subevent
- esl_lib_array_t data; ///< Payload
+ esl_lib_pawr_handle_t pawr_handle; ///< PAwR handle
+ uint8_t subevent; ///< Subevent
+ uint8_t response_slot_max; ///< The number of response slots to be used
+ uint8_t retry; ///< retry count for set data
+ esl_lib_array_t data; ///< Payload
} esl_lib_command_list_cmd_pawr_set_data_t;
/// Set PAwR config
diff --git a/app/bluetooth/common_host/esl_lib/esl_lib_connection.c b/app/bluetooth/common_host/esl_lib/esl_lib_connection.c
index f940005eed4..6196cec83a2 100644
--- a/app/bluetooth/common_host/esl_lib/esl_lib_connection.c
+++ b/app/bluetooth/common_host/esl_lib/esl_lib_connection.c
@@ -48,8 +48,10 @@
#define GATT_OVERHEAD 7
#define PREFERRED_PHY sl_bt_gap_phy_2m
#define ACCEPTED_PHY sl_bt_gap_phy_any
+#define CLOSE_TIMEOUT_MS 2000
#define CONNECTION_TIMEOUT_MS 10000
-#define RECONNECT_TIMEOUT_MS 5
+#define BONDING_TIMEOUT_MS 15000
+#define RECONNECT_TIMEOUT_MS 250
#define GATT_TIMEOUT_MS 10000
// connection parameters for PAST
@@ -60,6 +62,7 @@
#define PAST_CONN_TIMEOUT 1000
#define PAST_CONN_MIN_CE_LENGTH 0
#define PAST_CONN_MAX_CE_LENGTH 0xffff
+#define PAST_GRACE_INTERVAL_COUNT 6
#define PAWR_SERVICE_DATA 42
@@ -79,6 +82,7 @@ typedef struct {
// -----------------------------------------------------------------------------
// Forward declaration of private functions
+static void esl_lib_connection_safe_remove_ptr(esl_lib_connection_t *ptr);
static void run_command(esl_lib_command_list_cmd_t *cmd);
static sl_status_t close_connection(esl_lib_connection_t *conn);
static sl_status_t send_connection_status(esl_lib_connection_t *conn,
@@ -89,6 +93,7 @@ static sl_status_t send_cp_notification_event(esl_lib_connection_t *conn,
uint8_t *data);
static sl_status_t send_bonding_data(esl_lib_connection_t *conn,
uint8_t *data);
+static sl_status_t send_bonding_finished(esl_lib_connection_t *conn);
static sl_status_t send_att_response(esl_lib_connection_t *conn,
esl_lib_evt_type_t type,
sl_status_t status);
@@ -123,7 +128,7 @@ static void on_image_transfer_status(esl_lib_image_transfer_handle_t handle,
sl_status_t result,
esl_lib_ots_gattdb_handles_t *gattdb_handles);
static sl_status_t get_next_tag_info(esl_lib_connection_t *conn);
-static sl_status_t get_tag_info_finish(esl_lib_connection_t *conn);
+static sl_status_t get_tag_info_finish(esl_lib_connection_t *conn, sl_status_t status);
static esl_lib_data_type_t get_next_type(esl_lib_data_type_t type);
static uint16_t get_handle_for_type(esl_lib_connection_t *conn,
esl_lib_data_type_t tag_info_type);
@@ -138,7 +143,7 @@ static sl_status_t write_next_config_value(esl_lib_connection_t *conn);
static bool find_tlv(esl_lib_command_list_cmd_t *cmd,
esl_lib_connect_data_type_t type,
esl_lib_connect_tlv_t **tlv_out);
-
+static void close_broken_connection(esl_lib_connection_t **conn);
// -----------------------------------------------------------------------------
// Private variables
@@ -204,16 +209,15 @@ sl_status_t esl_lib_connection_check_gattdb_handles(esl_lib_gattdb_handles_t *ga
return SL_STATUS_OK;
}
-sl_status_t esl_lib_connection_open(esl_lib_command_list_cmd_t *cmd,
- esl_lib_connection_t *handle)
+sl_status_t esl_lib_connection_open(esl_lib_command_list_cmd_t *cmd)
{
sl_status_t sc = SL_STATUS_OK;
esl_lib_address_t *identity = NULL;
bd_addr *identity_addr = NULL;
- esl_lib_connection_t *conn = handle;
+ esl_lib_connection_t *conn = cmd->data.cmd_connect.conn_hnd;
uint8_t connection_handle = SL_BT_INVALID_CONNECTION_HANDLE;
bd_addr *addr = NULL;
- uint8_t addr_type = 0;
+ uint8_t address_type = 0;
esl_lib_connect_tlv_t *tlv = NULL;
esl_lib_pawr_t *pawr = NULL;
uint8_t flags = 0;
@@ -227,29 +231,33 @@ sl_status_t esl_lib_connection_open(esl_lib_command_list_cmd_t *cmd,
return SL_STATUS_INVALID_PARAMETER;
}
- bool retry = false;
- if (conn != NULL) {
- // Retry
- retry = true;
- esl_lib_log_connection_warning("Retry %d / %d" APP_LOG_NL,
- (conn->retry_count + 1),
- ESL_LIB_CONNECTION_RETRY_COUNT_MAX);
+ if (conn != ESL_LIB_INVALID_HANDLE) {
+ // it's a retry attempt
+ uint8_t retry_count = 1 + ESL_LIB_CONNECTION_RETRY_COUNT_MAX - cmd->data.cmd_connect.retries_left;
+
+ esl_lib_log_connection_warning("Retry %d / %d connect to " ESL_LIB_LOG_ADDR_FORMAT APP_LOG_NL,
+ retry_count,
+ ESL_LIB_CONNECTION_RETRY_COUNT_MAX,
+ ESL_LIB_LOG_ADDR(cmd->data.cmd_connect.address));
+ conn->command = cmd; // re-assign command because retry timer cleared it
+ conn->command_complete = false;
+ } else {
+ esl_lib_log_connection_debug("Initiate new connection" APP_LOG_NL);
}
- esl_lib_log_connection_info("Opening connection" APP_LOG_NL);
- if (!retry || conn->retry_count < ESL_LIB_CONNECTION_RETRY_COUNT_MAX) {
+ if (cmd->data.cmd_connect.retries_left) {
// Set address
address = &cmd->data.cmd_connect.address;
addr = (bd_addr *)address->addr;
- addr_type = address->addr_type;
+ address_type = address->address_type;
// Check for identity
if (find_tlv(cmd, ESL_LIB_CONNECT_DATA_TYPE_IDENTITY_ADDRESS, &tlv)) {
identity = (esl_lib_address_t*)tlv->data.data;
- esl_lib_log_connection_debug("Setting identity to" ESL_LIB_LOG_ADDR_FORMAT APP_LOG_NL,
+ esl_lib_log_connection_debug("Setting identity to " ESL_LIB_LOG_ADDR_FORMAT APP_LOG_NL,
ESL_LIB_LOG_ADDR(*identity));
identity_addr = (bd_addr *)identity;
- sc = sl_bt_gap_set_identity_address(*identity_addr, identity->addr_type);
+ sc = sl_bt_gap_set_identity_address(*identity_addr, identity->address_type);
if (sc != SL_STATUS_OK) {
esl_lib_log_connection_error("Failed to set identity address, sc = 0x%04x" APP_LOG_NL, sc);
return sc;
@@ -289,31 +297,37 @@ sl_status_t esl_lib_connection_open(esl_lib_command_list_cmd_t *cmd,
// Connect using PAwR
esl_lib_pawr_subevent_t *pawr_sub = (esl_lib_pawr_subevent_t *)tlv->data.data;
pawr = (esl_lib_pawr_t *)pawr_sub->handle;
- esl_lib_log_connection_info("Opening connection using PAwR to" ESL_LIB_LOG_ADDR_FORMAT APP_LOG_NL,
- ESL_LIB_LOG_ADDR(*address));
+ esl_lib_log_connection_debug("Opening connection via PAwR handle %d subevent %d to " ESL_LIB_LOG_ADDR_FORMAT APP_LOG_NL,
+ pawr->pawr_handle,
+ pawr_sub->subevent,
+ ESL_LIB_LOG_ADDR(*address));
sc = sl_bt_pawr_advertiser_create_connection(pawr->pawr_handle,
pawr_sub->subevent,
*addr,
- addr_type,
+ address_type,
&connection_handle);
} else {
// Connect using the address only
- esl_lib_log_connection_info("Opening connection to" ESL_LIB_LOG_ADDR_FORMAT APP_LOG_NL,
- ESL_LIB_LOG_ADDR(*address));
+ esl_lib_log_connection_debug("Opening connection to " ESL_LIB_LOG_ADDR_FORMAT APP_LOG_NL,
+ ESL_LIB_LOG_ADDR(*address));
sc = sl_bt_connection_open(*addr,
- addr_type,
+ address_type,
sl_bt_gap_phy_1m,
&connection_handle);
}
if (sc == SL_STATUS_OK) {
// If not retry, add a new connection
- if (!retry) {
+ if (conn == ESL_LIB_INVALID_HANDLE) {
// Allocate and add the connection to the connection list.
sc = esl_lib_connection_add(connection_handle, &conn);
if (sc == SL_STATUS_OK) {
// Move to connecting state with no error present.
conn->state = ESL_LIB_CONNECTION_STATE_CONNECTING;
-
+ // Save address and type
+ memcpy(conn->address.addr,
+ addr->addr,
+ sizeof(conn->address.addr));
+ conn->address_type = address_type;
conn->gattdb_known = ESL_LIB_FALSE;
// Check for gattdb
if (find_tlv(cmd, ESL_LIB_CONNECT_DATA_TYPE_GATTDB_HANDLES, &tlv)) {
@@ -323,13 +337,13 @@ sl_status_t esl_lib_connection_open(esl_lib_command_list_cmd_t *cmd,
tlv->data.data,
sizeof(conn->gattdb_handles));
for (uint8_t i = 0; i < sizeof(conn->gattdb_handles.esl_characteristics) / sizeof(uint16_t); i++) {
- esl_lib_log_connection_debug(CONN_FMT "ESL %u characteristic handle = 0x%x" APP_LOG_NL,
+ esl_lib_log_connection_debug(CONN_FMT "ESL %u characteristic handle = 0x%02x" APP_LOG_NL,
conn,
i,
conn->gattdb_handles.esl_characteristics[i]);
}
for (uint8_t i = 0; i < sizeof(conn->gattdb_handles.dis_characteristics) / sizeof(uint16_t); i++) {
- esl_lib_log_connection_debug(CONN_FMT "DIS %u characteristic handle = 0x%x" APP_LOG_NL,
+ esl_lib_log_connection_debug(CONN_FMT "DIS %u characteristic handle = 0x%02x" APP_LOG_NL,
conn,
i,
conn->gattdb_handles.dis_characteristics[i]);
@@ -350,35 +364,49 @@ sl_status_t esl_lib_connection_open(esl_lib_command_list_cmd_t *cmd,
}
}
- // Calculate size for copy command
- size_t cmd_size = ESL_LIB_COMMAND_LIST_HEADER_LEN
- + sizeof(esl_lib_command_list_cmd_connect_t)
- + cmd->data.cmd_connect.tlv_data.len;
- // Allocate and copy connect command
- conn->command = esl_lib_memory_allocate(cmd_size);
- if (conn->command != NULL) {
- memcpy(conn->command, cmd, cmd_size);
- memcpy(conn->address.addr,
- addr->addr,
- sizeof(conn->address.addr));
- conn->command_complete = false;
- esl_lib_log_connection_info("Opening connection in progress to" ESL_LIB_LOG_ADDR_FORMAT APP_LOG_NL,
- ESL_LIB_LOG_ADDR(*address));
- } else {
- esl_lib_log_connection_error("Allocation error" APP_LOG_NL);
- sc = SL_STATUS_ALLOCATION_FAILED;
- (void)sl_bt_connection_close(connection_handle);
- }
+ // Pass the ownership of initial connect command to conn. handle on success - otherwise ap_core will free it
+ conn->command = cmd;
+ conn->command->data.cmd_connect.conn_hnd = conn;
+ conn->command_complete = false;
+ esl_lib_log_connection_debug(CONN_FMT "Pending new connection to " ESL_LIB_LOG_ADDR_FORMAT APP_LOG_NL,
+ conn,
+ ESL_LIB_LOG_ADDR(*address));
}
} else {
+ --cmd->data.cmd_connect.retries_left;
conn->connection_handle = connection_handle;
- conn->state = ESL_LIB_CONNECTION_STATE_CONNECTING;
- conn->retry_count++;
+ conn->state = ESL_LIB_CONNECTION_STATE_RECONNECTING;
+
+ sc = app_timer_start(&conn->timer,
+ CONNECTION_TIMEOUT_MS,
+ connection_timeout,
+ conn,
+ false);
+ if (sc != SL_STATUS_OK) {
+ esl_lib_log_connection_error("Connection timeout reinit failed, handle = %u as 0x%p. Closing." APP_LOG_NL,
+ conn->connection_handle,
+ conn);
+ (void)close_connection(conn);
+ }
+ }
+ } else {
+ esl_lib_log(((sc == SL_STATUS_BT_CTRL_CONNECTION_LIMIT_EXCEEDED) \
+ ? ESL_LIB_LOG_LEVEL_WARNING : ESL_LIB_LOG_LEVEL_ERROR),
+ ESL_LIB_LOG_MODULE_CONNECTION,
+ "Connection failed to " ESL_LIB_LOG_ADDR_FORMAT ", sc = 0x%02x" APP_LOG_NL,
+ ESL_LIB_LOG_ADDR(*address),
+ sc);
+ if (conn != NULL) {
+ (void)esl_lib_connection_remove_ptr(conn);
+ cmd->data.cmd_connect.conn_hnd = ESL_LIB_INVALID_HANDLE;
}
}
} else {
- esl_lib_log_connection_error("Connection failed" APP_LOG_NL);
+ esl_lib_log_connection_error("Connection failure, no more retry attempts" APP_LOG_NL);
sc = SL_STATUS_BT_CTRL_CONNECTION_FAILED_TO_BE_ESTABLISHED;
+ // Force removal of connection handle in case of no more retry
+ (void)esl_lib_connection_remove_ptr(conn);
+ cmd->data.cmd_connect.conn_hnd = ESL_LIB_INVALID_HANDLE;
}
return sc;
}
@@ -393,7 +421,7 @@ sl_status_t esl_lib_connection_add(uint8_t conn,
return SL_STATUS_NULL_POINTER;
}
- esl_lib_log_connection_debug("Adding connection handle = %d" APP_LOG_NL, conn);
+ esl_lib_log_connection_debug("Add ESL library handle for BLE connection handle = %d" APP_LOG_NL, conn);
// Check if it exists
sc = esl_lib_connection_find(conn, &ptr);
@@ -404,15 +432,9 @@ sl_status_t esl_lib_connection_add(uint8_t conn,
ptr = (esl_lib_connection_t *)esl_lib_memory_allocate(sizeof(esl_lib_connection_t));
if (ptr != NULL) {
*ptr_out = ptr;
+ memset(ptr, 0, sizeof(*ptr));
ptr->connection_handle = conn;
ptr->command_complete = true;
- ptr->command = NULL;
- ptr->command_list = NULL;
- ptr->last_error = SL_STATUS_OK;
- ptr->retry_count = 0;
- ptr->tag_info_data = NULL;
- ptr->tag_info_list = NULL;
- ptr->tag_info_type = ESL_LIB_DATA_TYPE_UNINITIALIZED;
sl_slist_push_back(&connection_list, &ptr->node);
sc = app_timer_start(&ptr->timer,
@@ -421,7 +443,7 @@ sl_status_t esl_lib_connection_add(uint8_t conn,
ptr,
false);
if (sc == SL_STATUS_OK) {
- esl_lib_log_connection_debug("Added connection handle = %u as %p" APP_LOG_NL, conn, ptr);
+ esl_lib_log_connection_debug("Added ESL library handle at 0x%p for connection handle = %u" APP_LOG_NL, ptr, conn);
}
} else {
sc = SL_STATUS_ALLOCATION_FAILED;
@@ -430,7 +452,7 @@ sl_status_t esl_lib_connection_add(uint8_t conn,
}
}
if (sc != SL_STATUS_OK) {
- esl_lib_log_connection_error("Add connection handle = %u failed = %04x" APP_LOG_NL, conn, sc);
+ esl_lib_log_connection_error("Add connection handle = %u failed = 0x%04x" APP_LOG_NL, conn, sc);
}
return sc;
@@ -453,58 +475,21 @@ sl_status_t esl_lib_connection_find(uint8_t conn,
}
}
+ *ptr_out = NULL;
return sc;
}
sl_status_t esl_lib_connection_remove_ptr(esl_lib_connection_t *ptr)
{
if (esl_lib_connection_contains(ptr)) {
- (void)app_timer_stop(&ptr->timer);
- (void)app_timer_stop(&ptr->gatt_timer);
- sl_slist_remove(&connection_list, &ptr->node);
- esl_lib_command_list_cleanup(&ptr->command_list);
- if (ptr->command != NULL) {
- esl_lib_memory_free(ptr->command);
- ptr->command = NULL;
- }
- clean_tag_info(ptr);
- esl_lib_log_connection_debug(CONN_FMT "Removed connection handle = %u" APP_LOG_NL, ptr, ptr->connection_handle);
- esl_lib_memory_free(ptr);
+ esl_lib_connection_safe_remove_ptr(ptr);
} else {
- esl_lib_log_connection_error(CONN_FMT "Remove connection failed: handle not found" APP_LOG_NL, ptr);
+ esl_lib_log_connection_warning(CONN_FMT "Failed to remove the connection: handle not found" APP_LOG_NL, ptr);
}
return SL_STATUS_OK;
}
-sl_status_t esl_lib_connection_remove_handle(uint8_t conn,
- esl_lib_connection_t **ptr_out)
-{
- sl_status_t sc;
- esl_lib_connection_t *ptr;
-
- // Check if it exists
- sc = esl_lib_connection_find(conn, &ptr);
- if (sc == SL_STATUS_OK) {
- (void)app_timer_stop(&ptr->timer);
- (void)app_timer_stop(&ptr->gatt_timer);
- sl_slist_remove(&connection_list, &ptr->node);
- esl_lib_command_list_cleanup(&ptr->command_list);
- if (ptr->command != NULL) {
- esl_lib_memory_free(ptr->command);
- }
- clean_tag_info(ptr);
- esl_lib_log_connection_debug(CONN_FMT "Removed connection handle = %u" APP_LOG_NL, ptr, ptr->connection_handle);
- esl_lib_memory_free(ptr);
- *ptr_out = ptr;
- }
- if (sc != SL_STATUS_OK) {
- esl_lib_log_connection_error("Remove connection failed, connection handle = %u, sc = %04x" APP_LOG_NL, conn, sc);
- }
-
- return sc;
-}
-
bool esl_lib_connection_contains(esl_lib_connection_t *ptr)
{
bool ret = false;
@@ -524,14 +509,11 @@ void esl_lib_connection_cleanup(void)
esl_lib_connection_t *conn;
// Clean connection list
while ((conn = (esl_lib_connection_t *)sl_slist_pop(&connection_list)) != NULL) {
- esl_lib_command_list_cleanup(&conn->command_list);
- sl_slist_remove(&connection_list, &conn->node);
- if (conn->command != NULL) {
- esl_lib_memory_free(conn->command);
- }
- clean_tag_info(conn);
- esl_lib_memory_free(conn);
+ // Close connection
+ (void)close_connection(conn);
+ (void)esl_lib_connection_safe_remove_ptr(conn);
}
+ esl_lib_log_connection_debug("Connection cleanup complete" APP_LOG_NL);
}
sl_status_t esl_lib_connection_add_command(esl_lib_connection_t *conn,
@@ -542,7 +524,7 @@ sl_status_t esl_lib_connection_add_command(esl_lib_connection_t *conn,
return SL_STATUS_NULL_POINTER;
}
if (!esl_lib_connection_contains(conn)) {
- return SL_STATUS_NOT_FOUND;
+ return SL_STATUS_BT_CTRL_UNKNOWN_CONNECTION_IDENTIFIER;
}
sc = esl_lib_command_list_put(&conn->command_list, cmd);
if (sc == SL_STATUS_OK) {
@@ -550,7 +532,7 @@ sl_status_t esl_lib_connection_add_command(esl_lib_connection_t *conn,
conn,
cmd->cmd_code);
} else {
- esl_lib_log_connection_error(CONN_FMT "Add command %d failed, sc = %04x" APP_LOG_NL,
+ esl_lib_log_connection_error(CONN_FMT "Add command %d failed, sc = 0x%04x" APP_LOG_NL,
conn,
cmd->cmd_code,
sc);
@@ -565,8 +547,9 @@ void esl_lib_connection_step(void)
SL_SLIST_FOR_EACH_ENTRY(connection_list, conn, esl_lib_connection_t, node) {
if (conn->command_complete) {
- // If there is an ongoing but complete command, remove that.
- if (conn->command != NULL) {
+ // If there is a running but complete command, remove it - except for ESL_LIB_CMD_CONNECT requests,
+ // which are inherited from esl_lib_core and are therefore handled slightly differently.
+ if ((conn->command != NULL) && (conn->command->cmd_code != ESL_LIB_CMD_CONNECT)) {
esl_lib_command_list_remove(&conn->command_list, conn->command);
conn->command = NULL;
}
@@ -590,7 +573,7 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
esl_lib_connection_t *conn = NULL;
uint8_t *bonding_data = NULL;
uint8_t bonding_data_len = 0;
- esl_lib_status_t lib_status = ESL_LIB_STATUS_NO_ERROR;
+ esl_lib_status_t lib_status = ESL_LIB_STATUS_UNSPECIFIED_ERROR;
esl_lib_address_t *addr = NULL;
esl_lib_pawr_t *pawr = NULL;
esl_lib_connect_tlv_t *tlv = NULL;
@@ -605,10 +588,11 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
sc = esl_lib_connection_find(evt->data.evt_connection_opened.connection,
&conn);
if (sc == SL_STATUS_OK) {
+ (void)app_timer_stop(&conn->timer);
esl_lib_log_connection_debug(CONN_FMT "Connection found, connection handle = %u" APP_LOG_NL,
conn,
conn->connection_handle);
- conn->last_error = SL_STATUS_OK;
+ conn->last_error = sc;
conn->address_type = evt->data.evt_connection_opened.address_type;
memcpy(conn->address.addr,
evt->data.evt_connection_opened.address.addr,
@@ -617,7 +601,6 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
(void)sl_bt_connection_set_preferred_phy(conn->connection_handle,
PREFERRED_PHY,
ACCEPTED_PHY);
- (void)app_timer_stop(&conn->timer);
// Check OOB
if (find_tlv(conn->command, ESL_LIB_CONNECT_DATA_TYPE_OOB_DATA, &tlv)) {
aes_key_128 *remote_random = (aes_key_128 *)&tlv->data.data[0];
@@ -634,9 +617,18 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
conn->connection_handle);
}
}
+ // Restart timer to guard the bonding procedure
+ (void)app_timer_start(&conn->timer,
+ BONDING_TIMEOUT_MS,
+ connection_timeout,
+ conn,
+ false);
} else {
+ // Suppress error event for unknown connections
sc = SL_STATUS_OK;
}
+ esl_lib_core_connection_complete();
+ conn->state = ESL_LIB_CONNECTION_STATE_CONNECTION_OPENED;
}
break;
case sl_bt_evt_connection_closed_id:
@@ -647,12 +639,12 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
&conn);
if (sc == SL_STATUS_OK) {
sl_status_t reason = evt->data.evt_connection_closed.reason;
- esl_lib_log_connection_debug(CONN_FMT "Connection closing, connection handle = %u" APP_LOG_NL,
- conn,
- conn->connection_handle);
// Stop connection / reconnection timer
(void)app_timer_stop(&conn->timer);
(void)app_timer_stop(&conn->gatt_timer);
+ esl_lib_log_connection_debug(CONN_FMT "Connection closing, connection handle = %u" APP_LOG_NL,
+ conn,
+ conn->connection_handle);
if (check_connected(conn)) {
esl_lib_log_connection_debug(CONN_FMT "Removing connection, connection handle = %u" APP_LOG_NL,
conn,
@@ -669,9 +661,10 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
// And also remove connection from the list.
(void)esl_lib_connection_remove_ptr(conn);
conn = NULL;
- } else {
+ } else if (conn->command != NULL) {
// Not connected, check if a retry is required (link issue or bonding issue)
- if ((conn->retry_count < ESL_LIB_CONNECTION_RETRY_COUNT_MAX)
+ if ((conn->command->cmd_code == ESL_LIB_CMD_CONNECT)
+ && (conn->command->data.cmd_connect.retries_left)
&& ((reason == SL_STATUS_BT_CTRL_CONNECTION_FAILED_TO_BE_ESTABLISHED)
|| (conn->state == ESL_LIB_CONNECTION_STATE_BONDING_FAIL_RECONNECT))) {
esl_lib_log_connection_debug(CONN_FMT "Connection retry scheduled, connection handle = %u" APP_LOG_NL,
@@ -682,7 +675,16 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
if (find_tlv(conn->command, ESL_LIB_CONNECT_DATA_TYPE_PAWR, &tlv)) {
esl_lib_pawr_subevent_t *pawr_sub = (esl_lib_pawr_subevent_t *)tlv->data.data;
esl_lib_pawr_t *pawr = (esl_lib_pawr_t *)pawr_sub->handle;
- timeout = pawr->config.adv_interval.max * 1.5f;
+ timeout = (pawr->config.adv_interval.max << 1) - (pawr->config.adv_interval.max >> 3); // = 1.5f * (pawr->config.adv_interval.max * 1.25f) [ms]
+ }
+
+ if (reason == SL_STATUS_BT_CTRL_CONNECTION_FAILED_TO_BE_ESTABLISHED
+ && (conn->state == ESL_LIB_CONNECTION_STATE_CONNECTING
+ || conn->state == ESL_LIB_CONNECTION_STATE_RECONNECTING)) {
+ // If a connection request via PAwR times out, the sl_bt_evt_connection_closed_id
+ // event occurs without the preceding sl_bt_evt_connection_opened_id event!
+ conn->command_complete = true;
+ esl_lib_core_connection_complete();
}
// Schedule a reconnection to let tag process previous operation
sc = app_timer_start(&conn->timer,
@@ -699,28 +701,39 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
conn,
conn->connection_handle);
conn->command_complete = true;
- // And also remove connection from the list.
- (void)esl_lib_connection_remove_ptr(conn);
- conn = NULL;
+ } else {
+ break;
}
} else {
- if (conn->retry_count >= ESL_LIB_CONNECTION_RETRY_COUNT_MAX) {
+ // check again for the out-of-retries reason - set SL_STATUS_ABORT in case
+ if (conn->command->cmd_code == ESL_LIB_CMD_CONNECT && conn->command->data.cmd_connect.retries_left == 0) {
reason = SL_STATUS_ABORT;
+ esl_lib_log_connection_debug(CONN_FMT "No more connect retry for " ESL_LIB_LOG_ADDR_FORMAT ", last handle = %u" APP_LOG_NL,
+ conn,
+ conn->address_type,
+ (conn->address_type ? "random" : "public"),
+ ESL_LIB_LOG_BD_ADDR(conn->address),
+ conn->connection_handle);
}
(void)send_connection_error(conn,
ESL_LIB_STATUS_CONN_FAILED,
reason,
conn->state);
- esl_lib_log_connection_debug(CONN_FMT "No more retry, connection handle = %u" APP_LOG_NL,
- conn,
- conn->connection_handle);
- conn->command_complete = true;
- // And also remove connection from the list.
- (void)esl_lib_connection_remove_ptr(conn);
- conn = NULL;
+ if ((conn->state == ESL_LIB_CONNECTION_STATE_CONNECTING
+ || conn->state == ESL_LIB_CONNECTION_STATE_RECONNECTING)
+ && find_tlv(conn->command, ESL_LIB_CONNECT_DATA_TYPE_PAWR, &tlv)) {
+ // If a connection request via PAwR times out, the sl_bt_evt_connection_closed_id
+ // event occurs without the preceding sl_bt_evt_connection_opened_id event!
+ conn->command_complete = true;
+ esl_lib_core_connection_complete();
+ }
}
+ // And also remove connection from the list in the end.
+ (void)esl_lib_connection_remove_ptr(conn);
+ conn = NULL;
}
} else {
+ // Suppress error event for unknown connections
sc = SL_STATUS_OK;
}
break;
@@ -739,6 +752,8 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
if (evt->data.evt_connection_parameters.security_mode > sl_bt_connection_mode1_level1) {
if (conn->state == ESL_LIB_CONNECTION_STATE_BONDING
|| conn->state == ESL_LIB_CONNECTION_STATE_APPLYING_LTK) {
+ (void)app_timer_stop(&conn->timer);
+ (void)send_bonding_finished(conn);
if (conn->gattdb_known == ESL_LIB_TRUE) {
esl_lib_log_connection_debug(CONN_FMT "GATTDB known, skipping discovery, connection handle = %u" APP_LOG_NL,
conn,
@@ -758,7 +773,7 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
if (sc == SL_STATUS_OK) {
conn->state = ESL_LIB_CONNECTION_STATE_ESL_SUBSCRIBE;
} else {
- esl_lib_log_connection_error(CONN_FMT "ESL CP subscribe failed, connection handle = %u, sc = %04x" APP_LOG_NL,
+ esl_lib_log_connection_error(CONN_FMT "ESL CP subscribe failed, connection handle = %u, sc = 0x%04x" APP_LOG_NL,
conn,
conn->connection_handle,
sc);
@@ -774,9 +789,15 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
// No predefined GATT database, start service discovery
sc = sl_bt_gatt_discover_primary_services(conn->connection_handle);
if (sc == SL_STATUS_OK) {
+ (void)app_timer_stop(&conn->gatt_timer);
conn->state = ESL_LIB_CONNECTION_STATE_SERVICE_DISCOVERY;
+ sc = app_timer_start(&conn->gatt_timer,
+ GATT_TIMEOUT_MS,
+ gatt_timeout,
+ conn,
+ false);
} else {
- esl_lib_log_connection_error(CONN_FMT "Error starting service discovery, connection handle = %u, sc = %04x" APP_LOG_NL,
+ esl_lib_log_connection_error(CONN_FMT "Error starting service discovery, connection handle = %u, sc = 0x%04x" APP_LOG_NL,
conn,
conn->connection_handle,
sc);
@@ -796,22 +817,37 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
PAWR_SERVICE_DATA,
pawr->pawr_handle);
if (sc == SL_STATUS_OK) {
+ // calculate timeout as follows: tmeout_value_ms = 6 * (pawr->config.adv_interval.max * 1.25f) [ms]
+ uint32_t past_timeout = PAST_GRACE_INTERVAL_COUNT \
+ * ((pawr->config.adv_interval.max) + (pawr->config.adv_interval.max >> 2));
+ (void)app_timer_stop(&conn->timer);
conn->state = ESL_LIB_CONNECTION_STATE_PAST_CLOSE_CONNECTION;
- } else {
+ sc = app_timer_start(&conn->timer,
+ past_timeout,
+ connection_timeout,
+ conn,
+ false);
+ }
+ if (sc != SL_STATUS_OK) {
lib_status = ESL_LIB_STATUS_PAST_INIT_FAILED;
}
} else {
sc = SL_STATUS_NOT_FOUND;
lib_status = ESL_LIB_STATUS_PAST_INIT_FAILED;
- esl_lib_log_connection_error(CONN_FMT "Error in PAST init, connection handle = %u, PAwR = [%p] sc = %04x" APP_LOG_NL,
- conn,
- conn->connection_handle,
- conn->command->data.cmd_init_past.pawr_handle,
- sc);
+ }
+
+ if (sc != SL_STATUS_OK) {
+ esl_lib_log_connection_warning(CONN_FMT "PAST init unsuccesful, connection handle = %u, PAwR = [0x%p] sc = 0x%04x" APP_LOG_NL,
+ conn,
+ conn->connection_handle,
+ conn->command->data.cmd_init_past.pawr_handle,
+ sc);
+ close_broken_connection(&conn);
}
}
}
} else {
+ // Suppress error event for unknown connections
sc = SL_STATUS_OK;
}
break;
@@ -825,7 +861,7 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
esl_lib_log_connection_debug(CONN_FMT "Bonding LTK requested, connection handle = %u" APP_LOG_NL,
conn,
conn->connection_handle);
- if (conn->state == ESL_LIB_CONNECTION_STATE_CONNECTING) {
+ if (conn->state == ESL_LIB_CONNECTION_STATE_CONNECTION_OPENED) {
// Check if LTK is set for the request
if (find_tlv(conn->command, ESL_LIB_CONNECT_DATA_TYPE_LTK, &tlv)) {
bonding_data = tlv->data.data;
@@ -852,6 +888,18 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
bonding_data);
if (sc == SL_STATUS_OK) {
lib_status = ESL_LIB_STATUS_NO_ERROR;
+ } else {
+ // Defer forced close on error - normally the close event should come, this is just a watchdog
+ (void)app_timer_stop(&conn->timer);
+ (void)app_timer_start(&conn->timer,
+ CLOSE_TIMEOUT_MS,
+ connection_timeout,
+ conn,
+ false);
+ esl_lib_log_connection_error(CONN_FMT "Bonding procedure disrupted, connection handle = %u, sc = 0x%04x" APP_LOG_NL,
+ conn,
+ conn->connection_handle,
+ sc);
}
break;
case sl_bt_evt_sm_passkey_request_id:
@@ -870,9 +918,11 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
conn->connection_handle);
if (sc != SL_STATUS_OK) {
lib_status = ESL_LIB_STATUS_BONDING_FAILED;
- esl_lib_log_connection_error(CONN_FMT "Failed to set passkey, connection handle = %u" APP_LOG_NL,
+ esl_lib_log_connection_error(CONN_FMT "Failed to set passkey, connection handle = %u. Closing." APP_LOG_NL,
conn,
conn->connection_handle);
+ // Close the connection in case of error.
+ (void)sl_bt_connection_close(conn->connection_handle);
}
} else {
esl_lib_log_connection_error(CONN_FMT "No passkey available but requested, connection handle = %u" APP_LOG_NL,
@@ -880,6 +930,7 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
conn->connection_handle);
}
} else {
+ // Suppress error event for unknown connections
sc = SL_STATUS_OK;
}
break;
@@ -897,12 +948,13 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
// Close the connection in case of error.
(void)sl_bt_connection_close(conn->connection_handle);
lib_status = ESL_LIB_STATUS_BONDING_FAILED;
- esl_lib_log_connection_error(CONN_FMT "Increase security failed, connection handle = %u, sc = %04x" APP_LOG_NL,
+ esl_lib_log_connection_error(CONN_FMT "Increase security failed, connection handle = %u, sc = 0x%04x" APP_LOG_NL,
conn,
conn->connection_handle,
sc);
}
} else {
+ // Suppress error event for unknown connections
sc = SL_STATUS_OK;
}
break;
@@ -930,13 +982,13 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
sc = esl_lib_connection_find(evt->data.evt_sm_bonding_failed.connection,
&conn);
if (sc == SL_STATUS_OK) {
- if ((conn->retry_count < ESL_LIB_CONNECTION_RETRY_COUNT_MAX)
+ if ((conn->command->data.cmd_connect.retries_left)
&& ((evt->data.evt_sm_bonding_failed.reason == SL_STATUS_BT_CTRL_PIN_OR_KEY_MISSING)
|| (evt->data.evt_sm_bonding_failed.reason == SL_STATUS_BT_SMP_PAIRING_NOT_SUPPORTED))) {
- esl_lib_log_connection_error(CONN_FMT "Bonding failed, reconnecting, connection handle = %u, reason = %04x" APP_LOG_NL,
- conn,
- conn->connection_handle,
- evt->data.evt_sm_bonding_failed.reason);
+ esl_lib_log_connection_warning(CONN_FMT "Bonding failed, reconnecting, connection handle = %u, reason = 0x%04x" APP_LOG_NL,
+ conn,
+ conn->connection_handle,
+ evt->data.evt_sm_bonding_failed.reason);
if (evt->data.evt_sm_bonding_failed.reason == SL_STATUS_BT_CTRL_PIN_OR_KEY_MISSING) {
// Remove LTK if present
esl_lib_connect_tlv_t *tlv;
@@ -953,7 +1005,7 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
sc = SL_STATUS_OK;
lib_status = ESL_LIB_STATUS_NO_ERROR;
} else {
- esl_lib_log_connection_error(CONN_FMT "Bonding failed, disconnecting, connection handle = %u, reason = %04x" APP_LOG_NL,
+ esl_lib_log_connection_error(CONN_FMT "Bonding failed, disconnecting, connection handle = %u, reason = 0x%04x" APP_LOG_NL,
conn,
conn->connection_handle,
evt->data.evt_sm_bonding_failed.reason);
@@ -965,6 +1017,7 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
// Try to close connection
(void)close_connection(conn);
} else {
+ // Suppress error event for unknown connections
sc = SL_STATUS_OK;
}
break;
@@ -1033,6 +1086,7 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
}
}
} else {
+ // Suppress error event for unknown connections
sc = SL_STATUS_OK;
}
break;
@@ -1041,8 +1095,7 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
sc = esl_lib_connection_find(evt->data.evt_gatt_characteristic_value.connection,
&conn);
if (sc == SL_STATUS_OK) {
- if (evt->data.evt_gatt_characteristic_value.att_opcode
- == sl_bt_gatt_read_response) {
+ if (evt->data.evt_gatt_characteristic_value.att_opcode == sl_bt_gatt_read_response) {
esl_lib_log_connection_debug(CONN_FMT "Read response, connection handle = %u" APP_LOG_NL,
conn,
conn->connection_handle);
@@ -1054,7 +1107,7 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
lib_status = ESL_LIB_STATUS_NO_ERROR;
}
}
- } else if (evt->data.evt_gatt_characteristic_value.att_opcode & sl_bt_gatt_notification) {
+ } else if (evt->data.evt_gatt_characteristic_value.att_opcode == sl_bt_gatt_handle_value_notification) {
// Notification arrived
if (evt->data.evt_gatt_characteristic_value.characteristic
== conn->gattdb_handles.esl_characteristics[ESL_LIB_CHARACTERISTIC_INDEX_ESL_CONTROL_POINT]) {
@@ -1067,6 +1120,7 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
}
}
} else {
+ // Suppress error event for unknown connections
sc = SL_STATUS_OK;
}
break;
@@ -1076,8 +1130,6 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
if (sc == SL_STATUS_OK) {
if (conn->state == ESL_LIB_CONNECTION_STATE_WRITE_CONTROL_POINT) {
// This state is active only if write with response requested
- // Stop timer
- (void)app_timer_stop(&conn->gatt_timer);
// Send response with the result
(void)send_att_response(conn,
ESL_LIB_EVT_CONTROL_POINT_RESPONSE,
@@ -1089,8 +1141,6 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
conn->connection_handle);
} else if (conn->state == ESL_LIB_CONNECTION_STATE_CONFIGURE_TAG) {
// This state is active only if write with response requested
- // Stop timer
- (void)app_timer_stop(&conn->gatt_timer);
esl_lib_log_connection_debug(CONN_FMT "Configure tag - Writing next value, connection handle = %u" APP_LOG_NL,
conn,
conn->connection_handle);
@@ -1125,10 +1175,11 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
lib_status = ESL_LIB_STATUS_NO_ERROR;
}
} else {
- sc = get_tag_info_finish(conn);
+ sc = get_tag_info_finish(conn, sc);
}
break;
case ESL_LIB_CONNECTION_STATE_SERVICE_DISCOVERY:
+ (void)app_timer_stop(&conn->gatt_timer);
// If DIS found
if (conn->gattdb_handles.services.dis != ESL_LIB_INVALID_SERVICE_HANDLE) {
esl_lib_log_connection_debug(CONN_FMT "Service discovery finished, start DIS discovery, connection handle = %u" APP_LOG_NL,
@@ -1139,6 +1190,20 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
conn->gattdb_handles.services.dis);
if (sc == SL_STATUS_OK) {
conn->state = ESL_LIB_CONNECTION_STATE_DIS_DISCOVERY;
+ sc = app_timer_start(&conn->gatt_timer,
+ GATT_TIMEOUT_MS,
+ gatt_timeout,
+ conn,
+ false);
+ }
+ if (sc != SL_STATUS_OK) {
+ // Close connection
+ (void)sl_bt_connection_close(conn->connection_handle);
+ lib_status = ESL_LIB_STATUS_CONN_DISCOVERY_FAILED;
+ esl_lib_log_connection_error(CONN_FMT "DIS discovery failed, connection handle = %u, sc = 0x%04x" APP_LOG_NL,
+ conn,
+ conn->connection_handle,
+ sc);
}
} else {
esl_lib_log_connection_debug(CONN_FMT "Service discovery finished, start ESL discovery, connection handle = %u" APP_LOG_NL,
@@ -1149,22 +1214,28 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
conn->gattdb_handles.services.esl);
if (sc == SL_STATUS_OK) {
conn->state = ESL_LIB_CONNECTION_STATE_ESL_DISCOVERY;
+ sc = app_timer_start(&conn->gatt_timer,
+ GATT_TIMEOUT_MS,
+ gatt_timeout,
+ conn,
+ false);
}
}
if (sc != SL_STATUS_OK) {
// Close connection
(void)sl_bt_connection_close(conn->connection_handle);
lib_status = ESL_LIB_STATUS_CONN_DISCOVERY_FAILED;
- esl_lib_log_connection_error(CONN_FMT "Discovery failed, connection handle = %u, sc = %04x" APP_LOG_NL,
+ esl_lib_log_connection_error(CONN_FMT "Discovery failed, connection handle = %u, sc = 0x%04x" APP_LOG_NL,
conn,
conn->connection_handle,
sc);
}
break;
case ESL_LIB_CONNECTION_STATE_DIS_DISCOVERY:
+ (void)app_timer_stop(&conn->gatt_timer);
if (conn->gattdb_handles.services.esl != ESL_LIB_INVALID_SERVICE_HANDLE) {
// Discover ESL service characteristics
- esl_lib_log_connection_debug(CONN_FMT "Characteristic discovery finished, start ESL discovery, connection handle = %u" APP_LOG_NL,
+ esl_lib_log_connection_debug(CONN_FMT "Device information discovery finished, start ESL discovery, connection handle = %u" APP_LOG_NL,
conn,
conn->connection_handle);
@@ -1172,22 +1243,39 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
conn->gattdb_handles.services.esl);
if (sc == SL_STATUS_OK) {
conn->state = ESL_LIB_CONNECTION_STATE_ESL_DISCOVERY;
- } else {
+ sc = app_timer_start(&conn->gatt_timer,
+ GATT_TIMEOUT_MS,
+ gatt_timeout,
+ conn,
+ false);
+ }
+ if (sc != SL_STATUS_OK) {
// Close connection
(void)sl_bt_connection_close(conn->connection_handle);
lib_status = ESL_LIB_STATUS_CONN_DISCOVERY_FAILED;
- esl_lib_log_connection_error(CONN_FMT "Discovery failed, connection handle = %u, sc = %04x" APP_LOG_NL,
+ esl_lib_log_connection_error(CONN_FMT "DIS discovery failed, connection handle = %u, sc = 0x%04x" APP_LOG_NL,
conn,
conn->connection_handle,
sc);
}
+ } else {
+ // Set the cause of the error
+ sc = SL_STATUS_INVALID_HANDLE;
+ // Close connection
+ (void)sl_bt_connection_close(conn->connection_handle);
+ lib_status = ESL_LIB_STATUS_CONN_DISCOVERY_FAILED;
+ esl_lib_log_connection_error(CONN_FMT "ESL Service not found, connection handle = %u, sc = 0x%04x" APP_LOG_NL,
+ conn,
+ conn->connection_handle,
+ sc);
}
break;
case ESL_LIB_CONNECTION_STATE_ESL_DISCOVERY:
+ (void)app_timer_stop(&conn->gatt_timer);
// Subscribe to characteristics
if (conn->gattdb_handles.esl_characteristics[ESL_LIB_CHARACTERISTIC_INDEX_ESL_CONTROL_POINT]
!= ESL_LIB_INVALID_CHARACTERISTIC_HANDLE) {
- esl_lib_log_connection_debug(CONN_FMT "Characteristic discovery finished, subscribe to ESL CP notifications, connection handle = %u" APP_LOG_NL,
+ esl_lib_log_connection_debug(CONN_FMT "Feature discovery complete, subscribe to ESL CP notifications, connection handle = %u" APP_LOG_NL,
conn,
conn->connection_handle);
sc = sl_bt_gatt_set_characteristic_notification(conn->connection_handle,
@@ -1195,11 +1283,17 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
sl_bt_gatt_notification);
if (sc == SL_STATUS_OK) {
conn->state = ESL_LIB_CONNECTION_STATE_ESL_SUBSCRIBE;
- } else {
+ sc = app_timer_start(&conn->gatt_timer,
+ GATT_TIMEOUT_MS,
+ gatt_timeout,
+ conn,
+ false);
+ }
+ if (sc != SL_STATUS_OK) {
// Close connection
(void)sl_bt_connection_close(conn->connection_handle);
lib_status = ESL_LIB_STATUS_CONN_SUBSCRIBE_FAILED;
- esl_lib_log_connection_error(CONN_FMT "Subscribe failed, connection handle = %u, sc = %04x" APP_LOG_NL,
+ esl_lib_log_connection_error(CONN_FMT "Subscribe failed, connection handle = %u, sc = 0x%04x" APP_LOG_NL,
conn,
conn->connection_handle,
sc);
@@ -1210,7 +1304,7 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
// Close connection
(void)sl_bt_connection_close(conn->connection_handle);
lib_status = ESL_LIB_STATUS_CONN_SUBSCRIBE_FAILED;
- esl_lib_log_connection_error(CONN_FMT "ESL CP not found, connection handle = %u, sc = %04x" APP_LOG_NL,
+ esl_lib_log_connection_error(CONN_FMT "ESL CP not found, connection handle = %u, sc = 0x%04x" APP_LOG_NL,
conn,
conn->connection_handle,
sc);
@@ -1236,7 +1330,7 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
// Close connection
(void)sl_bt_connection_close(conn->connection_handle);
lib_status = ESL_LIB_STATUS_OTS_INIT_FAILED;
- esl_lib_log_connection_error(CONN_FMT "Image Transfer - OTS init failed, connection handle = %u, sc = %04x" APP_LOG_NL,
+ esl_lib_log_connection_error(CONN_FMT "Image Transfer - OTS init failed, connection handle = %u, sc = 0x%04x" APP_LOG_NL,
conn,
conn->connection_handle,
sc);
@@ -1256,7 +1350,7 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
(void)sl_bt_connection_close(conn->connection_handle);
lib_status = ESL_LIB_STATUS_CONN_LOST;
sc = evt->data.evt_gatt_procedure_completed.result;
- esl_lib_log_connection_error(CONN_FMT "Procedure failure, connection handle = %u, result = %04x" APP_LOG_NL,
+ esl_lib_log_connection_error(CONN_FMT "Procedure failure, connection handle = %u, result = 0x%04x" APP_LOG_NL,
conn,
conn->connection_handle,
sc);
@@ -1264,6 +1358,7 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
}
}
} else {
+ // Suppress error event for unknown connections
sc = SL_STATUS_OK;
}
break;
@@ -1275,22 +1370,19 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
if (conn != NULL) {
state.connection_state = conn->state;
- } else {
- state.core_state = ESL_LIB_CORE_STATE_CONNECTING;
- }
-
- if (conn != NULL) {
// Send connection error if connection is present.
(void)send_connection_error(conn,
lib_status,
sc,
conn->state);
+ conn->command_complete = true;
} else {
esl_lib_node_id_t node_id;
+ state.core_state = ESL_LIB_CORE_STATE_CONNECTING;
if (addr != NULL) {
// Send address if present.
node_id.type = ESL_LIB_NODE_ID_TYPE_ADDRESS;
- node_id.id.address.addr_type = addr->addr_type;
+ node_id.id.address.address_type = addr->address_type;
memcpy(node_id.id.address.addr, addr->addr, sizeof(bd_addr));
} else {
node_id.type = ESL_LIB_NODE_ID_TYPE_NONE;
@@ -1307,6 +1399,29 @@ void esl_lib_connection_on_bt_event(sl_bt_msg_t *evt)
// -----------------------------------------------------------------------------
// Private functions
+static void esl_lib_connection_safe_remove_ptr(esl_lib_connection_t *ptr)
+{
+ (void)app_timer_stop(&ptr->timer);
+ (void)app_timer_stop(&ptr->gatt_timer);
+ sl_slist_remove(&connection_list, &ptr->node);
+ esl_lib_command_list_cleanup(&ptr->command_list);
+
+ if (ptr->command != NULL) {
+ if (ptr->command->cmd_code == ESL_LIB_CMD_WRITE_IMAGE
+ && ptr->command->data.cmd_write_image.img_data_copied != NULL) {
+ esl_lib_memory_free(ptr->command->data.cmd_write_image.img_data_copied);
+ }
+ esl_lib_memory_free(ptr->command);
+ }
+ clean_tag_info(ptr);
+ esl_lib_log_connection_debug(CONN_FMT "Removed connection handle = %u" APP_LOG_NL,
+ ptr,
+ ptr->connection_handle);
+ // Sanitize handles in memory area to avoid possible corruption later due to garbage
+ ptr->connection_handle = SL_BT_INVALID_CONNECTION_HANDLE;
+ ptr->ots_handle = ESL_LIB_INVALID_HANDLE;
+ esl_lib_memory_free(ptr);
+}
static void run_command(esl_lib_command_list_cmd_t *cmd)
{
@@ -1323,7 +1438,7 @@ static void run_command(esl_lib_command_list_cmd_t *cmd)
if (esl_lib_connection_contains(conn)) {
sc = close_connection(conn);
} else {
- sc = SL_STATUS_NOT_FOUND;
+ sc = SL_STATUS_DELETED;
lib_status = ESL_LIB_STATUS_CONN_FAILED;
conn->command_complete = true;
}
@@ -1369,7 +1484,7 @@ static void run_command(esl_lib_command_list_cmd_t *cmd)
conn,
conn->tag_info_type,
conn->connection_handle);
- sc = get_tag_info_finish(conn);
+ sc = get_tag_info_finish(conn, sc);
}
}
if (sc == SL_STATUS_OK) {
@@ -1380,7 +1495,7 @@ static void run_command(esl_lib_command_list_cmd_t *cmd)
}
}
} else {
- sc = SL_STATUS_NOT_FOUND;
+ sc = SL_STATUS_DELETED;
conn->command_complete = true;
}
break;
@@ -1397,7 +1512,7 @@ static void run_command(esl_lib_command_list_cmd_t *cmd)
// Start write process using TLV(s)
(void)write_next_config_value(conn);
} else {
- sc = SL_STATUS_NOT_FOUND;
+ sc = SL_STATUS_DELETED;
conn->command_complete = true;
}
break;
@@ -1434,7 +1549,7 @@ static void run_command(esl_lib_command_list_cmd_t *cmd)
sc = SL_STATUS_OK;
}
} else {
- sc = SL_STATUS_NOT_FOUND;
+ sc = SL_STATUS_DELETED;
conn->command_complete = true;
}
break;
@@ -1463,7 +1578,7 @@ static void run_command(esl_lib_command_list_cmd_t *cmd)
conn->command_complete = true;
}
} else {
- sc = SL_STATUS_NOT_FOUND;
+ sc = SL_STATUS_DELETED;
lib_status = ESL_LIB_STATUS_OTS_ERROR;
conn->command_complete = true;
}
@@ -1490,7 +1605,7 @@ static void run_command(esl_lib_command_list_cmd_t *cmd)
conn->command_complete = true;
}
} else {
- sc = SL_STATUS_NOT_FOUND;
+ sc = SL_STATUS_DELETED;
lib_status = ESL_LIB_STATUS_OTS_ERROR;
conn->command_complete = true;
}
@@ -1514,8 +1629,14 @@ static void run_command(esl_lib_command_list_cmd_t *cmd)
lib_status = ESL_LIB_STATUS_PAST_INIT_FAILED;
conn->command_complete = true;
}
+ // Start timeout anyway: the ESL may close/closing/closed the connection because it can be already in sync
+ sc = app_timer_start(&conn->timer,
+ PAST_CONN_TIMEOUT,
+ connection_timeout,
+ conn,
+ false);
} else {
- sc = SL_STATUS_NOT_FOUND;
+ sc = SL_STATUS_DELETED;
lib_status = ESL_LIB_STATUS_PAST_INIT_FAILED;
conn->command_complete = true;
}
@@ -1524,7 +1645,7 @@ static void run_command(esl_lib_command_list_cmd_t *cmd)
break;
}
if (sc != SL_STATUS_OK && conn != NULL) {
- esl_lib_log_connection_warning(CONN_FMT "Command failure, connection handle = %u, sc = %04x" APP_LOG_NL,
+ esl_lib_log_connection_warning(CONN_FMT "Command failure, connection handle = %u, sc = 0x%04x" APP_LOG_NL,
conn,
conn->connection_handle,
sc);
@@ -1564,6 +1685,33 @@ static sl_status_t send_cp_notification_event(esl_lib_connection_t *conn,
return sc;
}
+static sl_status_t send_bonding_finished(esl_lib_connection_t *conn)
+{
+ sl_status_t sc;
+ esl_lib_evt_t *lib_evt;
+
+ sc = esl_lib_event_list_allocate(ESL_LIB_EVT_BONDING_FINISHED,
+ 0,
+ &lib_evt);
+ if (sc == SL_STATUS_OK) {
+ lib_evt->evt_code = ESL_LIB_EVT_BONDING_FINISHED;
+ lib_evt->data.evt_bonding_finished.connection_handle
+ = (esl_lib_connection_handle_t)conn;
+ // Copy address
+ lib_evt->data.evt_bonding_finished.address.address_type
+ = conn->address_type;
+ memcpy(lib_evt->data.evt_bonding_finished.address.addr,
+ conn->address.addr,
+ sizeof(conn->address.addr));
+ sc = esl_lib_event_list_push_back(lib_evt);
+ if (sc != SL_STATUS_OK) {
+ // Free up memory on failure
+ esl_lib_memory_free(lib_evt);
+ }
+ }
+ return sc;
+}
+
static sl_status_t send_bonding_data(esl_lib_connection_t *conn,
uint8_t *data)
{
@@ -1578,7 +1726,7 @@ static sl_status_t send_bonding_data(esl_lib_connection_t *conn,
lib_evt->data.evt_bonding_data.connection_handle
= (esl_lib_connection_handle_t)conn;
// Copy address
- lib_evt->data.evt_bonding_data.address.addr_type
+ lib_evt->data.evt_bonding_data.address.address_type
= conn->address_type;
memcpy(lib_evt->data.evt_bonding_data.address.addr,
conn->address.addr,
@@ -1620,7 +1768,7 @@ static sl_status_t send_connection_status(esl_lib_connection_t *conn,
lib_evt->data.evt_connection_opened.connection_handle
= (esl_lib_connection_handle_t)conn;
// Copy address
- lib_evt->data.evt_connection_opened.address.addr_type = conn->address_type;
+ lib_evt->data.evt_connection_opened.address.address_type = conn->address_type;
memcpy(lib_evt->data.evt_connection_opened.address.addr,
conn->address.addr,
sizeof(conn->address.addr));
@@ -1629,13 +1777,13 @@ static sl_status_t send_connection_status(esl_lib_connection_t *conn,
&conn->gattdb_handles,
sizeof(conn->gattdb_handles));
for (uint8_t i = 0; i < sizeof(conn->gattdb_handles.esl_characteristics) / sizeof(uint16_t); i++) {
- esl_lib_log_connection_debug(CONN_FMT "Sending ESL %u characteristic handle = 0x%x" APP_LOG_NL,
+ esl_lib_log_connection_debug(CONN_FMT "Sending ESL %u characteristic handle = 0x%02x" APP_LOG_NL,
conn,
i,
conn->gattdb_handles.esl_characteristics[i]);
}
for (uint8_t i = 0; i < sizeof(conn->gattdb_handles.dis_characteristics) / sizeof(uint16_t); i++) {
- esl_lib_log_connection_debug(CONN_FMT "Sending DIS %u characteristic handle = 0x%x" APP_LOG_NL,
+ esl_lib_log_connection_debug(CONN_FMT "Sending DIS %u characteristic handle = 0x%02x" APP_LOG_NL,
conn,
i,
conn->gattdb_handles.dis_characteristics[i]);
@@ -1647,7 +1795,7 @@ static sl_status_t send_connection_status(esl_lib_connection_t *conn,
// Set reason for disconnection event
lib_evt->data.evt_connection_closed.reason = reason;
// Copy address
- lib_evt->data.evt_connection_closed.address.addr_type = conn->address_type;
+ lib_evt->data.evt_connection_closed.address.address_type = conn->address_type;
memcpy(lib_evt->data.evt_connection_closed.address.addr,
conn->address.addr,
sizeof(conn->address.addr));
@@ -1669,6 +1817,9 @@ static sl_status_t send_att_response(esl_lib_connection_t *conn,
sl_status_t sc;
esl_lib_evt_t *lib_evt;
+ // Stop timer
+ (void)app_timer_stop(&conn->gatt_timer);
+
if (type == ESL_LIB_EVT_CONFIGURE_TAG_RESPONSE) {
sc = esl_lib_event_list_allocate(type, 0, &lib_evt);
if (sc == SL_STATUS_OK) {
@@ -1717,12 +1868,12 @@ static sl_status_t send_connection_error(esl_lib_connection_t *conn,
sl_status_t sc;
esl_lib_node_id_t node_id;
- if (check_connected(conn)) {
+ if (check_connected(conn) && conn->state != ESL_LIB_CONNECTION_STATE_PAST_CLOSE_CONNECTION) {
node_id.type = ESL_LIB_NODE_ID_TYPE_CONNECTION;
node_id.id.connection_handle = (esl_lib_connection_handle_t)conn;
} else {
node_id.type = ESL_LIB_NODE_ID_TYPE_ADDRESS;
- node_id.id.address.addr_type = conn->address_type;
+ node_id.id.address.address_type = conn->address_type;
memcpy(node_id.id.address.addr,
conn->address.addr,
sizeof(conn->address.addr));
@@ -1752,6 +1903,9 @@ static void connection_complete(esl_lib_connection_t *conn)
(void)send_connection_status(conn, ESL_LIB_TRUE, SL_STATUS_OK);
conn->state = ESL_LIB_CONNECTION_STATE_CONNECTED;
// Open command has been completed.
+ if (conn->command->cmd_code == ESL_LIB_CMD_CONNECT) {
+ esl_lib_memory_free(conn->command);
+ }
conn->command_complete = true;
}
@@ -1774,6 +1928,7 @@ static bool check_connected(esl_lib_connection_t *conn)
// Not connected in connecting phases
case ESL_LIB_CONNECTION_STATE_OFF:
case ESL_LIB_CONNECTION_STATE_CONNECTING:
+ case ESL_LIB_CONNECTION_STATE_RECONNECTING:
case ESL_LIB_CONNECTION_STATE_APPLYING_LTK:
case ESL_LIB_CONNECTION_STATE_NEW_BOND_REQUIRED:
case ESL_LIB_CONNECTION_STATE_BONDING:
@@ -1799,27 +1954,42 @@ static void on_image_transfer_status(esl_lib_image_transfer_handle_t handle,
(void)handle;
esl_lib_connection_t *conn = NULL;
sl_status_t sc = esl_lib_connection_find(connection, &conn);
- if (sc == SL_STATUS_OK && conn->state == ESL_LIB_CONNECTION_STATE_OTS_INIT) {
- // Copy handles if present
- if (gattdb_handles != NULL) {
- memcpy(conn->gattdb_handles.ots_characteristics,
- *gattdb_handles,
- sizeof(conn->gattdb_handles.ots_characteristics));
+ if (sc == SL_STATUS_OK) {
+ if (conn->state == ESL_LIB_CONNECTION_STATE_OTS_INIT) {
+ // Copy handles if present
+ if (gattdb_handles != NULL) {
+ memcpy(conn->gattdb_handles.ots_characteristics,
+ *gattdb_handles,
+ sizeof(conn->gattdb_handles.ots_characteristics));
+ }
+
+ if (state == ESL_LIB_IMAGE_TRANSFER_STATE_IDLE) {
+ // init succeeded
+ connection_complete(conn);
+ }
}
- if (state == ESL_LIB_IMAGE_TRANSFER_STATE_IDLE) {
- // init succeeded
- connection_complete(conn);
- } else if (state == ESL_LIB_IMAGE_TRANSFER_REMOVED) {
- // Removed since there were an error during init
- // Close connection
- (void)sl_bt_connection_close(conn->connection_handle);
+
+ if (state == ESL_LIB_IMAGE_TRANSFER_REMOVED && check_image_transfer(conn)) {
+ // Removed since there were an error during init or transfer
(void)send_connection_error(conn,
ESL_LIB_STATUS_OTS_INIT_FAILED,
result,
conn->state);
- esl_lib_connection_remove_ptr(conn);
- conn->command_complete = true;
+ // Close connection as OTS errors are mostly unrecoverable
+ esl_lib_log_connection_debug(CONN_FMT "Close connection due image transfer status 0x%04x, connection handle = %u, sc = 0x%04x" APP_LOG_NL,
+ conn,
+ result,
+ sc,
+ conn->connection_handle);
+ close_broken_connection(&conn);
+
+ if (conn != NULL) {
+ // Note that conn might have been deleted already due an error case!
+ conn->command_complete = true;
+ }
}
+ } else {
+ esl_lib_log_connection_debug("[Unknown] Image transfer status changed for a forcibly closed connection, no handle available anymore." APP_LOG_NL);
}
}
@@ -1854,13 +2024,29 @@ static void on_image_transfer_finished(esl_lib_image_transfer_handle_t handle,
ESL_LIB_STATUS_OTS_TRANSFER_FAILED,
result,
conn->state);
+ esl_lib_log_connection_debug(CONN_FMT "Close connection due image transfer finished result: 0x%04x, connection handle = %u, sc = 0x%04x" APP_LOG_NL,
+ conn,
+ result,
+ conn->connection_handle,
+ sc);
+ if (result == SL_STATUS_TIMEOUT
+ || result == SL_STATUS_NO_MORE_RESOURCE
+ || result == SL_STATUS_FAIL
+ || result == SL_STATUS_BT_CTRL_CONNECTION_REJECTED_DUE_TO_NO_SUITABLE_CHANNEL_FOUND) {
+ // Close connection as some OTS errors are unrecoverable
+ close_broken_connection(&conn);
+ }
}
- // Free up image data
- if (conn->command->data.cmd_write_image.img_data_copied != NULL) {
- esl_lib_memory_free(conn->command->data.cmd_write_image.img_data_copied);
+
+ if (conn != NULL) {
+ // Note that conn might have been deleted already due an error case!
+ // Free up image data if still there
+ if (conn->command->data.cmd_write_image.img_data_copied != NULL) {
+ esl_lib_memory_free(conn->command->data.cmd_write_image.img_data_copied);
+ }
+ conn->command_complete = true;
+ conn->state = ESL_LIB_CONNECTION_STATE_CONNECTED;
}
- conn->command_complete = true;
- conn->state = ESL_LIB_CONNECTION_STATE_CONNECTED;
}
}
@@ -1900,9 +2086,19 @@ static void on_image_transfer_type_arrived(esl_lib_image_transfer_handle_t handl
ESL_LIB_STATUS_OTS_META_READ_FAILED,
result,
conn->state);
+ esl_lib_log_connection_debug(CONN_FMT "Close connection due image get type fail 0x%04x, connection handle = %u, sc = 0x%04x" APP_LOG_NL,
+ conn,
+ result,
+ sc,
+ conn->connection_handle);
+ // Close connection as OTS errors are mostly unrecoverable
+ close_broken_connection(&conn);
+ }
+ if (conn != NULL) {
+ // Note that conn might have been deleted already due an error case!
+ conn->command_complete = true;
+ conn->state = ESL_LIB_CONNECTION_STATE_CONNECTED;
}
- conn->command_complete = true;
- conn->state = ESL_LIB_CONNECTION_STATE_CONNECTED;
}
}
@@ -1927,9 +2123,10 @@ static void gatt_timeout(app_timer_t *timer,
(void)send_att_response(conn,
ESL_LIB_EVT_CONFIGURE_TAG_RESPONSE,
SL_STATUS_TIMEOUT);
- // Write next value
- (void)write_next_config_value(conn);
+ esl_lib_log_connection_debug(CONN_FMT "Close connection due GATT timout during configuring phase!" APP_LOG_NL, conn);
}
+ // Close connection as GATT errors during configuration phase are unrecoverable
+ close_broken_connection(&conn);
} else {
esl_lib_log_connection_warning(CONN_FMT "GATT timeout for unknown connection!" APP_LOG_NL,
data);
@@ -1947,11 +2144,13 @@ static void reconnect_timeout(app_timer_t *timer,
conn);
if (esl_lib_connection_contains(conn)) {
- esl_lib_log_connection_debug(CONN_FMT "Connection retry, connection handle = %u" APP_LOG_NL,
+ esl_lib_log_connection_debug(CONN_FMT "Connection retry to " ESL_LIB_LOG_ADDR_FORMAT ", expired handle = %u" APP_LOG_NL,
conn,
+ ESL_LIB_LOG_ADDR(conn->command->data.cmd_connect.address),
conn->connection_handle);
- // Retry
- sc = esl_lib_connection_open(conn->command, conn);
+ // Resend command to core queue
+ sc = esl_lib_core_add_command(conn->command);
+
if (sc != SL_STATUS_OK) {
(void)send_connection_error(conn,
ESL_LIB_STATUS_CONN_FAILED,
@@ -1960,10 +2159,12 @@ static void reconnect_timeout(app_timer_t *timer,
esl_lib_log_connection_error(CONN_FMT "Failed to reopen connection, connection handle = %u" APP_LOG_NL,
conn,
conn->connection_handle);
- conn->command_complete = true;
// And also remove connection from the list.
(void)esl_lib_connection_remove_ptr(conn);
conn = NULL;
+ } else {
+ conn->command = NULL;
+ conn->command_complete = true;
}
} else {
esl_lib_log_connection_warning(CONN_FMT "Reconnect handle not found (possibly has already been removed)" APP_LOG_NL,
@@ -1980,16 +2181,9 @@ static void connection_timeout(app_timer_t *timer,
esl_lib_connection_t *conn = (esl_lib_connection_t *)data;
// Check if it exists
if (esl_lib_connection_contains(conn)) {
- esl_lib_log_connection_error(CONN_FMT "Connection timeout, connection handle = %u" APP_LOG_NL,
- conn,
- conn->connection_handle);
-
- // Free up command
- if (conn->command != NULL) {
- esl_lib_memory_free(conn->command);
- conn->command = NULL;
- }
- conn->command_complete = true;
+ esl_lib_log_connection_warning(CONN_FMT "Connection timeout, connection handle = %u" APP_LOG_NL,
+ conn,
+ conn->connection_handle);
// Try to close the connection
sc = close_connection(conn);
if (sc == SL_STATUS_OK) {
@@ -2004,8 +2198,16 @@ static void connection_timeout(app_timer_t *timer,
status,
SL_STATUS_TIMEOUT,
conn->state);
- // Remove connection
- (void)esl_lib_connection_remove_ptr(conn);
+
+ if (conn->state == ESL_LIB_CONNECTION_STATE_CONNECTING
+ || conn->state == ESL_LIB_CONNECTION_STATE_RECONNECTING) {
+ esl_lib_core_connection_complete();
+ }
+
+ if (status == ESL_LIB_STATUS_CONN_CLOSE_FAILED) {
+ // Remove connection
+ (void)esl_lib_connection_remove_ptr(conn);
+ }
} else {
esl_lib_log_connection_warning(CONN_FMT "Connection timeout for unknown connection" APP_LOG_NL,
conn);
@@ -2072,7 +2274,7 @@ static esl_lib_data_type_t get_next_type(esl_lib_data_type_t type)
return next_type;
}
-static sl_status_t get_tag_info_finish(esl_lib_connection_t *conn)
+static sl_status_t get_tag_info_finish(esl_lib_connection_t *conn, sl_status_t status)
{
uint32_t data_size = 0;
size_t current_size = 0;
@@ -2080,8 +2282,10 @@ static sl_status_t get_tag_info_finish(esl_lib_connection_t *conn)
esl_lib_evt_t *event;
uint8_t *ptr;
esl_lib_tlv_t *tlv;
- sl_status_t sc = SL_STATUS_OK;
+ sl_status_t sc = status;
+ // Stop GATT watchdog timer
+ (void)app_timer_stop(&conn->gatt_timer);
// Calculate event size from storage list
SL_SLIST_FOR_EACH_ENTRY(conn->tag_info_list, current, esl_lib_connection_tag_info_storage_t, node) {
// Get current size
@@ -2123,7 +2327,7 @@ static sl_status_t get_tag_info_finish(esl_lib_connection_t *conn)
// Get value for TLV
esl_lib_storage_copy(current->storage, tlv->data.data);
// Free up storage
- esl_lib_storage_delete(current->storage);
+ esl_lib_storage_delete(¤t->storage);
// Free list item also
esl_lib_memory_free(current);
// Move pointer to the next TLV
@@ -2136,24 +2340,39 @@ static sl_status_t get_tag_info_finish(esl_lib_connection_t *conn)
conn->connection_handle);
}
- // Send event
- sc = esl_lib_event_list_push_back(event);
-
- if (sc == SL_STATUS_OK) {
- esl_lib_log_connection_info(CONN_FMT "Get tag info finished, connection handle = %u" APP_LOG_NL,
- conn,
- conn->connection_handle);
- } else {
- esl_lib_log_connection_error(CONN_FMT "Get tag info finished but failed to send tag info, connection handle = %u" APP_LOG_NL,
+ if (sc != SL_STATUS_OK) {
+ esl_lib_log_connection_error(CONN_FMT "Get tag info finished with error status: 0x%04x, connection handle = %u" APP_LOG_NL,
conn,
+ sc,
conn->connection_handle);
+ // Force close as keeping connected would break the AP procedure flow
+ close_broken_connection(&conn);
+ } else {
+ // Send event
+ sc = esl_lib_event_list_push_back(event);
+
+ if (sc == SL_STATUS_OK) {
+ esl_lib_log_connection_debug(CONN_FMT "Get tag info finished, connection handle = %u" APP_LOG_NL,
+ conn,
+ conn->connection_handle);
+ } else {
+ esl_lib_log_connection_error(CONN_FMT "Get tag info finished but failed to send tag info, connection handle = %u." APP_LOG_NL,
+ conn,
+ conn->connection_handle);
+ // Force close as keeping connected would break the AP procedure flow
+ close_broken_connection(&conn);
+ }
}
- // Clear type
- conn->tag_info_type = ESL_LIB_DATA_TYPE_UNINITIALIZED;
+ if (conn != NULL) {
+ // Clear type
+ conn->tag_info_type = ESL_LIB_DATA_TYPE_UNINITIALIZED;
- // Set command complete
- conn->command_complete = true;
+ // Set command complete
+ conn->command_complete = true;
+ } else {
+ esl_lib_log_connection_debug("[Unknown] Get tag info finished for a forcibly closed broken connection, no handle available anymore." APP_LOG_NL);
+ }
return sc;
}
@@ -2164,6 +2383,7 @@ static sl_status_t get_next_tag_info(esl_lib_connection_t *conn)
uint16_t char_handle = ESL_LIB_INVALID_CHARACTERISTIC_HANDLE;
esl_lib_data_type_t type = conn->tag_info_type;
+ (void)app_timer_stop(&conn->gatt_timer);
esl_lib_log_connection_debug(CONN_FMT "Get next tag info, connection handle = %u" APP_LOG_NL,
conn,
conn->connection_handle);
@@ -2173,7 +2393,7 @@ static sl_status_t get_next_tag_info(esl_lib_connection_t *conn)
|| type == ESL_LIB_DATA_TYPE_GATT_CONTROL_POINT)) {
type = get_next_type(type);
char_handle = get_handle_for_type(conn, type);
- esl_lib_log_connection_debug(CONN_FMT "Next tag info type is %u (0x%x), connection handle = %u" APP_LOG_NL,
+ esl_lib_log_connection_debug(CONN_FMT "Next tag info type is %u (0x%02x), connection handle = %u" APP_LOG_NL,
conn,
type,
char_handle,
@@ -2193,17 +2413,25 @@ static sl_status_t get_next_tag_info(esl_lib_connection_t *conn)
conn->tag_info_type = type;
// Create storage for storing data
sc = esl_lib_storage_create(&conn->tag_info_data);
+
+ if (sc == SL_STATUS_OK) {
+ sc = app_timer_start(&conn->gatt_timer,
+ GATT_TIMEOUT_MS,
+ gatt_timeout,
+ conn,
+ false);
+ }
}
if (sc != SL_STATUS_OK) {
esl_lib_log_connection_error(CONN_FMT "Failed to read tag info type %u, connection handle = %u" APP_LOG_NL,
conn,
type,
conn->connection_handle);
- sc = get_tag_info_finish(conn);
+ sc = get_tag_info_finish(conn, sc);
}
} else {
// No valid handle found, finish
- sc = get_tag_info_finish(conn);
+ sc = get_tag_info_finish(conn, sc);
}
return sc;
@@ -2214,11 +2442,11 @@ static void clean_tag_info(esl_lib_connection_t *conn)
esl_lib_connection_tag_info_storage_t *current;
// Delete temporary data
- esl_lib_storage_delete(conn->tag_info_data);
+ esl_lib_storage_delete(&conn->tag_info_data);
// Delete saved data
while ((current = (esl_lib_connection_tag_info_storage_t *)sl_slist_pop(&conn->tag_info_list)) != NULL) {
- esl_lib_storage_delete(current->storage);
+ esl_lib_storage_delete(¤t->storage);
esl_lib_memory_free(current);
}
}
@@ -2264,8 +2492,6 @@ static sl_status_t write_next_config_value(esl_lib_connection_t *conn)
if (conn->config_index >= conn->command->data.cmd_configure_tag.tlv_data.len) {
// This was the last TLV
move_to_next = false;
- // Stop timer
- (void)app_timer_stop(&conn->gatt_timer);
// Consider command completed
conn->command_complete = true;
// Reset the state
@@ -2356,7 +2582,7 @@ static sl_status_t write_value(esl_lib_connection_t *conn,
break;
}
if (characteristic != ESL_LIB_INVALID_CHARACTERISTIC_HANDLE) {
- esl_lib_log_connection_debug(CONN_FMT "Writing value type %u (0x%x), connection handle = %u" APP_LOG_NL,
+ esl_lib_log_connection_debug(CONN_FMT "Writing value type %u (0x%02x), connection handle = %u" APP_LOG_NL,
conn,
type,
characteristic,
@@ -2376,7 +2602,7 @@ static sl_status_t write_value(esl_lib_connection_t *conn,
if (sc != SL_STATUS_OK) {
(void)app_timer_stop(&conn->gatt_timer);
} else {
- esl_lib_log_connection_debug(CONN_FMT "Writing value type %u (0x%x) succeeded, waiting for response, connection handle = %u" APP_LOG_NL,
+ esl_lib_log_connection_debug(CONN_FMT "Writing value type %u (0x%02x) succeeded, waiting for response, connection handle = %u" APP_LOG_NL,
conn,
type,
characteristic,
@@ -2390,7 +2616,7 @@ static sl_status_t write_value(esl_lib_connection_t *conn,
data,
&sent_len);
if (sc == SL_STATUS_OK) {
- esl_lib_log_connection_debug(CONN_FMT "Writing value type %u (0x%x) succeeded, connection handle = %u" APP_LOG_NL,
+ esl_lib_log_connection_debug(CONN_FMT "Writing value type %u (0x%02x) succeeded, connection handle = %u" APP_LOG_NL,
conn,
type,
characteristic,
@@ -2429,3 +2655,34 @@ static bool find_tlv(esl_lib_command_list_cmd_t *cmd,
return false;
}
+
+static void close_broken_connection(esl_lib_connection_t **conn)
+{
+ if (conn == NULL || *conn == NULL || (*conn)->connection_handle == SL_BT_INVALID_CONNECTION_HANDLE) {
+ // Nothing left to close (second invocation can happen on the same connection in edge cases, especially in case of various OTS errors)
+ return;
+ }
+
+ sl_status_t sc = sl_bt_connection_close((*conn)->connection_handle);
+ if (sc != SL_STATUS_OK) {
+ esl_lib_log_connection_error(CONN_FMT "Closing request failed with status: 0x%04x on connection handle = %u during close on error" APP_LOG_NL,
+ *conn,
+ sc,
+ (*conn)->connection_handle);
+ // Send error event about the deletion of the connection pointer
+ send_connection_status(*conn, ESL_LIB_FALSE, SL_STATUS_BT_CTRL_UNKNOWN_CONNECTION_IDENTIFIER);
+ // Force removal of connection handle in case of error on sl_bt_connection_close request
+ (void)esl_lib_connection_remove_ptr(*conn);
+ *conn = NULL;
+ } else {
+ esl_lib_log_connection_debug(CONN_FMT "Requested closing connection with handle = %u on error" APP_LOG_NL,
+ *conn,
+ (*conn)->connection_handle);
+ (void)app_timer_stop(&(*conn)->timer);
+ (void)app_timer_start(&(*conn)->timer,
+ CLOSE_TIMEOUT_MS,
+ connection_timeout,
+ *conn,
+ false);
+ }
+}
diff --git a/app/bluetooth/common_host/esl_lib/esl_lib_connection.h b/app/bluetooth/common_host/esl_lib/esl_lib_connection.h
index 14b3f53af8c..339950c252f 100644
--- a/app/bluetooth/common_host/esl_lib/esl_lib_connection.h
+++ b/app/bluetooth/common_host/esl_lib/esl_lib_connection.h
@@ -61,28 +61,27 @@ typedef struct {
/// Connection list item type
typedef struct {
sl_slist_node_t node; ///< List node pointer
- esl_lib_connection_state_t state; ///< State of the connection
esl_lib_command_list_cmd_t *command; ///< Command in progress
- bool command_complete; ///< Finished command
sl_slist_node_t *command_list; ///< Command list
+ sl_slist_node_t *tag_info_list; ///< Tag info storage list
+ bool command_complete; ///< Finished command
uint8_t connection_handle; ///< Connection handle
- esl_lib_image_transfer_handle_t ots_handle; ///< OTS handle
- aes_key_128 ltk; ///< LTK for the connection
- bd_addr address; ///< Address
uint8_t address_type; ///< Address type
uint8_t max_payload; ///< Max payload
uint8_t security; ///< Security level
+ uint8_t config_index; ///< Current config index
+ esl_lib_connection_state_t state; ///< State of the connection
+ esl_lib_image_transfer_handle_t ots_handle; ///< OTS handle
+ aes_key_128 ltk; ///< LTK for the connection
+ bd_addr address; ///< Address
app_timer_t timer; ///< Connection timer
app_timer_t gatt_timer; ///< GATT timer
sl_status_t last_error; ///< Last error
esl_lib_bool_t gattdb_known; ///< Predefined GATT database
esl_lib_gattdb_handles_t gattdb_handles; ///< List of UUIDs
- uint8_t retry_count; ///< Retry count
- sl_slist_node_t *tag_info_list; ///< Tag info storage list
esl_lib_data_type_t tag_info_type; ///< Current tag info type
esl_lib_storage_handle_t tag_info_data; ///< Current tag info data
esl_lib_data_type_t config_type; ///< Current config type
- uint8_t config_index; ///< Current config index
} esl_lib_connection_t;
// -----------------------------------------------------------------------------
@@ -116,16 +115,6 @@ sl_status_t esl_lib_connection_find(uint8_t conn,
*****************************************************************************/
sl_status_t esl_lib_connection_remove_ptr(esl_lib_connection_t *ptr);
-/**************************************************************************//**
- * Remove a connection from the list.
- * @param[in] conn Connection handle.
- * @param[out] ptr_out Pointer output.
- *
- * @return Status code.
- *****************************************************************************/
-sl_status_t esl_lib_connection_remove_handle(uint8_t conn,
- esl_lib_connection_t **ptr_out);
-
/**************************************************************************//**
* Check connection list for the given item.
* @param[in] ptr Pointer to the connection list item.
@@ -181,8 +170,7 @@ sl_status_t esl_lib_connection_check_gattdb_handles(esl_lib_gattdb_handles_t *ga
*
* @return SL_STATUS_OK if the configuration is valid.
*****************************************************************************/
-sl_status_t esl_lib_connection_open(esl_lib_command_list_cmd_t *cmd,
- esl_lib_connection_t *handle);
+sl_status_t esl_lib_connection_open(esl_lib_command_list_cmd_t *cmd);
#ifdef __cplusplus
};
diff --git a/app/bluetooth/common_host/esl_lib/esl_lib_core.c b/app/bluetooth/common_host/esl_lib/esl_lib_core.c
index 3acb7b0a9d1..030ece3e40e 100644
--- a/app/bluetooth/common_host/esl_lib/esl_lib_core.c
+++ b/app/bluetooth/common_host/esl_lib/esl_lib_core.c
@@ -1,6 +1,6 @@
/***************************************************************************//**
* @file
- * @brief ESL Host Library core component.
+ * @brief ESL host library core component.
*
*******************************************************************************
* # License
@@ -77,10 +77,11 @@ static sl_status_t send_core_error(esl_lib_status_t lib_status,
sl_status_t status,
esl_lib_core_state_t data);
static sl_status_t send_tag_found(uint8_t *addr,
- uint8_t addr_type,
+ uint8_t address_type,
int8_t rssi);
static sl_status_t send_scan_status(void);
static bool find_service_in_advertisement(uint8_t *data, uint8_t len);
+static void esl_lib_core_internal_reset(void);
// -----------------------------------------------------------------------------
// Private variables
@@ -95,6 +96,7 @@ static struct argparse_descriptor_s arg_descriptor[] =
{ "-device", "" }, // empty string as option allows anything
{ "-baud", "115200,921600" },
{ "-handshake", "no,ctsrts,hw" }, // more options can be given where it's needed
+ { "-secure", NULL }, // enable encryption for NCP communication
{ NULL, NULL }
};
@@ -113,11 +115,10 @@ void esl_lib_init(char *config)
}
// Parse configuration
- esl_lib_log_core_info("Parsing host library configuration: %s" APP_LOG_NL, config);
+ esl_lib_log_core_debug("Parsing host library configuration: %s" APP_LOG_NL, config);
parse_config(config, ap_state);
- esl_lib_log_core_info("AP host library instance started." APP_LOG_NL);
-
// Initialize NCP connection.
+ esl_lib_log_core_debug("Initializing NCP host" APP_LOG_NL);
sc = ncp_host_init();
if (sc == SL_STATUS_INVALID_PARAMETER) {
esl_lib_log_core_critical("Failed to initialize host library!" APP_LOG_NL);
@@ -127,17 +128,19 @@ void esl_lib_init(char *config)
esl_lib_log_core_critical("Error initializing host library: 0x%04x" APP_LOG_NL, sc);
exit(EXIT_FAILURE);
}
- esl_lib_log_core_info("NCP host initialised." APP_LOG_NL);
esl_lib_log_core_info("Resetting NCP target..." APP_LOG_NL);
// Reset NCP to ensure it gets into a defined state.
// Once the chip successfully boots, boot event should be received.
sl_bt_system_reset(sl_bt_system_boot_mode_normal);
// Initialize L2CAP transfer
+ esl_lib_log_core_debug("Init L2CAP transfer layer" APP_LOG_NL);
sli_bt_l2cap_transfer_init();
// Initialize OTS Client
+ esl_lib_log_core_debug("Prepare OTS client" APP_LOG_NL);
sli_bt_ots_client_init();
+ esl_lib_log_core_debug("AP host library instance ready" APP_LOG_NL);
}
void esl_lib_process_action(void)
@@ -150,13 +153,10 @@ void esl_lib_process_action(void)
void esl_lib_deinit(void)
{
- // Clean command list
- esl_lib_command_list_cleanup(&ap_state->command_list);
+ // stop scanning
+ (void)sl_bt_scanner_stop();
- // Cleanup relationship
- esl_lib_connection_cleanup();
- esl_lib_pawr_cleanup();
- esl_lib_ap_control_cleanup();
+ esl_lib_core_internal_reset();
esl_lib_memory_free(ap_state);
@@ -173,13 +173,18 @@ sl_status_t esl_lib_core_add_command(esl_lib_command_list_cmd_t *cmd)
void sl_bt_on_event(sl_bt_msg_t *evt)
{
- esl_lib_core_on_bt_event(evt);
+ esl_lib_image_transfer_on_bt_event(evt);
esl_lib_connection_on_bt_event(evt);
esl_lib_pawr_on_bt_event(evt);
- esl_lib_image_transfer_on_bt_event(evt);
+ esl_lib_core_on_bt_event(evt);
esl_lib_ap_control_on_bt_event(evt);
}
+void esl_lib_core_connection_complete()
+{
+ ap_state->command_complete = true;
+}
+
// -----------------------------------------------------------------------------
// Private functions
@@ -197,21 +202,23 @@ static void esl_lib_core_on_bt_event(sl_bt_msg_t *evt)
// This event indicates the device has started and the radio is ready.
// Do not call any stack command before receiving this boot event!
case sl_bt_evt_system_boot_id:
- lib_status = ESL_LIB_STATUS_INIT_FAILED;
+ // Do internal LIB reset
+ esl_lib_core_internal_reset();
+ ap_state->scan.enabled = ESL_LIB_FALSE;
+ ap_state->scan.configured = ESL_LIB_FALSE;
+ ap_state->command_complete = true;
+ ap_state->core_state = ESL_LIB_CORE_STATE_IDLE;
+ lib_status = ESL_LIB_STATUS_INIT_FAILED;
// Extract unique ID from BT Address.
sc = sl_bt_system_get_identity_address(&address, &address_type);
if (sc != SL_STATUS_OK) {
lib_critical_error = true;
}
- esl_lib_log_core_info("Bluetooth %s address: %02X:%02X:%02X:%02X:%02X:%02X" APP_LOG_NL,
- address_type ? "static random" : "public device",
- address.addr[5],
- address.addr[4],
- address.addr[3],
- address.addr[2],
- address.addr[1],
- address.addr[0]);
+ esl_lib_log_core_info("Bluetooth " ESL_LIB_LOG_ADDR_FORMAT APP_LOG_NL,
+ address_type,
+ address_type ? "random" : "public",
+ ESL_LIB_LOG_BD_ADDR(address));
// Configure Security Manager to default
sc = sl_bt_sm_configure(0,
@@ -253,14 +260,8 @@ static void esl_lib_core_on_bt_event(sl_bt_msg_t *evt)
case sl_bt_evt_scanner_legacy_advertisement_report_id:
if (find_service_in_advertisement(evt->data.evt_scanner_legacy_advertisement_report.data.data,
evt->data.evt_scanner_legacy_advertisement_report.data.len)) {
- esl_lib_log_core_debug("Tag found with %s address: %02X:%02X:%02X:%02X:%02X:%02X , RSSI = %d" APP_LOG_NL,
- evt->data.evt_scanner_legacy_advertisement_report.address_type ? "static random" : "public device",
- evt->data.evt_scanner_legacy_advertisement_report.address.addr[5],
- evt->data.evt_scanner_legacy_advertisement_report.address.addr[4],
- evt->data.evt_scanner_legacy_advertisement_report.address.addr[3],
- evt->data.evt_scanner_legacy_advertisement_report.address.addr[2],
- evt->data.evt_scanner_legacy_advertisement_report.address.addr[1],
- evt->data.evt_scanner_legacy_advertisement_report.address.addr[0],
+ esl_lib_log_core_debug("Tag found with " ESL_LIB_LOG_ADDR_FORMAT ", RSSI = %d" APP_LOG_NL,
+ ESL_LIB_LOG_ADDR(evt->data.evt_scanner_legacy_advertisement_report),
evt->data.evt_scanner_legacy_advertisement_report.rssi);
(void)send_tag_found(evt->data.evt_scanner_legacy_advertisement_report.address.addr,
evt->data.evt_scanner_legacy_advertisement_report.address_type,
@@ -269,7 +270,7 @@ static void esl_lib_core_on_bt_event(sl_bt_msg_t *evt)
break;
case sl_bt_evt_system_resource_exhausted_id:
lib_status = ESL_LIB_STATUS_RESOURCE_EXCEEDED;
- esl_lib_log_core_error("Resource exhausted" APP_LOG_NL);
+ esl_lib_log_core_warning("Resource exhausted" APP_LOG_NL);
(void)send_core_error(lib_status,
SL_STATUS_ALLOCATION_FAILED,
ap_state->core_state);
@@ -277,6 +278,7 @@ static void esl_lib_core_on_bt_event(sl_bt_msg_t *evt)
default:
break;
}
+
if (lib_critical_error) {
esl_lib_log_core_critical("Critical error, exiting..." APP_LOG_NL);
// Send error
@@ -302,7 +304,7 @@ static void parse_config(char *config, esl_lib_ap_state_t *data)
sc = simple_argparse_init(arg_descriptor, &handle);
if (sc != SL_STATUS_OK) {
- esl_lib_log_core_critical("Failed to initialize AP Host Library! sc = 0x%04x" APP_LOG_NL, sc);
+ esl_lib_log_core_critical("Failed to initialize AP host library! sc = 0x%04x" APP_LOG_NL, sc);
exit(EXIT_FAILURE);
}
@@ -315,9 +317,9 @@ static void parse_config(char *config, esl_lib_ap_state_t *data)
simple_argparse_deinit(handle);
exit(EXIT_FAILURE);
} else {
- esl_lib_log_core_info("AP Host Library parsed commands: %d" APP_LOG_NL, parsed_count);
+ esl_lib_log_core_debug("AP host library parsed commands: %d" APP_LOG_NL, parsed_count);
for (size_t i = 0; i < parsed_count; ++i) {
- esl_lib_log_core_info("%s : %s" APP_LOG_NL, parsed[i].arg, parsed[i].opt);
+ esl_lib_log_core_debug("%s : %s" APP_LOG_NL, parsed[i].arg, parsed[i].opt);
if (strcmp(arg_descriptor[0].arg, parsed[i].arg) == 0) {
// Connection type
@@ -340,6 +342,10 @@ static void parse_config(char *config, esl_lib_ap_state_t *data)
if (strcmp("no", parsed[i].opt) == 0) {
(void)ncp_host_set_option('f', parsed[i].opt);
}
+ } else if (strcmp(arg_descriptor[4].arg, parsed[i].arg) == 0) {
+ int status = ncp_host_set_option('s', NULL);
+ esl_lib_log_core_debug("Attempt to enable NCP encryption, result status: 0x%04x" APP_LOG_NL,
+ status);
}
}
}
@@ -349,7 +355,7 @@ static void parse_config(char *config, esl_lib_ap_state_t *data)
esl_lib_log_core_critical("Failed to deinit arparse, sc = 0x%04x" APP_LOG_NL, sc);
exit(EXIT_FAILURE);
}
- esl_lib_log_core_info("AP Host Library initialized" APP_LOG_NL);
+ esl_lib_log_core_debug("AP host library configured" APP_LOG_NL);
}
/***************************************************************************//**
@@ -390,7 +396,7 @@ static sl_status_t send_scan_status(void)
sl_status_t sc;
esl_lib_evt_t *lib_evt;
- esl_lib_log_core_info("Scanning = %u" APP_LOG_NL, ap_state->scan.enabled);
+ esl_lib_log_core_debug("Scanning = %u" APP_LOG_NL, ap_state->scan.enabled);
sc = esl_lib_event_list_allocate(ESL_LIB_EVT_SCAN_STATUS,
0,
@@ -411,7 +417,7 @@ static sl_status_t send_scan_status(void)
}
static sl_status_t send_tag_found(uint8_t *addr,
- uint8_t addr_type,
+ uint8_t address_type,
int8_t rssi)
{
sl_status_t sc;
@@ -423,7 +429,7 @@ static sl_status_t send_tag_found(uint8_t *addr,
if (sc == SL_STATUS_OK) {
lib_evt->evt_code = ESL_LIB_EVT_TAG_FOUND;
lib_evt->data.evt_tag_found.rssi = rssi;
- lib_evt->data.evt_tag_found.address.addr_type = addr_type;
+ lib_evt->data.evt_tag_found.address.address_type = address_type;
// Copy address
memcpy(lib_evt->data.evt_tag_found.address.addr,
addr,
@@ -456,7 +462,10 @@ static sl_status_t send_core_error(esl_lib_status_t lib_status,
static void run_command(esl_lib_command_list_cmd_t *cmd)
{
sl_status_t sc = SL_STATUS_FAIL;
- esl_lib_status_t lib_status = ESL_LIB_STATUS_NO_ERROR;
+ esl_lib_status_t lib_status = ESL_LIB_STATUS_UNKNOWN_COMMAND;
+ esl_lib_node_id_t node_id;
+
+ node_id.type = ESL_LIB_NODE_ID_TYPE_NONE;
if (cmd != NULL) {
switch (cmd->cmd_code) {
@@ -500,17 +509,6 @@ static void run_command(esl_lib_command_list_cmd_t *cmd)
}
ap_state->command_complete = true;
break;
- case ESL_LIB_CMD_AP_CONTROL_INIT_GATTDB:
- esl_lib_log_core_debug("Command: AP control init GATT DB" APP_LOG_NL);
- lib_status = ESL_LIB_STATUS_CONTROL_FAILED;
- sc = esl_lib_ap_control_init();
- if (sc == SL_STATUS_OK) {
- lib_status = ESL_LIB_STATUS_NO_ERROR;
- } else {
- esl_lib_log_core_error("Failed to initialize AP control GATTDB, sc = 0x%04x" APP_LOG_NL, sc);
- }
- ap_state->command_complete = true;
- break;
case ESL_LIB_CMD_GET_SCAN_STATUS:
send_scan_status();
lib_status = ESL_LIB_STATUS_NO_ERROR;
@@ -576,13 +574,21 @@ static void run_command(esl_lib_command_list_cmd_t *cmd)
// Assume that the connect command is failed
// and try to initiate the connection.
lib_status = ESL_LIB_STATUS_CONN_FAILED;
- sc = esl_lib_connection_open(cmd, ESL_LIB_INVALID_HANDLE);
+ node_id.type = ESL_LIB_NODE_ID_TYPE_ADDRESS;
+ node_id.id.address.address_type = cmd->data.cmd_connect.address.address_type;
+ memcpy(node_id.id.address.addr, cmd->data.cmd_connect.address.addr, sizeof(bd_addr));
+ sc = esl_lib_connection_open(cmd);
if (sc == SL_STATUS_OK) {
lib_status = ESL_LIB_STATUS_NO_ERROR;
} else {
- esl_lib_log_core_error("Failed to open connection, sc = 0x%04x" APP_LOG_NL, sc);
+ esl_lib_log(((sc == SL_STATUS_BT_CTRL_CONNECTION_LIMIT_EXCEEDED) \
+ ? ESL_LIB_LOG_LEVEL_WARNING : ESL_LIB_LOG_LEVEL_ERROR),
+ ESL_LIB_LOG_MODULE_CORE,
+ "Failed to open connection, sc = 0x%04x" APP_LOG_NL, sc);
+ ap_state->command_complete = true;
+ esl_lib_memory_free(ap_state->command);
}
- ap_state->command_complete = true;
+ ap_state->command = NULL;
break;
default:
break; // default
@@ -590,14 +596,15 @@ static void run_command(esl_lib_command_list_cmd_t *cmd)
}
if (sc != SL_STATUS_OK) {
- esl_lib_node_id_t node_id;
esl_lib_status_data_t status_data;
status_data = (esl_lib_status_data_t)ap_state->core_state;
- node_id.type = ESL_LIB_NODE_ID_TYPE_NONE;
- esl_lib_log_core_error("Error in BT state machine, lib status = %d, sc = 0x%04x, core status = %d" APP_LOG_NL,
- lib_status,
- sc,
- ap_state->core_state);
+ esl_lib_log(((sc == SL_STATUS_BT_CTRL_CONNECTION_LIMIT_EXCEEDED) \
+ ? ESL_LIB_LOG_LEVEL_WARNING : ESL_LIB_LOG_LEVEL_ERROR),
+ ESL_LIB_LOG_MODULE_CORE,
+ "State machine failure, lib status = %d, sc = 0x%04x, core status = %d" APP_LOG_NL,
+ lib_status,
+ sc,
+ ap_state->core_state);
// Send available data in the error message
(void)esl_lib_event_push_error(lib_status,
&node_id,
@@ -663,3 +670,30 @@ static bool find_service_in_advertisement(uint8_t *data, uint8_t len)
}
return false;
}
+
+static void esl_lib_core_internal_reset(void)
+{
+ esl_lib_evt_t *last_evt;
+
+ // Clean up current command, if any
+ if (ap_state->command != NULL) {
+ esl_lib_command_list_remove(&ap_state->command_list, ap_state->command);
+ ap_state->command = NULL;
+ }
+
+ // Clean command list
+ esl_lib_command_list_cleanup(&ap_state->command_list);
+ esl_lib_log_core_debug("Command list cleanup complete" APP_LOG_NL);
+
+ // Cleanup relationship
+ esl_lib_image_transfer_cleanup();
+ esl_lib_connection_cleanup();
+ esl_lib_pawr_cleanup();
+ esl_lib_ap_control_cleanup();
+
+ // Cleanup events
+ while ((last_evt = esl_lib_event_list_get_first()) != NULL) {
+ esl_lib_event_list_remove_first();
+ }
+ esl_lib_log_core_debug("Event list cleanup complete" APP_LOG_NL);
+}
diff --git a/app/bluetooth/common_host/esl_lib/esl_lib_core.h b/app/bluetooth/common_host/esl_lib/esl_lib_core.h
index 3a7742f4fd3..dad614234e0 100644
--- a/app/bluetooth/common_host/esl_lib/esl_lib_core.h
+++ b/app/bluetooth/common_host/esl_lib/esl_lib_core.h
@@ -88,6 +88,11 @@ void esl_lib_deinit(void);
*****************************************************************************/
sl_status_t esl_lib_core_add_command(esl_lib_command_list_cmd_t *cmd);
+/**************************************************************************//**
+ * Connection request complete callback.
+ *****************************************************************************/
+void esl_lib_core_connection_complete(void);
+
#ifdef __cplusplus
};
#endif
diff --git a/app/bluetooth/common_host/esl_lib/esl_lib_event_list.c b/app/bluetooth/common_host/esl_lib/esl_lib_event_list.c
index 6692804c533..fa131598f88 100644
--- a/app/bluetooth/common_host/esl_lib/esl_lib_event_list.c
+++ b/app/bluetooth/common_host/esl_lib/esl_lib_event_list.c
@@ -135,6 +135,9 @@ sl_status_t esl_lib_event_list_allocate(esl_lib_evt_type_t event_type,
case ESL_LIB_EVT_BONDING_DATA:
size += sizeof(esl_lib_evt_bonding_data_t);
break;
+ case ESL_LIB_EVT_BONDING_FINISHED:
+ size += sizeof(esl_lib_evt_bonding_finished_t);
+ break;
case ESL_LIB_EVT_PAWR_STATUS:
size += sizeof(esl_lib_evt_pawr_status_t);
break;
diff --git a/app/bluetooth/common_host/esl_lib/esl_lib_image_transfer.c b/app/bluetooth/common_host/esl_lib/esl_lib_image_transfer.c
index e3111c74778..11c9fd75a9d 100644
--- a/app/bluetooth/common_host/esl_lib/esl_lib_image_transfer.c
+++ b/app/bluetooth/common_host/esl_lib/esl_lib_image_transfer.c
@@ -49,8 +49,8 @@
#define ATT_ERR_OFFSET 0x1100
#define IT_FMT ESL_LIB_LOG_HANDLE_FORMAT
#define TIMEOUT_INIT_MS 10000
-#define TIMEOUT_GATT_MS 1000
-#define TIMEOUT_TRANSFER_MS 60000
+#define TIMEOUT_GATT_MS 2000
+#define TIMEOUT_TRANSFER_BASE_MS 10
typedef enum {
OTS_STATE_NOT_INITIALIZED, // Initial state
@@ -113,7 +113,7 @@ static sl_status_t search_image(image_transfer_t *image_transfer,
uint8_t image_index,
bool *current_object_selected);
static void start_requested_operation(image_transfer_t *image_transfer);
-static void remove_transfer(image_transfer_t *image_transfer,
+static void remove_transfer(image_transfer_t **image_transfer,
sl_status_t sc,
bool finish_transfer);
static void operation_finished(image_transfer_t *image_transfer,
@@ -197,8 +197,8 @@ sl_status_t esl_lib_image_transfer_init(uint8_t
sl_bt_ots_gattdb_handles_t ots_handles;
sl_bt_ots_gattdb_handles_t *ots_handle_ptr = NULL;
- esl_lib_log_it_info("Image transfer init, connection handle = %u" APP_LOG_NL,
- connection);
+ esl_lib_log_it_debug("Image transfer init, connection handle = %u" APP_LOG_NL,
+ connection);
// Check for NULLs
if (handle_out == NULL
@@ -217,14 +217,9 @@ sl_status_t esl_lib_image_transfer_init(uint8_t
if (transfer == ESL_LIB_IMAGE_TRANSFER_INVALID_HANDLE) {
return SL_STATUS_ALLOCATION_FAILED;
} else {
+ memset(transfer, 0, sizeof(*transfer));
transfer->image_transfer_handle = (esl_lib_image_transfer_handle_t)transfer;
- transfer->ots_state = OTS_STATE_NOT_INITIALIZED;
- transfer->ots_ongoing_command = OTS_COMMAND_NONE;
- transfer->transfer_state = ESL_LIB_IMAGE_TRANSFER_STATE_NOT_INITIALIZED;
- transfer->result = SL_STATUS_OK;
- transfer->ots_write_data = NULL;
transfer->connection_handle = connection;
- memset(&transfer->ots_current_object_id, 0, sizeof(transfer->ots_current_object_id));
if (gattdb_handles != NULL) {
memcpy(ots_handles.characteristics.array,
@@ -232,8 +227,8 @@ sl_status_t esl_lib_image_transfer_init(uint8_t
sizeof(ots_handles.characteristics.array));
ots_handles.service = ots_service_handle;
ots_handle_ptr = &ots_handles;
- esl_lib_log_it_info("Image transfer GATTDB specified, connection handle = %u" APP_LOG_NL,
- connection);
+ esl_lib_log_it_debug("Image transfer GATTDB specified, connection handle = %u" APP_LOG_NL,
+ connection);
}
// Initialize OTS client
@@ -253,9 +248,9 @@ sl_status_t esl_lib_image_transfer_init(uint8_t
*handle_out = transfer->image_transfer_handle;
- esl_lib_log_it_info(IT_FMT "Image transfer init started, connection handle = %u" APP_LOG_NL,
- transfer->image_transfer_handle,
- connection);
+ esl_lib_log_it_debug(IT_FMT "Image transfer init started, connection handle = %u" APP_LOG_NL,
+ transfer->image_transfer_handle,
+ connection);
// Start timer
app_timer_start(&transfer->timer,
TIMEOUT_INIT_MS,
@@ -280,9 +275,9 @@ sl_status_t esl_lib_image_transfer_get_type(esl_lib_image_transfer_handle_t hand
sl_status_t sc;
image_transfer_t *image_transfer;
- esl_lib_log_it_info(IT_FMT "Get type for image index %u" APP_LOG_NL,
- handle,
- image_index);
+ esl_lib_log_it_debug(IT_FMT "Get type for image index %u" APP_LOG_NL,
+ handle,
+ image_index);
if (handle == ESL_LIB_IMAGE_TRANSFER_INVALID_HANDLE) {
return SL_STATUS_INVALID_HANDLE;
@@ -323,6 +318,7 @@ sl_status_t esl_lib_image_transfer_get_type(esl_lib_image_transfer_handle_t hand
// Requested object is selected, move on with reading the type
sc = sl_bt_ots_client_read_object_type(&image_transfer->ots_client);
if (sc == SL_STATUS_OK) {
+ (void)app_timer_stop(&image_transfer->timer);
// Start timer
app_timer_start(&image_transfer->timer,
TIMEOUT_GATT_MS,
@@ -363,9 +359,9 @@ sl_status_t esl_lib_image_transfer_start(esl_lib_image_transfer_handle_t handle,
sl_status_t sc;
image_transfer_t *image_transfer;
- esl_lib_log_it_info(IT_FMT "Image transfer for image index %u" APP_LOG_NL,
- handle,
- image_index);
+ esl_lib_log_it_debug(IT_FMT "Image transfer for image index %u" APP_LOG_NL,
+ handle,
+ image_index);
// Check parameters
if (handle == ESL_LIB_IMAGE_TRANSFER_INVALID_HANDLE) {
@@ -410,9 +406,11 @@ sl_status_t esl_lib_image_transfer_start(esl_lib_image_transfer_handle_t handle,
handle,
image_index);
} else {
- esl_lib_log_it_debug(IT_FMT "Starting Image Transfer for current image, index %u" APP_LOG_NL,
+ esl_lib_log_it_debug(IT_FMT "Starting image transfer for current image, index %u, size %u bytes." APP_LOG_NL,
handle,
- image_index);
+ image_index,
+ (uint32_t)image_size);
+
// Current object is selected, move on with writing the object.
sc = sl_bt_ots_client_oacp_write(&image_transfer->ots_client,
(uint32_t)image_offset,
@@ -421,12 +419,15 @@ sl_status_t esl_lib_image_transfer_start(esl_lib_image_transfer_handle_t handle,
L2CAP_CHANNEL_SDU,
L2CAP_CHANNEL_PDU);
if (sc == SL_STATUS_OK) {
+ (void)app_timer_stop(&image_transfer->timer);
// Start timer
- app_timer_start(&image_transfer->timer,
- TIMEOUT_TRANSFER_MS,
- transfer_timeout,
- image_transfer,
- false);
+ sc = app_timer_start(&image_transfer->timer,
+ TIMEOUT_TRANSFER_BASE_MS * image_size,
+ transfer_timeout,
+ image_transfer,
+ false);
+ }
+ if (sc == SL_STATUS_OK) {
image_transfer->ots_ongoing_command = OTS_COMMAND_WRITE;
image_transfer->ots_state = OTS_STATE_WRITE;
set_state(image_transfer,
@@ -458,8 +459,8 @@ sl_status_t esl_lib_image_transfer_abort(esl_lib_image_transfer_handle_t handle)
sl_status_t sc;
image_transfer_t *image_transfer;
- esl_lib_log_it_info(IT_FMT "Image transfer abort" APP_LOG_NL,
- handle);
+ esl_lib_log_it_debug(IT_FMT "Image transfer abort" APP_LOG_NL,
+ handle);
if (handle == ESL_LIB_IMAGE_TRANSFER_INVALID_HANDLE) {
return SL_STATUS_INVALID_HANDLE;
@@ -491,8 +492,8 @@ sl_status_t esl_lib_image_transfer_get_features(esl_lib_image_transfer_handle_t
sl_status_t sc = SL_STATUS_OK;
image_transfer_t *image_transfer;
- esl_lib_log_it_info(IT_FMT "Image transfer get features" APP_LOG_NL,
- handle);
+ esl_lib_log_it_debug(IT_FMT "Image transfer get features" APP_LOG_NL,
+ handle);
if (handle == ESL_LIB_IMAGE_TRANSFER_INVALID_HANDLE) {
return SL_STATUS_INVALID_HANDLE;
}
@@ -523,6 +524,11 @@ void esl_lib_image_transfer_on_bt_event(sl_bt_msg_t *evt)
{
sli_bt_ots_client_on_bt_event(evt);
sli_bt_l2cap_transfer_on_bt_event(evt);
+
+ // Post process any (unsolicited?) boot event
+ if (SL_BT_MSG_ID(evt->header) == sl_bt_evt_system_boot_id) {
+ esl_lib_image_transfer_cleanup();
+ }
}
void esl_lib_image_transfer_step(void)
@@ -531,6 +537,18 @@ void esl_lib_image_transfer_step(void)
sli_bt_l2cap_transfer_process_action();
}
+void esl_lib_image_transfer_cleanup(void)
+{
+ image_transfer_t *image_transfer;
+ while ((image_transfer = (image_transfer_t *)sl_slist_pop(&image_transfer_list)) != NULL) {
+ (void)app_timer_stop(&image_transfer->timer);
+ remove_transfer(&image_transfer,
+ SL_STATUS_DELETED,
+ false);
+ }
+ esl_lib_log_it_debug("Image transfer cleanup complete" APP_LOG_NL);
+}
+
// -----------------------------------------------------------------------------
// Private functions
@@ -553,6 +571,7 @@ static sl_status_t search_image(image_transfer_t *image_transfer,
&image_transfer->ots_current_object_id);
if (!*current_object_requested) {
+ (void)app_timer_stop(&image_transfer->timer);
// Check server capabilities to GOTO
bool goto_supported = (image_transfer->ots_server_features.olcp_features
& SL_BT_OTS_OLCP_FEATURE_GO_TO_OP_CODE_SUPPORTED_MASK);
@@ -593,6 +612,10 @@ static sl_status_t search_image(image_transfer_t *image_transfer,
ESL_LIB_IMAGE_TRANSFER_STATE_BUSY,
SL_STATUS_OK,
NULL);
+ } else {
+ esl_lib_log_it_debug(IT_FMT "Write to the currently selected image, index %u" APP_LOG_NL,
+ image_transfer->image_transfer_handle,
+ image_index);
}
}
}
@@ -729,6 +752,7 @@ static sl_status_t ots_error_to_sl_status(uint16_t att_error,
static void start_requested_operation(image_transfer_t *image_transfer)
{
sl_status_t sc;
+ (void)app_timer_stop(&image_transfer->timer);
if (image_transfer->ots_ongoing_command == OTS_COMMAND_OBJECT_TYPE) {
esl_lib_log_it_debug(IT_FMT "Reading object type of current object" APP_LOG_NL,
image_transfer->image_transfer_handle);
@@ -750,8 +774,9 @@ static void start_requested_operation(image_transfer_t *image_transfer)
operation_finished(image_transfer, sc, false);
}
} else if (image_transfer->ots_ongoing_command == OTS_COMMAND_WRITE) {
- esl_lib_log_it_debug(IT_FMT "Write current object" APP_LOG_NL,
- image_transfer->image_transfer_handle);
+ esl_lib_log_it_debug(IT_FMT "Write current object of size %u bytes." APP_LOG_NL,
+ image_transfer->image_transfer_handle,
+ image_transfer->ots_write_size);
// Current object is selected, move on with writing the object.
sc = sl_bt_ots_client_oacp_write(&image_transfer->ots_client,
@@ -763,7 +788,7 @@ static void start_requested_operation(image_transfer_t *image_transfer)
if (sc == SL_STATUS_OK) {
// Start timer
app_timer_start(&image_transfer->timer,
- TIMEOUT_TRANSFER_MS,
+ TIMEOUT_TRANSFER_BASE_MS * image_transfer->ots_write_size,
transfer_timeout,
image_transfer,
false);
@@ -778,25 +803,26 @@ static void start_requested_operation(image_transfer_t *image_transfer)
}
}
-static void remove_transfer(image_transfer_t *image_transfer,
+static void remove_transfer(image_transfer_t **image_transfer,
sl_status_t sc,
bool finish_transfer)
{
+ (void)app_timer_stop(&(*image_transfer)->timer);
esl_lib_log_it_debug(IT_FMT "Removing transfer" APP_LOG_NL,
- image_transfer->image_transfer_handle);
- sl_slist_remove(&image_transfer_list, &image_transfer->node);
+ (*image_transfer)->image_transfer_handle);
+ sl_slist_remove(&image_transfer_list, &(*image_transfer)->node);
if (finish_transfer) {
esl_lib_log_it_debug(IT_FMT "Finishing transfer" APP_LOG_NL,
- image_transfer->image_transfer_handle);
- image_transfer->cb_finish((esl_lib_image_transfer_handle_t)image_transfer,
- image_transfer->connection_handle,
- sc,
- image_transfer->requested_image_index);
+ (*image_transfer)->image_transfer_handle);
+ (*image_transfer)->cb_finish((esl_lib_image_transfer_handle_t)image_transfer,
+ (*image_transfer)->connection_handle,
+ sc,
+ (*image_transfer)->requested_image_index);
}
- set_state(image_transfer, ESL_LIB_IMAGE_TRANSFER_REMOVED, sc, NULL);
+ set_state(*image_transfer, ESL_LIB_IMAGE_TRANSFER_REMOVED, sc, NULL);
// Remove transfer
- esl_lib_memory_free(image_transfer);
+ esl_lib_memory_free(*image_transfer);
}
static void operation_finished(image_transfer_t *image_transfer,
@@ -804,11 +830,11 @@ static void operation_finished(image_transfer_t *image_transfer,
bool finish_transfer)
{
if (sc == SL_STATUS_OK) {
- esl_lib_log_it_debug(IT_FMT "Operation finished, sc = 0x%04x" APP_LOG_NL,
+ esl_lib_log_it_debug(IT_FMT "OTS operation succeeded, sc = 0x%04x" APP_LOG_NL,
image_transfer->image_transfer_handle,
sc);
} else {
- esl_lib_log_it_error(IT_FMT "Operation finished, sc = 0x%04x" APP_LOG_NL,
+ esl_lib_log_it_error(IT_FMT "OTS operation failed, sc = 0x%04x" APP_LOG_NL,
image_transfer->image_transfer_handle,
sc);
}
@@ -872,17 +898,13 @@ static void ots_init(sl_bt_ots_client_handle_t client,
esl_lib_log_it_error(IT_FMT "Failed to read features, sc = 0x%04x" APP_LOG_NL,
image_transfer->image_transfer_handle,
sc);
- remove_transfer(image_transfer, sc, false);
+ remove_transfer(&image_transfer, sc, false);
}
} else {
esl_lib_log_it_error(IT_FMT "OTS not initialized, sc = 0x%04x" APP_LOG_NL,
image_transfer->image_transfer_handle,
result);
- remove_transfer(image_transfer, result, false);
- set_state(image_transfer,
- ESL_LIB_IMAGE_TRANSFER_REMOVED,
- result,
- NULL);
+ remove_transfer(&image_transfer, result, false);
}
}
}
@@ -915,7 +937,7 @@ static void ots_features(sl_bt_ots_client_handle_t client,
image_transfer->image_transfer_handle,
status);
// Error during feature read
- remove_transfer(image_transfer, status, false);
+ remove_transfer(&image_transfer, status, false);
}
}
}
@@ -1118,10 +1140,10 @@ static void ots_oacp(sl_bt_ots_client_handle_t client,
(void)object;
image_transfer_t *image_transfer = find_image_transfer_by_client(client);
if (image_transfer != NULL) {
- // Stop timer
- app_timer_stop(&image_transfer->timer);
sl_status_t sc = ots_error_to_sl_status(status, response);
if (opcode == SL_BT_OTS_OACP_OPCODE_WRITE && sc != SL_STATUS_OK) {
+ // Stop timer
+ app_timer_stop(&image_transfer->timer);
esl_lib_log_it_error(IT_FMT "OTS OACP Write operation failed, status = 0x%04x, response = 0x%04x, sc = 0x%04x" APP_LOG_NL,
image_transfer->image_transfer_handle,
status,
@@ -1204,7 +1226,7 @@ static void ots_disconnect(sl_bt_ots_client_handle_t client)
esl_lib_log_it_debug(IT_FMT "OTS disconnected" APP_LOG_NL,
image_transfer->image_transfer_handle);
sc = SL_STATUS_BT_CTRL_REMOTE_USER_TERMINATED;
- remove_transfer(image_transfer,
+ remove_transfer(&image_transfer,
sc,
image_transfer->ots_ongoing_command == OTS_COMMAND_WRITE);
}
@@ -1217,11 +1239,7 @@ static void init_timeout(app_timer_t *timer,
esl_lib_log_it_error(IT_FMT "OTS init timeout" APP_LOG_NL,
image_transfer->image_transfer_handle);
// Remove transfer that could not be initialized
- set_state(image_transfer,
- ESL_LIB_IMAGE_TRANSFER_REMOVED,
- SL_STATUS_TIMEOUT,
- NULL);
- remove_transfer(image_transfer, SL_STATUS_TIMEOUT, false);
+ remove_transfer(&image_transfer, SL_STATUS_TIMEOUT, false);
}
static void gatt_timeout(app_timer_t *timer,
diff --git a/app/bluetooth/common_host/esl_lib/esl_lib_image_transfer.h b/app/bluetooth/common_host/esl_lib/esl_lib_image_transfer.h
index ed48191ee0a..1fac377cd5a 100644
--- a/app/bluetooth/common_host/esl_lib/esl_lib_image_transfer.h
+++ b/app/bluetooth/common_host/esl_lib/esl_lib_image_transfer.h
@@ -105,6 +105,11 @@ typedef void (*esl_lib_image_transfer_type_callback_t)(esl_lib_image_transfer_ha
// -----------------------------------------------------------------------------
// Public functions
+/**************************************************************************//**
+ * Clean all imagetransfer data.
+ *****************************************************************************/
+void esl_lib_image_transfer_cleanup(void);
+
/***************************************************************************//**
* Initialize ESL AP Image Transfer.
* @param[in] connection Connection handle.
diff --git a/app/bluetooth/common_host/esl_lib/esl_lib_log.h b/app/bluetooth/common_host/esl_lib/esl_lib_log.h
index 2593833be05..88e778c37b6 100644
--- a/app/bluetooth/common_host/esl_lib/esl_lib_log.h
+++ b/app/bluetooth/common_host/esl_lib/esl_lib_log.h
@@ -42,16 +42,25 @@ extern "C" {
#include "app_log.h"
#define ESL_LIB_LOG_BUFFER_SIZE 2048
-#define ESL_LIB_LOG_ADDR_FORMAT " %02X:%02X:%02X:%02X:%02X:%02X type = %d "
-#define ESL_LIB_LOG_HANDLE_FORMAT "[%p] "
-#define ESL_LIB_LOG_ADDR(address) \
- (address).addr[5], \
- (address).addr[4], \
- (address).addr[3], \
- (address).addr[2], \
- (address).addr[1], \
- (address).addr[0], \
- (address).addr_type
+#define ESL_LIB_LOG_ADDR_FORMAT "type %u %s address: %02X:%02X:%02X:%02X:%02X:%02X"
+#define ESL_LIB_LOG_HANDLE_FORMAT "[0x%p] "
+#define ESL_LIB_LOG_ADDR(_addr) \
+ (_addr).address_type, \
+ ((_addr).address_type ? "random" : "public"), \
+ (_addr).address.addr[5], \
+ (_addr).address.addr[4], \
+ (_addr).address.addr[3], \
+ (_addr).address.addr[2], \
+ (_addr).address.addr[1], \
+ (_addr).address.addr[0]
+
+#define ESL_LIB_LOG_BD_ADDR(_bd_addr) \
+ (_bd_addr).addr[5], \
+ (_bd_addr).addr[4], \
+ (_bd_addr).addr[3], \
+ (_bd_addr).addr[2], \
+ (_bd_addr).addr[1], \
+ (_bd_addr).addr[0]
/// Logging structure type
typedef struct {
diff --git a/app/bluetooth/common_host/esl_lib/esl_lib_memory.c b/app/bluetooth/common_host/esl_lib/esl_lib_memory.c
index aaf49c0eef8..d86805169db 100644
--- a/app/bluetooth/common_host/esl_lib/esl_lib_memory.c
+++ b/app/bluetooth/common_host/esl_lib/esl_lib_memory.c
@@ -77,6 +77,13 @@ void *_esl_lib_malloc(size_t size, const char *file, uint32_t line)
item->file = (char *)file;
// Push it to the list
sl_slist_push(&list, &item->node);
+ if (item->file[8] != 'e') { // event list allocations excluded
+ esl_lib_log_debug(LOG_MODULE, "%8p Size = %zu allocated in %s:%u" APP_LOG_NL,
+ item->ptr,
+ item->size,
+ item->file,
+ item->line);
+ }
// Return pointer to the allocated memory
return data;
}
@@ -91,6 +98,13 @@ void _esl_lib_free(void *ptr, const char *file, uint32_t line)
if (item != NULL) {
// Remove from the list
sl_slist_remove(&list, &item->node);
+ if (item->file[8] != 'e') { // events excluded
+ esl_lib_log_debug(LOG_MODULE, "%8p Size = %zu freed in %s:%u" APP_LOG_NL,
+ item->ptr,
+ item->size,
+ item->file,
+ item->line);
+ }
// Free storage
free(ptr);
// Free list item
diff --git a/app/bluetooth/common_host/esl_lib/esl_lib_memory.h b/app/bluetooth/common_host/esl_lib/esl_lib_memory.h
index 2963bbc227a..c7e1cfaaf07 100644
--- a/app/bluetooth/common_host/esl_lib/esl_lib_memory.h
+++ b/app/bluetooth/common_host/esl_lib/esl_lib_memory.h
@@ -41,7 +41,7 @@ extern "C" {
#ifdef ESL_LIB_MEMORY_LEAK_CHECK
#define esl_lib_memory_allocate(size) _esl_lib_malloc(size, __FILE__, __LINE__)
-#define esl_lib_memory_free(ptr) _esl_lib_free(ptr, __FILE__, __LINE__)
+#define esl_lib_memory_free(ptr) do { _esl_lib_free(ptr, __FILE__, __LINE__); ptr = NULL; } while (0)
// Internal allocator function
void *_esl_lib_malloc(size_t size, const char *file, uint32_t line);
// Internal free function
@@ -69,7 +69,7 @@ size_t esl_lib_memory_get_size(void);
#else // ESL_LIB_MEMORY_LEAK_CHECK
#define esl_lib_memory_allocate(size) malloc(size)
-#define esl_lib_memory_free(ptr) free(ptr)
+#define esl_lib_memory_free(ptr) do { free(ptr); ptr = NULL; } while (0)
#define esl_lib_memory_log()
#define esl_lib_memory_get_count() 0
#define esl_lib_memory_get_size() 0
diff --git a/app/bluetooth/common_host/esl_lib/esl_lib_pawr.c b/app/bluetooth/common_host/esl_lib/esl_lib_pawr.c
index b4fbbb23580..79daf47945d 100644
--- a/app/bluetooth/common_host/esl_lib/esl_lib_pawr.c
+++ b/app/bluetooth/common_host/esl_lib/esl_lib_pawr.c
@@ -42,6 +42,11 @@
// Value for no flag present for the advertiser
#define PERIODIC_ADVERTISER_FLAG_NONE 0
+// Maximum count of PAWR set data call within one burst
+// NOTE: best to align with NCP ESL AP's SL_BT_CONFIG_PAWR_PACKET_REQUEST_COUNT!
+#define PAWR_DATA_MAX_BURST_READ 4
+
+// PAwR handle print formatter alias
#define PAWR_FMT ESL_LIB_LOG_HANDLE_FORMAT
typedef enum {
@@ -61,7 +66,7 @@ static sl_status_t send_pawr_error(esl_lib_pawr_t *pawr,
esl_lib_status_t lib_status,
sl_status_t status,
esl_lib_pawr_state_t data);
-static void run_command(esl_lib_command_list_cmd_t *cmd);
+static sl_status_t run_command(esl_lib_command_list_cmd_t *cmd);
static sl_status_t send_pawr_status(esl_lib_pawr_t *pawr_ptr);
// -----------------------------------------------------------------------------
@@ -91,12 +96,12 @@ sl_status_t esl_lib_pawr_add(esl_lib_pawr_t **ptr_out)
// Set handle
ptr->pawr_handle = SL_BT_INVALID_ADVERTISING_SET_HANDLE;
// Set command parameters
- ptr->command_complete = true;
ptr->command = NULL;
ptr->command_list = NULL;
// Set states
ptr->enabled = ESL_LIB_FALSE;
ptr->configured = ESL_LIB_FALSE;
+ ptr->state = ESL_LIB_PAWR_STATE_IDLE;
// Config default values
ptr->config.adv_interval.min = ESL_LIB_PERIODIC_ADV_MIN_INTERVAL_DEFAULT;
ptr->config.adv_interval.max = ESL_LIB_PERIODIC_ADV_MAX_INTERVAL_DEFAULT;
@@ -152,7 +157,7 @@ sl_status_t esl_lib_pawr_remove_ptr(esl_lib_pawr_t *ptr)
}
// Delete storage
- (void)esl_lib_storage_delete(ptr->storage_handle);
+ (void)esl_lib_storage_delete(&ptr->storage_handle);
esl_lib_log_pawr_debug(PAWR_FMT "Removed PAwR" APP_LOG_NL, ptr);
// Delete PAwR data
esl_lib_memory_free(ptr);
@@ -170,7 +175,7 @@ sl_status_t esl_lib_pawr_remove_handle(uint8_t pawr,
if (sc == SL_STATUS_OK) {
sc = esl_lib_pawr_remove_ptr(ptr);
if (sc == SL_STATUS_OK) {
- *ptr_out = ptr;
+ *ptr_out = NULL;
}
}
return sc;
@@ -195,15 +200,16 @@ void esl_lib_pawr_cleanup(void)
esl_lib_pawr_t *pawr;
// Clean PAwR list
while ((pawr = (esl_lib_pawr_t *)sl_slist_pop(&pawr_list)) != NULL) {
+ (void)sl_bt_periodic_advertiser_stop(pawr->pawr_handle);
esl_lib_command_list_cleanup(&pawr->command_list);
sl_slist_remove(&pawr_list, &pawr->node);
- (void)esl_lib_storage_delete(pawr->storage_handle);
+ (void)esl_lib_storage_delete(&pawr->storage_handle);
if (pawr->command != NULL) {
esl_lib_memory_free(pawr->command);
}
esl_lib_memory_free(pawr);
}
- esl_lib_log_pawr_debug("PAwR cleanup" APP_LOG_NL);
+ esl_lib_log_pawr_debug("PAwR cleanup complete" APP_LOG_NL);
}
sl_status_t esl_lib_pawr_add_command(esl_lib_pawr_t *pawr,
@@ -223,7 +229,7 @@ sl_status_t esl_lib_pawr_add_command(esl_lib_pawr_t *pawr,
pawr,
cmd->cmd_code);
} else {
- esl_lib_log_pawr_error(PAWR_FMT "Add command %d failed, sc = %04x" APP_LOG_NL,
+ esl_lib_log_pawr_error(PAWR_FMT "Add command %d failed, sc = 0x%04x" APP_LOG_NL,
pawr,
cmd->cmd_code,
sc);
@@ -234,25 +240,40 @@ sl_status_t esl_lib_pawr_add_command(esl_lib_pawr_t *pawr,
void esl_lib_pawr_step(void)
{
esl_lib_pawr_t *pawr;
- esl_lib_command_list_cmd_t *cmd;
SL_SLIST_FOR_EACH_ENTRY(pawr_list, pawr, esl_lib_pawr_t, node) {
- if (pawr->command_complete) {
- // If there is an ongoing but complete command, remove that.
- if (pawr->command != NULL) {
+ uint8_t burst_count = PAWR_DATA_MAX_BURST_READ;
+ // Move and execute next command.
+ while ((pawr->command = esl_lib_command_list_get(&pawr->command_list)) != NULL
+ && burst_count--) {
+ sl_status_t sc;
+
+ esl_lib_log_pawr_debug(PAWR_FMT "Execute command burst, cmd = %d, cycles left %u" APP_LOG_NL,
+ pawr,
+ pawr->command->cmd_code,
+ burst_count);
+ sc = run_command(pawr->command);
+
+ if (sc == SL_STATUS_TRANSMIT && pawr->command->cmd_code == ESL_LIB_CMD_PAWR_SET_DATA) {
+ esl_lib_log_pawr_debug(PAWR_FMT "BREAK command burst due transmit failure, defer %u cycles" APP_LOG_NL,
+ pawr,
+ burst_count);
+ if (pawr->command->data.cmd_pawr_set_data.retry-- != 0) {
+ sl_slist_push(&pawr->command_list, &pawr->command->node); // retry "one step" later, keep command order
+ } else {
+ // Send pawr transmit error event if all retry attempt failed
+ (void)send_pawr_error(pawr,
+ ESL_LIB_STATUS_PAWR_SET_DATA_FAILED,
+ sc,
+ pawr->state);
+ esl_lib_command_list_remove(&pawr->command_list, pawr->command);
+ pawr->command = NULL;
+ }
+ return; // break full PAwR processing cycle anyway, if BT CTRL seems busy
+ } else {
esl_lib_command_list_remove(&pawr->command_list, pawr->command);
pawr->command = NULL;
}
- // Move and execute next command.
- cmd = esl_lib_command_list_get(&pawr->command_list);
- if (cmd != NULL) {
- esl_lib_log_pawr_debug(PAWR_FMT "Running next command = %d" APP_LOG_NL,
- pawr,
- cmd->cmd_code);
- pawr->command = cmd;
- pawr->command_complete = false;
- run_command(pawr->command);
- }
}
}
}
@@ -327,7 +348,7 @@ void esl_lib_pawr_on_bt_event(sl_bt_msg_t *evt)
break;
}
if (sc != SL_STATUS_OK && pawr_ptr != NULL) {
- esl_lib_log_pawr_error(PAWR_FMT "PAwR error, PAwR handle = %u, sc = %04x" APP_LOG_NL,
+ esl_lib_log_pawr_error(PAWR_FMT "PAwR error, PAwR handle = %u, sc = 0x%04x" APP_LOG_NL,
pawr_ptr,
pawr_ptr->pawr_handle,
sc);
@@ -423,7 +444,7 @@ static sl_status_t send_pawr_error(esl_lib_pawr_t *pawr,
return sc;
}
-static void run_command(esl_lib_command_list_cmd_t *cmd)
+static sl_status_t run_command(esl_lib_command_list_cmd_t *cmd)
{
sl_status_t sc = SL_STATUS_OK;
esl_lib_pawr_t *pawr = NULL;
@@ -476,29 +497,38 @@ static void run_command(esl_lib_command_list_cmd_t *cmd)
send_pawr_status(pawr);
}
}
- pawr->command_complete = true;
break;
case ESL_LIB_CMD_PAWR_SET_DATA:
lib_status = ESL_LIB_STATUS_PAWR_SET_DATA_FAILED;
pawr = (esl_lib_pawr_t *)cmd->data.cmd_pawr_set_data.pawr_handle;
- esl_lib_log_pawr_debug(PAWR_FMT "Set data command, PAwR handle = %u" APP_LOG_NL,
- pawr,
- pawr->pawr_handle);
+ // is it a retry attempt?
+ uint8_t retry_count = ESL_LIB_PAWR_SET_DATA_RETRY_COUNT_MAX - cmd->data.cmd_pawr_set_data.retry;
+
+ if (retry_count) {
+ esl_lib_log_pawr_warning("Retry set data %d / %d" APP_LOG_NL,
+ (retry_count),
+ ESL_LIB_PAWR_SET_DATA_RETRY_COUNT_MAX);
+ } else {
+ esl_lib_log_pawr_debug(PAWR_FMT "Set data command, PAwR handle = %u" APP_LOG_NL,
+ pawr,
+ pawr->pawr_handle);
+ }
sc = sl_bt_pawr_advertiser_set_subevent_data(pawr->pawr_handle,
cmd->data.cmd_pawr_set_data.subevent,
0,
- pawr->config.response_slot.count,
+ cmd->data.cmd_pawr_set_data.response_slot_max,
cmd->data.cmd_pawr_set_data.data.len,
cmd->data.cmd_pawr_set_data.data.data);
if (sc == SL_STATUS_OK) {
- esl_lib_log_pawr_debug(PAWR_FMT "Set data command succeeded, PAwR handle = %u" APP_LOG_NL,
+ esl_lib_log_pawr_debug(PAWR_FMT "Set data for subevent %u with %u expected responses, PAwR handle = %u" APP_LOG_NL,
pawr,
+ cmd->data.cmd_pawr_set_data.subevent,
+ cmd->data.cmd_pawr_set_data.response_slot_max,
pawr->pawr_handle);
lib_status = ESL_LIB_STATUS_NO_ERROR;
}
- pawr->command_complete = true;
break;
case ESL_LIB_CMD_PAWR_CONFIGURE:
pawr = (esl_lib_pawr_t *)cmd->data.cmd_pawr_config.pawr_handle;
@@ -512,28 +542,33 @@ static void run_command(esl_lib_command_list_cmd_t *cmd)
lib_status = ESL_LIB_STATUS_NO_ERROR;
// Set configured
pawr->configured = ESL_LIB_TRUE;
- pawr->command_complete = true;
break;
case ESL_LIB_CMD_GET_PAWR_STATUS:
pawr = (esl_lib_pawr_t *)cmd->data.cmd_get_pawr_status;
send_pawr_status(pawr);
sc = SL_STATUS_OK;
lib_status = ESL_LIB_STATUS_NO_ERROR;
- pawr->command_complete = true;
break;
default:
break;
}
if (sc != SL_STATUS_OK && pawr != NULL) {
- esl_lib_log_pawr_error(PAWR_FMT "Command error, PAwR handle = %u, sc = %04x" APP_LOG_NL,
- pawr,
- pawr->pawr_handle,
- sc);
-
- // Send connection error if connection is present.
- (void)send_pawr_error(pawr,
- lib_status,
- sc,
- pawr->state);
+ if (!(pawr->command->cmd_code == ESL_LIB_CMD_PAWR_SET_DATA
+ && (sc == SL_STATUS_BT_CTRL_COMMAND_DISALLOWED
+ || sc == SL_STATUS_BT_CTRL_PAWR_TOO_LATE
+ || sc == SL_STATUS_BT_CTRL_PAWR_TOO_EARLY))) {
+ esl_lib_log_pawr_error(PAWR_FMT "Command error, PAwR handle = %u, sc = 0x%04x" APP_LOG_NL,
+ pawr,
+ pawr->pawr_handle,
+ sc);
+ // Send pawr error event immediately in almost all cases, but various set data issues
+ (void)send_pawr_error(pawr,
+ lib_status,
+ sc,
+ pawr->state);
+ } else {
+ sc = SL_STATUS_TRANSMIT; // Convert to common status code for those set of failures
+ }
}
+ return sc;
}
diff --git a/app/bluetooth/common_host/esl_lib/esl_lib_pawr.h b/app/bluetooth/common_host/esl_lib/esl_lib_pawr.h
index e55eae62609..c3250d0b56f 100644
--- a/app/bluetooth/common_host/esl_lib/esl_lib_pawr.h
+++ b/app/bluetooth/common_host/esl_lib/esl_lib_pawr.h
@@ -47,12 +47,23 @@ extern "C" {
// -----------------------------------------------------------------------------
// Definitions
+///< Max re-send count on PAwR in case of SL_STATUS_BT_CTRL_COMMAND_DISALLOWED
+#define ESL_LIB_PAWR_SET_DATA_RETRY_COUNT_MAX 3
+
+///< Some SL_STATUS_BT_CTRL status codes that aren't defined in sl_status.h yet.
+#ifndef SL_STATUS_BT_CTRL_PAWR_TOO_LATE
+#define SL_STATUS_BT_CTRL_PAWR_TOO_LATE 0x1046
+#endif // SL_STATUS_BT_CTRL_PAWR_TOO_LATE
+
+#ifndef SL_STATUS_BT_CTRL_PAWR_TOO_EARLY
+#define SL_STATUS_BT_CTRL_PAWR_TOO_EARLY 0x1047
+#endif // SL_STATUS_BT_CTRL_PAWR_TOO_EARLY
+
/// PAwR list item type
typedef struct {
sl_slist_node_t node; ///< List node pointer
esl_lib_pawr_state_t state; ///< PAwR state
esl_lib_command_list_cmd_t *command; ///< Command in progress
- bool command_complete; ///< Finished command
sl_slist_node_t *command_list; ///< Command list
uint8_t pawr_handle; ///< PAwR handle
esl_lib_storage_handle_t storage_handle; ///< Storage handle
diff --git a/app/bluetooth/common_host/esl_lib/esl_lib_storage.c b/app/bluetooth/common_host/esl_lib/esl_lib_storage.c
index 9a36838ff48..986fd59cc0e 100644
--- a/app/bluetooth/common_host/esl_lib/esl_lib_storage.c
+++ b/app/bluetooth/common_host/esl_lib/esl_lib_storage.c
@@ -162,16 +162,16 @@ sl_status_t esl_lib_storage_clean(esl_lib_storage_handle_t handle)
return sc;
}
-sl_status_t esl_lib_storage_delete(esl_lib_storage_handle_t handle)
+sl_status_t esl_lib_storage_delete(esl_lib_storage_handle_t *handle)
{
sl_status_t sc;
// Clean storage
- sc = esl_lib_storage_clean(handle);
+ sc = esl_lib_storage_clean(*handle);
if (sc == SL_STATUS_OK) {
// Free up memory
- esl_lib_memory_free(handle);
+ esl_lib_memory_free(*handle);
}
return sc;
diff --git a/app/bluetooth/common_host/esl_lib/esl_lib_storage.h b/app/bluetooth/common_host/esl_lib/esl_lib_storage.h
index 61ee0e906fc..91029658d4f 100644
--- a/app/bluetooth/common_host/esl_lib/esl_lib_storage.h
+++ b/app/bluetooth/common_host/esl_lib/esl_lib_storage.h
@@ -106,7 +106,7 @@ sl_status_t esl_lib_storage_clean(esl_lib_storage_handle_t handle);
*
* @return Status code.
*****************************************************************************/
-sl_status_t esl_lib_storage_delete(esl_lib_storage_handle_t handle);
+sl_status_t esl_lib_storage_delete(esl_lib_storage_handle_t *handle);
#ifdef __cplusplus
};
diff --git a/app/bluetooth/common_host/esl_lib/makefile b/app/bluetooth/common_host/esl_lib/makefile
index 97e981fe5dc..7c1749b3730 100644
--- a/app/bluetooth/common_host/esl_lib/makefile
+++ b/app/bluetooth/common_host/esl_lib/makefile
@@ -5,6 +5,12 @@
PROJECTNAME = esl_lib
SDK_DIR = ../../../..
+# Enable security for ESL C LIB NCP by default
+# It can be disabled by assigning zero value to the SECURITY variable
+# e.g. via command line like 'make SECURITY=0'.
+# If enabled, make sure that openssl is available in your environment.
+SECURITY ?= 1
+
ifneq (, $(filter $(MAKECMDGOALS), export))
# Collect all resources when exporting.
MEMCHECK_DEFAULT = 1
@@ -15,6 +21,18 @@ endif
# Memory leak check is disabled by default. Enable it like this: make MEMCHECK=1
MEMCHECK ?= $(MEMCHECK_DEFAULT)
+# Security related settings
+ifneq ($(SECURITY), 0)
+ # Suppress OpenSSL 3.0 warnings until proper update is made on ncp_sec
+ override CFLAGS += -DOPENSSL_API_COMPAT=0x10101000L
+ # Override some secure NCP host component default settings
+ override CFLAGS += -DPEEK_US_SLEEP=1
+ override CFLAGS += -DRECV_FUNC_US_SLEEP=1
+ override CFLAGS += -DMSG_RECV_TIMEOUT_MS=2
+endif
+
+# NCP config override
+override CFLAGS += -DDEFAULT_HOST_BUFLEN=16384
################################################################################
# Components #
@@ -26,7 +44,7 @@ MEMCHECK ?= $(MEMCHECK_DEFAULT)
include $(SDK_DIR)/app/bluetooth/component_host/toolchain.mk
include $(SDK_DIR)/app/bluetooth/component_host/app_log.mk
include $(SDK_DIR)/app/bluetooth/component_host/app_assert.mk
-include $(SDK_DIR)/app/bluetooth/component_host/ncp_host_nothread.mk
+include $(SDK_DIR)/app/bluetooth/component_host/ncp_host_bt.mk
include $(SDK_DIR)/app/bluetooth/component_host/app_timer.mk
include $(SDK_DIR)/app/bluetooth/component_host/slist.mk
include $(SDK_DIR)/app/bluetooth/component_host/app_queue.mk
@@ -180,7 +198,7 @@ $(EXE_DIR)/$(PROJECTNAME): $(OBJS) $(LIBS)
$(PROJECTNAME)_wrapper.py: $(PROJECTNAME).h
@echo "Generating Python wrapper"
- ctypesgen --no-gnu-types -I$(SDK_DIR)/platform/common/inc $(PROJECTNAME).h $(SDK_DIR)/platform/common/inc/sl_status.h -x sl_status_get_string_n -x sl_status_print -o $(PROJECTNAME)_wrapper.py
+ ctypesgen --no-gnu-types --allow-gnu-c --no-macro-warnings -I$(SDK_DIR)/platform/common/inc $(PROJECTNAME).h $(SDK_DIR)/platform/common/inc/sl_status.h -x sl_status_get_string_n -x sl_status_print -o $(PROJECTNAME)_wrapper.py 2> /dev/null
@echo "Fixing up Python wrapper"
../../script/ctypesgen_wrapper_fix.py $(PROJECTNAME)_wrapper.py -v
diff --git a/app/bluetooth/common_host/host_comm/config/host_comm_config.h b/app/bluetooth/common_host/host_comm/config/host_comm_config.h
index 9f573fe01cc..096aad8333c 100644
--- a/app/bluetooth/common_host/host_comm/config/host_comm_config.h
+++ b/app/bluetooth/common_host/host_comm/config/host_comm_config.h
@@ -55,6 +55,30 @@
//
End Receive / Transmit buffer configuration
+// Robust
+
+// Message header
+// Robustify the communication by adding a message header and filter out invalid messages.
+// Note: This configuration should match on the sender and receiver side.
+// Default: Off
+#ifndef HOST_COMM_ROBUST
+#define HOST_COMM_ROBUST 0
+#endif // HOST_COMM_ROBUST
+
+// CRC
+// Add payload CRC and perform checking.
+// Note: This configuration should match on the sender and receiver side.
+// Default: On
+#ifndef HOST_COMM_ROBUST_CRC
+#define HOST_COMM_ROBUST_CRC 1
+#endif // HOST_COMM_ROBUST_CRC
+//
+
+// End Robust
+
// <<< end of configuration section >>>
+#define SL_SIMPLE_COM_ROBUST HOST_COMM_ROBUST
+#define SL_SIMPLE_COM_ROBUST_CRC HOST_COMM_ROBUST_CRC
+
#endif // HOST_COMM_CONFIG_H
diff --git a/app/bluetooth/common_host/host_comm/host_comm_posix.c b/app/bluetooth/common_host/host_comm/host_comm_posix.c
index 47f232e3a2b..bd4805c9134 100644
--- a/app/bluetooth/common_host/host_comm/host_comm_posix.c
+++ b/app/bluetooth/common_host/host_comm/host_comm_posix.c
@@ -275,26 +275,25 @@ void *msg_recv_func(void *ptr)
while (run) {
int32_t len;
len = host_comm_pk(handle_ptr);
- if (len < 0) {
- // Peek is not supported, read data one by one
- len = 1;
- }
if (len > sizeof(buf_in.buf)) {
// If readable data exceeds the buffer size then
// read it one by one to avoid overflow
len = 1;
app_log_warning("Input buffer size too low, please increase it." APP_LOG_NL);
+ } else if (len < 0) {
+ // Peek is not supported, read data one by one
+ len = 1;
}
if ((len > 0) && (len <= (sizeof(buf_in.buf) - buf_in.len))) {
- memset(&buf_tmp, 0, sizeof(buf_tmp));
ret = host_comm_input(handle_ptr, len, buf_tmp.buf);
pthread_mutex_lock(&mutex);
memcpy(&buf_in.buf[buf_in.len], &buf_tmp.buf[0], ret);
buf_in.len += ret;
pthread_mutex_unlock(&mutex);
+ } else {
+ app_sleep_us(RECV_FUNC_US_SLEEP);
}
- app_sleep_us(RECV_FUNC_US_SLEEP);
}
return 0;
}
diff --git a/app/bluetooth/common_host/host_comm/host_comm_robust.c b/app/bluetooth/common_host/host_comm/host_comm_robust.c
new file mode 100644
index 00000000000..2c56705e1f7
--- /dev/null
+++ b/app/bluetooth/common_host/host_comm/host_comm_robust.c
@@ -0,0 +1,129 @@
+/***************************************************************************//**
+ * @file
+ * @brief Host layer for robust communication
+ *******************************************************************************
+ * # License
+ * Copyright 2023 Silicon Laboratories Inc. www.silabs.com
+ *******************************************************************************
+ *
+ * SPDX-License-Identifier: Zlib
+ *
+ * The licensor of this software is Silicon Laboratories Inc.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ ******************************************************************************/
+
+#include
+#include "sl_simple_com_robust.h"
+#include "host_comm.h"
+#include "host_comm_robust.h"
+#include "host_comm_config.h"
+
+typedef struct {
+ uint16_t len;
+ uint8_t data[DEFAULT_HOST_BUFLEN];
+} buf_t;
+
+static buf_t buf_in_packed = { 0 };
+static buf_t buf_in_unpacked = { 0 };
+static buf_t buf_out = { 0 };
+
+// Pack and write data to NCP.
+int32_t host_comm_robust_tx(uint32_t len, const uint8_t *data)
+{
+ if (sl_simple_com_robust_get_pack_buffer_size(len) > DEFAULT_HOST_BUFLEN) {
+ return -1;
+ }
+
+ buf_out.len = sl_simple_com_robust_pack_data(buf_out.data, data, (size_t)len);
+ return host_comm_tx(buf_out.len, buf_out.data);
+}
+
+// Read data from NCP.
+int32_t host_comm_robust_rx(uint32_t len, uint8_t *data)
+{
+ int32_t ret = -1;
+
+ if (data == NULL) {
+ return ret;
+ }
+
+ // Copy data from the unpacked buffer with the desired length.
+ if (buf_in_unpacked.len >= len) {
+ memcpy(data, buf_in_unpacked.data, len);
+ buf_in_unpacked.len -= len;
+ memmove(buf_in_unpacked.data,
+ &buf_in_unpacked.data[len],
+ buf_in_unpacked.len);
+ ret = len;
+ }
+
+ return ret;
+}
+
+// Peek if readable data exists.
+int32_t host_comm_robust_peek(void)
+{
+ int32_t raw_rx_len = host_comm_peek();
+
+ // Copy new data from host_comm RX buffer to temporary buffer.
+ if (raw_rx_len > 0) {
+ // If full, circulate data
+ if (buf_in_packed.len + raw_rx_len > DEFAULT_HOST_BUFLEN) {
+ int32_t overflow = buf_in_packed.len + raw_rx_len - DEFAULT_HOST_BUFLEN;
+ buf_in_packed.len -= overflow;
+ memmove(buf_in_packed.data,
+ &buf_in_packed.data[overflow],
+ buf_in_packed.len);
+ }
+
+ int32_t ret = host_comm_rx(raw_rx_len,
+ (uint8_t *)&buf_in_packed.data[buf_in_packed.len]);
+ if (ret > 0) {
+ buf_in_packed.len += ret;
+ }
+ }
+
+ // Unpack. If successful, move from packed buffer to unpacked buffer.
+ if (buf_in_packed.len > 0) {
+ sl_simple_com_robust_result_t result;
+ int32_t unpack_rx_len = buf_in_packed.len;
+
+ // Clamp incoming data length to maximum available space.
+ // (Overestimating required space.)
+ if (buf_in_unpacked.len + unpack_rx_len > DEFAULT_HOST_BUFLEN) {
+ unpack_rx_len = DEFAULT_HOST_BUFLEN - buf_in_unpacked.len;
+ }
+
+ result = sl_simple_com_robust_unpack_data(buf_in_packed.data, unpack_rx_len);
+ if (result.status == SL_STATUS_OK) {
+ memcpy(&buf_in_unpacked.data[buf_in_unpacked.len],
+ result.payload,
+ result.payload_size);
+ buf_in_unpacked.len += result.payload_size;
+
+ buf_in_packed.len -= result.processed;
+ memmove(buf_in_packed.data,
+ &buf_in_packed.data[result.processed],
+ buf_in_packed.len);
+ }
+ }
+
+ // Return the length of the unpacked buffer.
+ return (int32_t)buf_in_unpacked.len;
+}
diff --git a/app/bluetooth/common_host/host_comm/host_comm_robust.h b/app/bluetooth/common_host/host_comm/host_comm_robust.h
new file mode 100644
index 00000000000..6a9ea1ba632
--- /dev/null
+++ b/app/bluetooth/common_host/host_comm/host_comm_robust.h
@@ -0,0 +1,61 @@
+/***************************************************************************//**
+ * @file
+ * @brief Host layer for robust communication
+ *******************************************************************************
+ * # License
+ * Copyright 2023 Silicon Laboratories Inc. www.silabs.com
+ *******************************************************************************
+ *
+ * SPDX-License-Identifier: Zlib
+ *
+ * The licensor of this software is Silicon Laboratories Inc.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ ******************************************************************************/
+
+#ifndef HOST_COMM_ROBUST_H
+#define HOST_COMM_ROBUST_H
+
+/**************************************************************************//**
+ * Pack and write data to NCP.
+ *
+ * @param[in] len Number of bytes to write.
+ * @param[in] data Data to write.
+ *
+ * @return Number of written bytes, -1 on error.
+ *****************************************************************************/
+int32_t host_comm_robust_tx(uint32_t len, const uint8_t *data);
+
+/**************************************************************************//**
+ * Read data from NCP.
+ *
+ * @param[in] len Number of bytes to read.
+ * @param[in] data Read data.
+ *
+ * @return Number of bytes read, -1 on error.
+ *****************************************************************************/
+int32_t host_comm_robust_rx(uint32_t len, uint8_t *data);
+
+/**************************************************************************//**
+ * Peek if readable data exists.
+ *
+ * @return Number of bytes on the buffer, -1 on error.
+ *****************************************************************************/
+int32_t host_comm_robust_peek(void);
+
+#endif // HOST_COMM_ROBUST_H
diff --git a/app/bluetooth/common_host/host_comm/host_comm_win.c b/app/bluetooth/common_host/host_comm/host_comm_win.c
index e8da724866e..077f39e9701 100644
--- a/app/bluetooth/common_host/host_comm/host_comm_win.c
+++ b/app/bluetooth/common_host/host_comm/host_comm_win.c
@@ -228,18 +228,18 @@ void *msg_recv_func(void *ptr)
while (run) {
int32_t len;
len = host_comm_pk(handle_ptr);
- if (len < 0) {
- // Peek is not supported, read data one by one
- len = 1;
- }
+
if (len > sizeof(buf_in.buf)) {
// If readable data exceeds the buffer size then
// read it one by one to avoid overflow
len = 1;
app_log_warning("Input buffer size too low, please increase it." APP_LOG_NL);
+ } else if (len < 0) {
+ // Peek is not supported, read data one by one
+ len = 1;
}
+
if (len > 0) {
- memset(&buf_tmp, 0, sizeof(buf_tmp));
ret = host_comm_input(handle_ptr, len, buf_tmp.buf);
if (ret <= (sizeof(buf_in.buf) - buf_in.len)) {
pthread_mutex_lock(&mutex);
@@ -247,8 +247,10 @@ void *msg_recv_func(void *ptr)
buf_in.len += ret;
pthread_mutex_unlock(&mutex);
}
+ } else if (len != 1) {
+ app_sleep_us(RECV_FUNC_US_SLEEP);
}
- app_sleep_us(RECV_FUNC_US_SLEEP);
}
+
return 0;
}
diff --git a/app/bluetooth/common_host/ncp_host/ncp_host.c b/app/bluetooth/common_host/ncp_host/ncp_host.c
index e1317deb46e..3c8ddd9f49b 100644
--- a/app/bluetooth/common_host/ncp_host/ncp_host.c
+++ b/app/bluetooth/common_host/ncp_host/ncp_host.c
@@ -35,6 +35,16 @@
#include "app_sleep.h"
#include "ncp_host_config.h"
#include "host_comm_config.h"
+#if defined(HOST_COMM_ROBUST) && HOST_COMM_ROBUST == 1
+#include "host_comm_robust.h"
+#define HOST_COMM_TX host_comm_robust_tx
+#define HOST_COMM_RX host_comm_robust_rx
+#define HOST_COMM_PEEK host_comm_robust_peek
+#else // HOST_COMM_ROBUST
+#define HOST_COMM_TX host_comm_tx
+#define HOST_COMM_RX host_comm_rx
+#define HOST_COMM_PEEK host_comm_peek
+#endif // HOST_COMM_ROBUST
// Default parameter values.
#define MAX_OPT_LEN 255
@@ -134,13 +144,12 @@ void ncp_host_tx(uint32_t len, uint8_t* data)
#if defined(SECURITY) && SECURITY == 1
if (enable_security) {
- memset(buf_ncp_out.buf, 0, sizeof(buf_ncp_out.buf));
security_encrypt((char*)data, (char*)&buf_ncp_out.buf, &len);
- ret = host_comm_tx(len, buf_ncp_out.buf);
+ ret = HOST_COMM_TX(len, buf_ncp_out.buf);
} else
#endif // defined(SECURITY) && SECURITY == 1
{
- ret = host_comm_tx(len, data);
+ ret = HOST_COMM_TX(len, data);
}
if (ret < 0) {
host_comm_deinit();
@@ -183,9 +192,12 @@ int32_t ncp_host_peek_timeout(uint32_t len, uint32_t timeout)
uint32_t timeout_counter = 0;
do {
- ret = host_comm_peek();
- timeout_counter++;
- app_sleep_us(PEEK_US_SLEEP);
+ ret = HOST_COMM_PEEK();
+
+ if (ret < len) {
+ timeout_counter++;
+ app_sleep_us(PEEK_US_SLEEP);
+ }
} while ((ret < len) && (timeout_counter < timeout));
return ret;
@@ -198,12 +210,13 @@ int32_t ncp_host_peek(void)
{
int32_t msg_len;
- msg_len = host_comm_peek();
+ msg_len = HOST_COMM_PEEK();
if (msg_len) {
int32_t ret;
uint8_t msg_header = 0;
+
// Read first byte
- ret = host_comm_rx(1, &buf_ncp_raw.buf[0]);
+ ret = HOST_COMM_RX(1, &buf_ncp_raw.buf[0]);
if (ret < 0) {
return -1;
}
@@ -215,7 +228,7 @@ int32_t ncp_host_peek(void)
if (ret < 0) {
return -1;
}
- ret = host_comm_rx(1, (void*) &buf_ncp_raw.buf[1]);
+ ret = HOST_COMM_RX(1, (void *)&buf_ncp_raw.buf[1]);
if (ret < 0) {
return -1;
}
@@ -224,12 +237,12 @@ int32_t ncp_host_peek(void)
if (msg_len >= DEFAULT_HOST_BUFLEN - 2) {
return -1;
}
- ret = ncp_host_peek_timeout(msg_len, MSG_RECV_TIMEOUT_COUNT);
+ ret = ncp_host_peek_timeout(msg_len, MSG_RECV_TIMEOUT_COUNT * msg_len);
if (ret < 0) {
return -1;
}
// Read the rest of the message
- ret = host_comm_rx(msg_len, (void*) &buf_ncp_raw.buf[2]);
+ ret = HOST_COMM_RX(msg_len, (void *)&buf_ncp_raw.buf[2]);
if (ret < 0) {
return -1;
}
@@ -243,7 +256,6 @@ int32_t ncp_host_peek(void)
{
memcpy(buf_ncp_in.buf, buf_ncp_raw.buf, msg_len);
}
- memset(buf_ncp_raw.buf, 0, sizeof(buf_ncp_raw.buf));
buf_ncp_in.len = msg_len;
#if defined(SECURITY) && SECURITY == 1
if (enable_security) {
@@ -276,12 +288,12 @@ void ncp_sec_host_command_handler(buf_ncp_host_t *buf)
security_reset();
// Wait for the security handshake response (80 bytes length)
ret = ncp_host_peek_timeout(SEC_BGAPI_RSP_MSG_LEN,
- MSG_RECV_TIMEOUT_COUNT * 10);
+ MSG_RECV_TIMEOUT_COUNT * SEC_BGAPI_RSP_MSG_LEN);
if (ret < 0) {
return;
}
// Read the rest of the message
- ret = host_comm_rx(SEC_BGAPI_RSP_MSG_LEN, (void *) &response);
+ ret = HOST_COMM_RX(SEC_BGAPI_RSP_MSG_LEN, (void *) &response);
resp_cmd = (sl_bt_msg_t *)response;
if (SL_BT_MSG_ID(resp_cmd->header)
== sl_bt_rsp_user_message_to_target_id) {
diff --git a/app/bluetooth/common_host/ncp_sec/ncp_sec_host.c b/app/bluetooth/common_host/ncp_sec/ncp_sec_host.c
index f7e3e8bec5f..bbd55ae96cf 100644
--- a/app/bluetooth/common_host/ncp_sec/ncp_sec_host.c
+++ b/app/bluetooth/common_host/ncp_sec/ncp_sec_host.c
@@ -36,6 +36,7 @@
#include
#include
#include
+#include
#include "app_log.h"
#include "sl_bt_api.h"
diff --git a/app/bluetooth/common_host/ots/config/sl_bt_ots_client_config.h b/app/bluetooth/common_host/ots/config/sl_bt_ots_client_config.h
index fdad0609088..aac9ec3d3bc 100644
--- a/app/bluetooth/common_host/ots/config/sl_bt_ots_client_config.h
+++ b/app/bluetooth/common_host/ots/config/sl_bt_ots_client_config.h
@@ -47,6 +47,6 @@
// <<< end of configuration section >>>
-#define SL_BT_CONFIG_MAX_CONNECTIONS (4)
+#define SL_BT_CONFIG_MAX_CONNECTIONS (8)
#endif // SL_BT_OTS_CLIENT_CONFIG_H
diff --git a/app/bluetooth/component/esl_tag_core.slcc b/app/bluetooth/component/esl_tag_core.slcc
index 3e61e4c4ea2..7287f4cbff5 100644
--- a/app/bluetooth/component/esl_tag_core.slcc
+++ b/app/bluetooth/component/esl_tag_core.slcc
@@ -86,16 +86,20 @@ requires:
template_contribution:
- name: bluetooth_on_event
value:
- include: esl_tag_core.h
+ include: esl_tag_internal.h
function: esl_core_bt_on_event
priority: -8192
- name: event_handler
value:
event: internal_app_init
- include: esl_tag_core.h
+ include: esl_tag_internal.h
handler: esl_core_init
priority: 8
-
+ - name: event_handler
+ value:
+ event: internal_app_process_action
+ include: esl_tag_internal.h
+ handler: esl_core_step
validation_library:
- path: ../../../common/validation/autonumber_common.lua
name: autonumber_common
diff --git a/app/bluetooth/component/simple_com.slcc b/app/bluetooth/component/simple_com.slcc
index 76cea2e972d..42394c36b5a 100644
--- a/app/bluetooth/component/simple_com.slcc
+++ b/app/bluetooth/component/simple_com.slcc
@@ -17,10 +17,10 @@ config_file:
- path: config/sl_simple_com_config.h
- path: config/sl_simple_com_freertos_config.h
condition:
- - freertos
+ - freertos
- path: config/sl_simple_com_micriumos_config.h
condition:
- - micriumos_kernel
+ - micriumos_kernel
source:
- path: sl_simple_com_usart.c
condition:
@@ -34,19 +34,21 @@ source:
- path: sl_simple_com_micriumos.c
condition:
- micriumos_kernel
+ - path: sl_simple_com_robust.c
include:
- path: .
file_list:
- path: sl_simple_com.h
+ - path: sl_simple_com_robust.h
provides:
- name: simple_com
requires:
- name: uartdrv_usart
condition:
- - uartdrv_core
+ - uartdrv_core
- name: cpc_secondary
condition:
- - cpc_core
+ - cpc_core
- name: app_assert
- name: status
- name: emlib_common
@@ -54,12 +56,12 @@ requires:
- name: emlib_cmu
- name: sleeptimer
condition:
- - "device_sdid_80"
+ - "device_sdid_80"
define:
-- name: "EFR32BG1_USART_E202_WORKAROUND"
- value: "1"
- condition:
- - "device_sdid_80"
+ - name: "EFR32BG1_USART_E202_WORKAROUND"
+ value: "1"
+ condition:
+ - "device_sdid_80"
template_file:
- path: "template/sl_simple_com_isr.c.jinja"
condition: [uartdrv_instance]
@@ -69,7 +71,7 @@ template_contribution:
- name: component_catalog
value: bluetooth_ncp
condition:
- - cpc_core
+ - cpc_core
- name: event_handler
value:
event: internal_app_init
@@ -83,7 +85,7 @@ template_contribution:
handler: sl_simple_com_os_task_init
priority: 1
condition:
- - kernel
+ - kernel
- name: event_handler
value:
event: service_process_action
diff --git a/app/bluetooth/component_host/ncp_host_bt.mk b/app/bluetooth/component_host/ncp_host_bt.mk
index fa566aa87d3..a12b3284c70 100644
--- a/app/bluetooth/component_host/ncp_host_bt.mk
+++ b/app/bluetooth/component_host/ncp_host_bt.mk
@@ -40,6 +40,7 @@ $(SDK_DIR)/app/bluetooth/common_host/ncp_host/config \
$(SDK_DIR)/app/bluetooth/common_host/system \
$(SDK_DIR)/app/bluetooth/common_host/tcp \
$(SDK_DIR)/app/bluetooth/common_host/uart \
+$(SDK_DIR)/app/bluetooth/common/simple_com \
$(SDK_DIR)/platform/common/inc \
$(SDK_DIR)/protocol/bluetooth/inc
@@ -50,6 +51,8 @@ endif
override C_SRC += \
$(SDK_DIR)/app/bluetooth/common_host/app_sleep/app_sleep.c \
$(foreach OS_i, $(OS), $(SDK_DIR)/app/bluetooth/common_host/host_comm/host_comm_$(OS_i).c) \
+$(SDK_DIR)/app/bluetooth/common_host/host_comm/host_comm_robust.c \
+$(SDK_DIR)/app/bluetooth/common/simple_com/sl_simple_com_robust.c \
$(SDK_DIR)/app/bluetooth/common_host/ncp_host/ncp_host.c \
$(foreach OS_i, $(OS), $(SDK_DIR)/app/bluetooth/common_host/tcp/tcp_$(OS_i).c) \
$(foreach OS_i, $(OS), $(SDK_DIR)/app/bluetooth/common_host/uart/uart_$(OS_i).c) \
diff --git a/app/bluetooth/documentation/slBluetooth_docContent.xml b/app/bluetooth/documentation/slBluetooth_docContent.xml
index fd2216d356e..656d7a0c585 100644
--- a/app/bluetooth/documentation/slBluetooth_docContent.xml
+++ b/app/bluetooth/documentation/slBluetooth_docContent.xml
@@ -1,6 +1,6 @@
-
+
Includes detailed information on using the Gecko Bootloader with Silicon Labs Bluetooth applications. It supplements the general Gecko Bootloader implementation information provided in UG489: Silicon Labs Gecko Bootloader User's Guide.
@@ -8,7 +8,7 @@
-
+
Describes the Wi-Fi impact on Bluetooth and methods to improve Bluetooth coexistence with Wi-Fi. Explains design considerations to improve coexistence without direct interaction between Bluetooth and Wi-Fi radios. These techniques are applicable to the EFR32MGx and EFR32BGx series. Discusses the Silicon Labs Packet Traffic Arbitration (PTA) support to coordinate 2.4GHz RF traffic for co-located Bluetooth and Wi-Fi radios.
@@ -16,7 +16,7 @@
-
+
Explains how NVM3 can be used as non-volatile data storage in various protocol implementations.
@@ -24,7 +24,7 @@
-
+
Describes how to lock and unlock the debug access of EFR32 Gecko Series 2 devices. Many aspects of the debug access, including the secure debug unlock are described. The Debug Challenge Interface (DCI) and Secure Engine (SE) Mailbox Interface for locking and unlocking debug access are also included.
@@ -32,7 +32,7 @@
-
+
Contains detailed information on configuring and using the Secure Boot with hardware Root of Trust and Secure Loader on Series 2 devices, including how to provision the signing key. This is a companion document to UG489: Silicon Labs Gecko Bootloader User's Guide.
@@ -40,7 +40,7 @@
-
+
Details on programming, provisioning, and configuring Series 2 devices in production environments. Covers Secure Engine Subsystem of Series 2 devices, which runs easily upgradeable Secure Engine (SE) or Virtual Secure Engine (VSE) firmware.
@@ -48,14 +48,14 @@
-
+
Describes how to measure the power consumption of EFR32BG devices running the Bluetooth i-Beacon example. For general instructions, see AN969: Measuring Power Consumption in Wireless Gecko Devices, available on silabs.com.
-
+
How to program, provision, and configure the anti-tamper module on EFR32 Series 2 devices with Secure Vault.
@@ -63,7 +63,7 @@
-
+
Describes how to configure the NCP target and how to program the NCP host when using the Bluetooth Stack in Network Co-Processor mode
@@ -71,14 +71,14 @@
-
+
Describes how to integrate a v3.x Silicon Labs Bluetooth application with an RTOS, and demonstrate how a time- and event-driven application can be run in parallel with the Bluetooth stack.
-
+
Reviews performing radio frequency physical layer evaluation with EFR32BG SoCs and BGM modules using the Direct Test Mode protocol in Bluetooth SDK v3.x.
@@ -86,7 +86,7 @@
-
+
How to authenticate an EFR32 Series 2 device with Secure Vault, using secure device certificates and signatures.
@@ -94,14 +94,14 @@
-
+
Provides details on how to develop a dynamic multiprotocol application running Bluetooth and a proprietary protocol on RAIL in GSDK v3.x.
-
+
How to securely "wrap" keys in EFR32 Series 2 devices with Secure Vault, so they can be stored in non-volatile storage.
@@ -109,28 +109,28 @@
-
+
Describes the sample applications provided to demonstrate the directing finding capabilities of Bluetooth 5.1. Angle of Arrival (AoA) estimation is demonstrated with the use of Silicon Labs' Real Time Locating (RTL) library. These techniques are applicable to the EFR32MGx and EFR32BGx series.
-
+
Bluetooth 5.1 makes it possible to send Constant Tone Extensions (CTEs) in Bluetooth packets on which phase measurements can be done. This guide is for those implementing custom applications that take advantage of phase measurement and antenna switching capabilites.
-
+
Provides details on designing Bluetooth Low Energy applications with security and privacy in mind.
-
+
Describes how to provision and configure Series 2 devices through the DCI and SWD.
@@ -138,14 +138,14 @@
-
+
Includes the results of the interoperability testing of Silicon Labs' ICs and Bluetooth Low Energy stack with Android and iOS smart phones.
-
+
Describes how to integrate crypto functionality into applications using PSA Crypto compared to Mbed TLS.
@@ -153,7 +153,7 @@
-
+
Describes using Simplicity Studio 5's Network Analyzer to debug Bluetooth Mesh and Low Energy applications. It can be read jointly with AN958: Debugging and Programming Interfaces for Customer Designs for more information on using Packet Trace Interface with custom hardware.
@@ -161,7 +161,7 @@
-
+
Gecko Bootloader v2.x, introduced in GSDK 4.0, contains a number of changes compared to Gecko Bootloader v1.x. This document describes the differences between the versions, including how to configure the new Gecko Bootloader in Simplicity Studio 5.
@@ -169,14 +169,14 @@
-
+
Gives a short overview of the standard Host Controller Interface (HCI) and how to use it with a Silicon Labs Bluetooth LE controller.
-
+
Describes how to run any combination of Zigbee EmberZNet, OpenThread, and Bluetooth networking stacks on a Linux host processor, interfacing with a single EFR32 Radio Co-processor (RCP) with multiprotocol and multi-PAN support, as well as how to run the Zigbee stack on the EFR32 as a network co-processor (NCP) alongside the OpenThread RCP.
@@ -184,21 +184,21 @@
-
+
Summarizes Amazon FreeRTOS components and sample applications, and explains how to use the examples to communicate with the Amazon Web Services (AWS) cloud with a smart phone app.
-
+
Describes how to exploit the different features of Bluetooth technology to achieve the minimum possible energy consumption for a given use case.
-
+
Covers the basics of ARMv8-M TrustZone, describes how TrustZone is implemented on Series 2 devices, and provides application examples.
@@ -206,63 +206,63 @@
-
+
Describes the theoretical background of certificate-based authentication and pairing, and demonstrates the usage of the related sample applications that can be found in the Silicon Labs Bluetooth SDK.
-
+
Provides an overview and hyperlinks to all packaged documentation.
-
+
Describes the differences between using Bluetooth SDK v2.x in Simplicity Studio 4 and using Bluetooth SDK v3.x in Simplicity Studio 5. Outlines the steps needed to migrate a v2.x project to v3.x.
-
+
Describes the software components provided by Silicon Labs to support Direction Finding (DF) and provides instructions on how to start developing your own application.
-
+
Contains a comprehensive list of APIs used to interface to the Silicon Labs Bluetooth Real-Time Locating Library.
-
+
Contains a comprehensive list of APIs used to interface to the Silicon Labs Bluetooth stack.
-
+
Lists compatibility requirements and sources for all software components in the development environment. Discusses the latest changes to the Silicon Labs Bluetooth SDK and associated utilities, including added/deleted/deprecated features/API, and lists fixed and known issues.
-
+
Discusses the latest changes to the The Real-Time Locating (RTL) library, including added/deleted/deprecated APIs, and lists fixed and known issues.
-
+
A detailed overview of the changes, additions, and fixes in the Gecko Platform components. The Gecko Platform includes EMLIB, EMDRV, RAIL Library, NVM3, and the component-based infrastructure.
@@ -270,7 +270,7 @@
-
+
Introduces the security concepts that must be considered when implementing an Internet of Things (IoT) system. Using the ioXt Alliance's eight security principles as a structure, it clearly delineates the solutions Silicon Labs provides to support endpoint security and what you must do outside of the Silicon Labs framework.
@@ -278,7 +278,7 @@
-
+
Introduces bootloading for Silicon Labs networking devices. Discusses the Gecko Bootloader as well as legacy Ember and Bluetooth bootloaders, and describes the file formats used by each.
@@ -286,7 +286,7 @@
-
+
Introduces non-volatile data storage using flash and the three different storage implementations offered for Silicon Labs microcontrollers and SoCs: Simulated EEPROM, PS Store, and NVM3.
@@ -294,14 +294,14 @@
-
+
Offers an overview for those new to the Bluetooth low energy technology.
-
+
Describes the four multiprotocol modes, discusses considerations when selecting protocols for multiprotocol implementations, and reviews the Radio Scheduler, a required component of a dynamic multiprotocol solution.
@@ -309,7 +309,7 @@
-
+
Describes methods to improve the coexistence of 2.4 GHz IEEE 802.11b/g/n Wi-Fi and other 2.4 GHz radios such as Bluetooth, Bluetooth Mesh, Bluetooth Low Energy, and IEEE 802.15.4-based radios such as Zigbee and OpenThread.
@@ -317,14 +317,14 @@
-
+
Explains the basics of Bluetooth Angle of Arrival (AoA) and Angle of Departure (AoD) direction finding technologies and provides the theory behind estimating angle of arrival.
-
+
Reviews using this XML-based mark-up language to describe the Bluetooth GATT database, configure access and security properties, and include the GATT database as part of the firmware.
@@ -332,7 +332,7 @@
-
+
Describes how and when to use Simplicity Commander's Command-Line Interface.
@@ -340,7 +340,7 @@
-
+
Describes how to implement a dynamic multiprotocol solution.
@@ -348,14 +348,14 @@
-
+
Covers the Bluetooth stack v3.x architecture, application development flow, using the MCU core and peripherals, stack configuration options, and stack resource usage.
-
+
Describes how to use the Simplicity Studio 5 GATT Configurator, an intuitive interface providing access to all the Profiles, Services, Characteristics, and Descriptors as defined in the Bluetooth specification.
@@ -363,7 +363,7 @@
-
+
Describes the high-level implementation of the Silicon Labs Gecko Bootloader for EFR32 SoCs and NCPs, and provides information on how to get started using the Gecko Bootloader with Silicon Labs wireless protocol stacks in GSDK 4.0 and higher.
@@ -371,7 +371,7 @@
-
+
The Bluetooth Direction Finding Tool Suite is meant to ease development with the Silicon Labs' RTL library. It provides multiple tools to configure the system, and also helps the development with analyzer tools that calculate many output parameters from the observed IQ samples.
diff --git a/app/bluetooth/esf.properties b/app/bluetooth/esf.properties
index 8c0c04b62c2..57173ee5450 100644
--- a/app/bluetooth/esf.properties
+++ b/app/bluetooth/esf.properties
@@ -3,8 +3,8 @@ id=com.silabs.stack.ble
label=Bluetooth SDK
description=Bluetooth Software Development Kit
-version=6.1.0.0
-prop.subLabel=Bluetooth\\ 6.1.0
+version=6.2.0.0
+prop.subLabel=Bluetooth\\ 6.2.0
# Default compatibility of the BLE SDK
prop.boardCompatibility=.*
diff --git a/app/bluetooth/example/bt_ncp/bt_ncp_esl_ap.slcp b/app/bluetooth/example/bt_ncp/bt_ncp_esl_ap.slcp
index a4cbf5b6265..1718835bedd 100644
--- a/app/bluetooth/example/bt_ncp/bt_ncp_esl_ap.slcp
+++ b/app/bluetooth/example/bt_ncp/bt_ncp_esl_ap.slcp
@@ -18,36 +18,38 @@ filter:
value: ["Advanced"]
component:
- - id: bluetooth_stack
- - id: bluetooth_feature_legacy_advertiser
- - id: bluetooth_feature_extended_advertiser
- - id: bluetooth_feature_connection_phy_update
- - id: bluetooth_feature_periodic_advertiser
- - id: bluetooth_feature_pawr_advertiser
- - id: bluetooth_feature_advertiser_past
- - id: bluetooth_feature_connection
- - id: bluetooth_feature_gap
- - id: bluetooth_feature_gatt
- - id: bluetooth_feature_gatt_server
- - id: bluetooth_feature_nvm
- - id: bluetooth_feature_legacy_scanner
- - id: bluetooth_feature_extended_scanner
- - id: bluetooth_feature_sm
- - id: bluetooth_feature_system
- - id: bluetooth_feature_dynamic_gattdb
- - id: bluetooth_feature_l2cap
- id: ncp
+ - id: ncp_sec
+ - id: sl_system
+ - id: device_init
+ - id: bt_post_build
+ - id: bootloader_interface
- id: uartdrv_usart
instance:
- vcom
- id: mpu
- - id: bootloader_interface
- - id: sl_system
- - id: device_init
- - id: bt_post_build
+ - id: bluetooth_feature_nvm
requires:
+ - name: bluetooth_stack
+ - name: bluetooth_feature_sm
+ - name: bluetooth_feature_gap
+ - name: bluetooth_feature_gatt
+ - name: bluetooth_feature_l2cap
+ - name: bluetooth_feature_system
+ - name: bluetooth_feature_connection
+ - name: bluetooth_feature_gatt_server
+ - name: bluetooth_feature_dynamic_gattdb
+ - name: bluetooth_feature_legacy_scanner
+ - name: bluetooth_feature_advertiser_past
+ - name: bluetooth_feature_pawr_advertiser
+ - name: bluetooth_feature_extended_scanner
+ - name: bluetooth_feature_legacy_advertiser
+ - name: bluetooth_feature_extended_advertiser
+ - name: bluetooth_feature_periodic_advertiser
+ - name: bluetooth_feature_connection_phy_update
- name: bluetooth_feature_external_bonding_database
+ - name: bluetooth_feature_use_accurate_api_address_types
source:
- path: main.c
@@ -75,6 +77,14 @@ configuration:
value: "2752"
- name: SL_HEAP_SIZE
value: "12288"
+ - name: SL_NCP_CMD_BUF_SIZE
+ value: "384"
+ - name: SL_NCP_EVT_BUF_SIZE
+ value: "384"
+ - name: SL_SIMPLE_COM_TX_BUF_SIZE
+ value: "384"
+ - name: SL_SIMPLE_COM_RX_BUF_SIZE
+ value: "768"
- name: SL_BT_CONFIG_MAX_CONNECTIONS
value: "8"
- name: SL_BT_CONFIG_USER_L2CAP_COC_CHANNELS
@@ -103,6 +113,12 @@ configuration:
value: "1"
- name: SL_UARTDRV_USART_VCOM_FLOW_CONTROL_TYPE
value: uartdrvFlowControlHwUart
+ condition:
+ - brd4001a
+ - name: SL_UARTDRV_USART_VCOM_FLOW_CONTROL_TYPE
+ value: uartdrvFlowControlHw
+ unless:
+ - brd4001a
- name: SL_PSA_KEY_USER_SLOT_COUNT
value: "0"
condition:
diff --git a/app/bluetooth/example/bt_rail_dmp_soc_light/bt_rail_dmp_soc_light_freertos.slcp b/app/bluetooth/example/bt_rail_dmp_soc_light/bt_rail_dmp_soc_light_freertos.slcp
index c8dd989d61a..0d509381b27 100644
--- a/app/bluetooth/example/bt_rail_dmp_soc_light/bt_rail_dmp_soc_light_freertos.slcp
+++ b/app/bluetooth/example/bt_rail_dmp_soc_light/bt_rail_dmp_soc_light_freertos.slcp
@@ -46,9 +46,7 @@ component:
- id: demo_ui
- id: component_catalog
- id: ibeacon
- - id: iostream_usart
- instance:
- - vcom
+ - id: iostream_recommended_stream
- id: dmd_memlcd
- id: mpu
- id: simple_led
@@ -116,8 +114,18 @@ configuration:
value: "12000"
- name: SL_BOARD_ENABLE_VCOM
value: "1"
+ - name: SL_IOSTREAM_EUSART_VCOM_FLOW_CONTROL_TYPE
+ value: "eusartHwFlowControlNone"
condition:
- - iostream_usart
+ - iostream_eusart
+ - name: SL_IOSTREAM_LEUART_VCOM_FLOW_CONTROL_TYPE
+ value: "uartFlowControlNone"
+ condition:
+ - iostream_leuart
+ - name: SL_IOSTREAM_USART_VCOM_FLOW_CONTROL_TYPE
+ value: "uartFlowControlNone"
+ condition:
+ - iostream_uart
- name: SL_IOSTREAM_USART_VCOM_FLOW_CONTROL_TYPE
value: "usartHwFlowControlNone"
condition:
diff --git a/app/bluetooth/example/bt_rail_dmp_soc_light/bt_rail_dmp_soc_light_micriumos.slcp b/app/bluetooth/example/bt_rail_dmp_soc_light/bt_rail_dmp_soc_light_micriumos.slcp
index 9ea576b9af5..09113991921 100644
--- a/app/bluetooth/example/bt_rail_dmp_soc_light/bt_rail_dmp_soc_light_micriumos.slcp
+++ b/app/bluetooth/example/bt_rail_dmp_soc_light/bt_rail_dmp_soc_light_micriumos.slcp
@@ -46,9 +46,7 @@ component:
- id: demo_ui
- id: component_catalog
- id: ibeacon
- - id: iostream_usart
- instance:
- - vcom
+ - id: iostream_recommended_stream
- id: dmd_memlcd
- id: mpu
- id: simple_led
@@ -116,8 +114,18 @@ configuration:
value: "14600"
- name: SL_BOARD_ENABLE_VCOM
value: "1"
+ - name: SL_IOSTREAM_EUSART_VCOM_FLOW_CONTROL_TYPE
+ value: "eusartHwFlowControlNone"
condition:
- - iostream_usart
+ - iostream_eusart
+ - name: SL_IOSTREAM_LEUART_VCOM_FLOW_CONTROL_TYPE
+ value: "uartFlowControlNone"
+ condition:
+ - iostream_leuart
+ - name: SL_IOSTREAM_USART_VCOM_FLOW_CONTROL_TYPE
+ value: "uartFlowControlNone"
+ condition:
+ - iostream_uart
- name: SL_IOSTREAM_USART_VCOM_FLOW_CONTROL_TYPE
value: "usartHwFlowControlNone"
condition:
diff --git a/app/bluetooth/example/bt_rail_dmp_soc_light_std/bt_rail_dmp_soc_light_std_freertos.slcp b/app/bluetooth/example/bt_rail_dmp_soc_light_std/bt_rail_dmp_soc_light_std_freertos.slcp
index 75faf782b66..45ce5a9863d 100644
--- a/app/bluetooth/example/bt_rail_dmp_soc_light_std/bt_rail_dmp_soc_light_std_freertos.slcp
+++ b/app/bluetooth/example/bt_rail_dmp_soc_light_std/bt_rail_dmp_soc_light_std_freertos.slcp
@@ -42,9 +42,7 @@ component:
- id: demo_ui
- id: component_catalog
- id: ibeacon
- - id: iostream_usart
- instance:
- - vcom
+ - id: iostream_recommended_stream
- id: dmd_memlcd
- id: mpu
- id: simple_led
@@ -111,8 +109,18 @@ configuration:
value: "12000"
- name: SL_BOARD_ENABLE_VCOM
value: "1"
+ - name: SL_IOSTREAM_EUSART_VCOM_FLOW_CONTROL_TYPE
+ value: "eusartHwFlowControlNone"
condition:
- - iostream_usart
+ - iostream_eusart
+ - name: SL_IOSTREAM_LEUART_VCOM_FLOW_CONTROL_TYPE
+ value: "uartFlowControlNone"
+ condition:
+ - iostream_leuart
+ - name: SL_IOSTREAM_USART_VCOM_FLOW_CONTROL_TYPE
+ value: "uartFlowControlNone"
+ condition:
+ - iostream_uart
- name: SL_IOSTREAM_USART_VCOM_FLOW_CONTROL_TYPE
value: "usartHwFlowControlNone"
condition:
diff --git a/app/bluetooth/example/bt_rail_dmp_soc_light_std/bt_rail_dmp_soc_light_std_micriumos.slcp b/app/bluetooth/example/bt_rail_dmp_soc_light_std/bt_rail_dmp_soc_light_std_micriumos.slcp
index 87e64ef6f47..5d7bf266fbb 100644
--- a/app/bluetooth/example/bt_rail_dmp_soc_light_std/bt_rail_dmp_soc_light_std_micriumos.slcp
+++ b/app/bluetooth/example/bt_rail_dmp_soc_light_std/bt_rail_dmp_soc_light_std_micriumos.slcp
@@ -42,9 +42,7 @@ component:
- id: demo_ui
- id: component_catalog
- id: ibeacon
- - id: iostream_usart
- instance:
- - vcom
+ - id: iostream_recommended_stream
- id: dmd_memlcd
- id: mpu
- id: simple_led
@@ -111,8 +109,18 @@ configuration:
value: "14600"
- name: SL_BOARD_ENABLE_VCOM
value: "1"
+ - name: SL_IOSTREAM_EUSART_VCOM_FLOW_CONTROL_TYPE
+ value: "eusartHwFlowControlNone"
condition:
- - iostream_usart
+ - iostream_eusart
+ - name: SL_IOSTREAM_LEUART_VCOM_FLOW_CONTROL_TYPE
+ value: "uartFlowControlNone"
+ condition:
+ - iostream_leuart
+ - name: SL_IOSTREAM_USART_VCOM_FLOW_CONTROL_TYPE
+ value: "uartFlowControlNone"
+ condition:
+ - iostream_uart
- name: SL_IOSTREAM_USART_VCOM_FLOW_CONTROL_TYPE
value: "usartHwFlowControlNone"
condition:
diff --git a/app/bluetooth/example/bt_soc_iop_test/bt_soc_iop_test_display.slcp b/app/bluetooth/example/bt_soc_iop_test/bt_soc_iop_test_display.slcp
index 7e610a19838..b82cf0226a8 100644
--- a/app/bluetooth/example/bt_soc_iop_test/bt_soc_iop_test_display.slcp
+++ b/app/bluetooth/example/bt_soc_iop_test/bt_soc_iop_test_display.slcp
@@ -34,9 +34,7 @@ component:
- id: component_catalog
- id: mpu
- id: app_timer
- - id: iostream_usart
- instance:
- - vcom
+ - id: iostream_recommended_stream
- id: app_log
- id: dmd_memlcd
- id: glib
@@ -86,8 +84,6 @@ configuration:
value: "9200"
- name: SL_BOARD_ENABLE_VCOM
value: "1"
- condition:
- - iostream_usart
- name: SL_BOARD_ENABLE_DISPLAY
value: "1"
contidtion:
diff --git a/app/bluetooth/example_host/bt_host_esl_ap/ap_cli.py b/app/bluetooth/example_host/bt_host_esl_ap/ap_cli.py
index 35fa2e0f33e..3a77400b80f 100644
--- a/app/bluetooth/example_host/bt_host_esl_ap/ap_cli.py
+++ b/app/bluetooth/example_host/bt_host_esl_ap/ap_cli.py
@@ -25,6 +25,7 @@
# 3. This notice may not be removed or altered from any source distribution.
import cmd
+from shlex import split as lexical_split
import queue
import re
import threading
@@ -33,7 +34,7 @@
import struct
import textwrap
from datetime import datetime as dt
-from ap_logger import getLogger, log
+from ap_logger import getLogger, log, setLogLevel, LEVELS, logLevelName
from ap_core import AccessPoint
from ap_config import IOP_TEST, BLOCKING_WAIT_TIMEOUT
from ap_constants import LED_PATTERN_LENGTH, LED_DEFAULT_GAMUT, LED_DEFAULT_PATTERN, \
@@ -42,8 +43,7 @@
PA_RESPONSE_SLOT_SPACING_MIN, PA_RESPONSE_SLOT_SPACING_MAX, PA_RESPONSE_SLOT_DELAY_MIN, \
PA_RESPONSE_SLOT_DELAY_MAX, PA_SUBEVENT_INTERVAL_MIN, PA_SUBEVENT_INTERVAL_MAX, \
PA_SUBEVENT_MIN, PA_SUBEVENT_MAX, ADDRESS_TYPE_PUBLIC_ADDRESS, \
- ADDRESS_TYPE_STATIC_ADDRESS, ADDRESS_TYPE_RANDOM_RESOLVABLE_ADDRESS, \
- ADDRESS_TYPE_RANDOM_NONRESOLVABLE_ADDRESS, VALID_ESL_ID_NUMBER_REGEX, VALID_BD_ADDRESS_REGEX
+ ADDRESS_TYPE_STATIC_ADDRESS, VALID_ESL_ID_NUMBER_REGEX, VALID_BD_ADDRESS_REGEX
from PIL import Image
def clamp(n, minn, maxn):
@@ -55,6 +55,12 @@ def ble_address_type(arg_value):
raise argparse.ArgumentTypeError("Invalid Bluetooth address type.")
return arg_value
+def ble_address_type_all(arg_value):
+ pat = re.compile(r"("+ VALID_BD_ADDRESS_REGEX + "|all)")
+ if not pat.match(arg_value):
+ raise argparse.ArgumentTypeError("Not a valid Bluetooth address.")
+ return arg_value
+
def address_type(arg_value):
pat = re.compile(r"("+ VALID_ESL_ID_NUMBER_REGEX + "|" + VALID_BD_ADDRESS_REGEX + "|^(?=\s*$)|all)")
if not pat.match(arg_value):
@@ -69,17 +75,20 @@ def esl_id_type(arg_value):
def time_type(arg_value):
try:
- arg_value = dt.strptime(arg_value, "%H:%M:%S")
+ if "." in arg_value:
+ arg_value = dt.strptime(arg_value, "%H:%M:%S.%f")
+ else:
+ arg_value = dt.strptime(arg_value, "%H:%M:%S")
return arg_value
except (TypeError, ValueError) as exc: # strptime can cause type or value error - exception chaining
- raise argparse.ArgumentTypeError("Invalid argument [time=]: Execution time of the command in hour:min:sec format.") from exc
-
+ raise argparse.ArgumentTypeError("Invalid argument [time=]: the execution time of the command must be in hour:min:sec[.fraction] format.") from exc
+
def date_type(arg_value):
try:
arg_value = dt.strptime(arg_value, "%Y-%m-%d")
return arg_value
except (ValueError, TypeError) as exc: # strptime can cause type or value error - exception chaining
- raise argparse.ArgumentTypeError("Invalid argument [date=]: Execution date of the command in ISO-8601 format.") from exc
+ raise argparse.ArgumentTypeError("Invalid argument [date=]: the execution date of the command in ISO-8601 format.") from exc
def data_type(arg_value):
@@ -88,6 +97,16 @@ def data_type(arg_value):
raise argparse.ArgumentTypeError("Invalid data type for vendor opcode command.")
return arg_value
+def split_sequence(sequence, sep):
+ chunk = []
+ for val in sequence:
+ if val == sep:
+ yield chunk
+ chunk = []
+ else:
+ chunk.append(val)
+ yield chunk
+
class ArgumentParser(argparse.ArgumentParser):
""" ArgumentParser with custom help message. """
def __init__(self, *args, add_help=False, **kwargs):
@@ -134,9 +153,7 @@ def __init__(self, ap: AccessPoint):
self.arg_set_rssi_threshold()
self.arg_script()
self.arg_update_complete()
-
- # Logger
- self.log = getLogger("CLI")
+ self.arg_verbosity()
# LED control defaults
self.led_pattern = bytearray(LED_DEFAULT_PATTERN)
@@ -145,7 +162,12 @@ def __init__(self, ap: AccessPoint):
self.led_repeats = bytearray(LED_DEFAULT_DURATION)
self.led_index = 0
self.prompt = ''
-
+
+ # Logger
+ @property
+ def log(self):
+ return getLogger("CLI")
+
def onecmd(self, line):
"""Interpret the argument as though it had been typed in response
to the prompt.
@@ -169,13 +191,14 @@ def onecmd(self, line):
else:
try:
func = getattr(self, 'do_' + cmd)
- args, unknown = self.command_parser.parse_known_args(line.split())
+ lexical_splitted_command = lexical_split(line)
+ args, unknown = self.command_parser.parse_known_args(lexical_splitted_command)
if unknown:
- self.log.error(f"Unknown argument '{unknown}' for command {cmd}.")
+ self.log.error("Unknown argument '%s' for command %s",unknown, cmd)
else:
return func(args)
except AttributeError as e:
- self.log.error("Unknown command: " + cmd)
+ self.log.error("Command error: %s", cmd)
self.log.debug(e)
except Exception as e:
self.log.error(e)
@@ -200,7 +223,7 @@ def loop(self):
readline.set_completer(self.complete)
readline.parse_and_bind(self.completekey+": complete")
except ImportError:
- self.log.info("Auto complete missing")
+ self.log.debug("Auto complete not supported.")
threading.Thread(target=self.poll_input, daemon=True).start()
stop = None
while not stop:
@@ -208,9 +231,31 @@ def loop(self):
line = self.queue.get(timeout=BLOCKING_WAIT_TIMEOUT)
except queue.Empty:
continue
- line = self.precmd(line)
- stop = self.onecmd(line)
- stop = self.postcmd(stop, line)
+ # Following preprocessing steps make the command chaining available by detecting semicolon as a command separator
+ try:
+ lexical_splitted_input = lexical_split(line)
+ except Exception as e:
+ self.log.error(e)
+ continue
+
+ input_groups = list(split_sequence(lexical_splitted_input,';'))
+ # Now we have a list of lists - each element (sub-list) in this list is a single command with its arguments
+ for single_input in input_groups:
+ # Check for empty commands in the chain, ignore if any - also stop on exit command
+ if len(single_input) and not stop:
+ # Original cmd class expects string input instead of list - our inherited class expects the same
+ stringline = ''
+ # So now we convert each sublist to an appropriate string...
+ for e in single_input:
+ # ...where 'appropriate' means that if a list element contains space or any escape sequence then we need to put into quotes!
+ if e.count("'"):
+ stringline += '"{0}"'.format(e if not e.count('"') else e.replace('"','\\"'))
+ else:
+ stringline += e if not any(c in e for c in " \\\"") else "'{0}'".format(e)
+ stringline += ' '
+ stringline = self.precmd(stringline)
+ stop = self.onecmd(stringline)
+ stop = self.postcmd(stop, stringline)
self.postloop()
def arg_demo(self):
@@ -220,7 +265,7 @@ def arg_demo(self):
def do_demo(self, arg):
"""
- Start or stop advertising Dynamic GATT.
+ Control the built-in advertising feature of the ESL NCP AP target for the ESL demo in the EFR Connect mobile application.
"""
if arg.choice == 'on':
self.ap.ap_adv_start()
@@ -229,23 +274,28 @@ def do_demo(self, arg):
def arg_scan(self):
parser_scan = self.subparsers.add_parser('scan',
- description=self.do_scan.__doc__)
- parser_scan.add_argument('choice', choices=['start', 'stop'], help="Control AP scanning to detect or ignore nearby advertiser ESL devices.")
- parser_scan.add_argument('--active', '-a', action='store_true', help="Start active scan instead of default passive.")
+ description=self.do_scan.__doc__,
+ epilog='''
+ Note: You can obtain the current status of the scanning by omitting the choice.''')
+ parser_scan.add_argument('choice', nargs='?', choices=['start', 'stop'], help="Control AP scanning to detect or ignore nearby advertiser ESL devices.")
+ parser_scan.add_argument('--active', '-a', action='store_true', help="Start active scan instead of default passive type.")
def do_scan(self, arg):
"""
Start or stop scanning for advertising ESL devices.
"""
active_scan = False
+ scan_enable = None
if arg.choice == 'start':
if arg.active:
active_scan = True
- self.ap.ap_scan(True, active_scan)
+ scan_enable = True
elif arg.choice == 'stop':
if arg.active:
- self.log.info("active ignored")
- self.ap.ap_scan(False, active_scan)
+ self.log.info("The active scan option will be ignored during stop.")
+ scan_enable = False
+
+ self.ap.ap_scan(scan_enable, active_scan)
def arg_connect(self):
parser_connect = self.subparsers.add_parser('connect',
@@ -255,20 +305,22 @@ def arg_connect(self):
Notes: and can be used instead of if ESL is already configured.
will be taken into account only if the given is unknown - otherwise the proper type reported
by the remote device will be used. If the group ID is not given after the ESL ID then the default value
- group zero is used. This applies to many commands expecting the group ID as optional parameter. The
- auto-configuring uses the following schema for ESL addressing:
- (16 * number_of_already_synchronized_tags) + 1''')
- parser_connect.add_argument('address', type=address_type, help="Bluetooth address (e.g. 'AA:BB:CC:DD:EE:22') in case insensitive format or ESL ID of the tag.")
- parser_connect.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)", default=0)
- parser_connect.add_argument('--addr_type', '-t', metavar='', choices=['public', 'static', 'rand_res', 'rand_nonres'], help=textwrap.dedent('''[address_type]: ESL address type (optional), possible values:
- - public: Public device address (default)
- - static: Static device address
- - rand_res: Resolvable private random address
- - rand_nonres: Non-resolvable private random address'''))
+ group zero is used. This applies to many commands expecting the group ID as optional parameter.
+
+ The 'all' keyword can be used with a special meaning with 'connect' command: it will try to connect to
+ all advertiser ESLs (within the 'group_id' if it is given or to any advertisers if it isn't) up to the
+ the maximum number of simultaneous connections supported by the current build of the ESL library and
+ the attached Network Co-Processor embedded controller.
+ ''')
+ parser_connect.add_argument('address', nargs="?", type=address_type, help="Bluetooth address (e.g. 'AA:BB:CC:DD:EE:22') in case insensitive format or ESL ID of the tag.")
+ parser_connect.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)")
+ parser_connect.add_argument('--addr_type', '-t', metavar='', choices=['public', 'static'], help=textwrap.dedent('''[address_type]: ESL address type (optional), possible values:
+ - public: Public device address (default assumption)
+ - static: Random static device address'''))
def do_connect(self, arg):
"""
- Connect to an ESL device with the specified address.
+ Connect to one or more ESL devices.
"""
group_id = arg.group_id
bt_addr = None
@@ -278,38 +330,49 @@ def do_connect(self, arg):
address_type = ADDRESS_TYPE_PUBLIC_ADDRESS
elif arg.addr_type == "static":
address_type = ADDRESS_TYPE_STATIC_ADDRESS
- elif arg.addr_type == "randres":
- address_type = ADDRESS_TYPE_RANDOM_RESOLVABLE_ADDRESS
- elif arg.addr_type == "rand_nonres":
- address_type = ADDRESS_TYPE_RANDOM_NONRESOLVABLE_ADDRESS
- if arg.address.isnumeric():
- esl_id = int(arg.address)
- else:
- bt_addr = arg.address.lower()
+ if arg.address is not None:
+ if arg.address.isnumeric():
+ esl_id = int(arg.address)
+ if arg.addr_type is not None:
+ address_type = None
+ self.log.warning("Explicit address type ignored for already configured ESLs - correct type must be known and will be used instead!")
+ else:
+ try:
+ bt_addr = ble_address_type(arg.address.lower())
+ except:
+ esl_id = str(arg.address) # it must be 'all', then
+
self.ap.ap_connect(esl_id, bt_addr, group_id, address_type)
def arg_disconnect(self):
parser_disconnect = self.subparsers.add_parser('disconnect',
- formatter_class=lambda prog: argparse.RawDescriptionHelpFormatter(prog, max_help_position=40),
+ formatter_class=lambda prog: argparse.RawTextHelpFormatter(prog, max_help_position=30),
description=self.do_disconnect.__doc__,
- epilog="Note: Should no address be given, then the default active connection will be closed if any.")
- parser_disconnect.add_argument('--address', '-a', metavar='', type=address_type, help='''Bluetooth address (e.g. 'AA:BB:CC:DD:EE:22') in case insensitive format or ESL ID of the tag.''')
- parser_disconnect.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)", default=0)
+ epilog='''
+ Notes: If no address is specified, the default active connection is closed - if only one exists.
+
+ To close more existing connections at once, you can use the 'disconnect all' command.
+ ''')
+ parser_disconnect.add_argument('address', nargs='?', metavar='', type=address_type, help='''Bluetooth address (e.g. 'AA:BB:CC:DD:EE:22') in case insensitive format or ESL ID of the tag.''')
+ parser_disconnect.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)")
def do_disconnect(self, arg):
"""
- Initiate the Periodic Advertisement Sync Transfer process then
- disconnect from an ESL device with the specified address.
+ Initiate the Periodic Advertisement Sync Transfer process if PAwR train is
+ available then disconnect from an ESL device with the specified address.
"""
group_id = arg.group_id
bt_addr = None
esl_id = None
if arg.address is not None:
if arg.address.isnumeric():
- esl_id = arg.address
+ esl_id = int(arg.address)
else:
- bt_addr = arg.address.lower()
+ try:
+ bt_addr = ble_address_type(arg.address.lower())
+ except:
+ esl_id = str(arg.address) # it must be 'all', then
self.ap.ap_disconnect(esl_id, bt_addr, group_id)
def arg_list(self):
@@ -319,7 +382,7 @@ def arg_list(self):
epilog='''
Examples: list a --verbose
list synchronized -v
- Note: To reset the list of advertising and blocked lists you may want to issue a
+ Note: To reset the list of advertising and blocked lists you may want to issue a
command at any time.''')
parser_list.add_argument('state', nargs='+', metavar="state", choices=['advertising', 'a', 'synchronized', 's', 'unsynchronized', 'u', 'connected', 'c', 'blocked', 'b'], help=textwrap.dedent('''
: List advertising tag information
@@ -351,17 +414,17 @@ def do_list(self, arg):
def arg_led(self):
parser_led = self.subparsers.add_parser('led',
- formatter_class=lambda prog: argparse.RawDescriptionHelpFormatter(prog, max_help_position=45),
+ formatter_class=lambda prog: argparse.RawDescriptionHelpFormatter(prog, max_help_position=45),
description=self.do_led.__doc__,
epilog='''
- Notes: Almost all of the optional led control parameters are "sticky", meaning that the last values are
+ Notes: Almost all of the optional led control parameters are "sticky", meaning that the last values are
preserved by the AP internally and will be re-used next time, if the given parameter is omitted in the
argument list. This doesn't apply on the delay, time and absolute parameters, though.''')
group_led_delay_absolute = parser_led.add_mutually_exclusive_group()
group_led_repeats_duration = parser_led.add_mutually_exclusive_group()
parser_led.add_argument('choice', choices=['on', 'off', 'flash']). help="Turn LED on/off or flash the LED"
parser_led.add_argument('esl_id', type=esl_id_type, help='ESL ID or all')
- parser_led.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)", default=0)
+ parser_led.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)")
parser_led.add_argument('--default', '-d', action='store_true', help="Restore the default flashing pattern built-in with AP")
parser_led.add_argument('--pattern', '-p', help="A string containing either '1's or '0's, max length: 40", metavar='')
parser_led.add_argument('--on_period', '-on', type=int, help='''Integer value from 1 to 255, meaning 'delay *2ms' for on state bits of the pattern. '0' is prohibited''', metavar='')
@@ -380,17 +443,22 @@ def do_led(self, arg):
"""
Turn on / off or flash an LED utilizing the LED control command.
"""
+ now = dt.now()
delay_ms = 0
absolute_base = pattern = None
input_error = False
- now = dt.now()
- self.ap.absolute_now = self.ap.get_absolute_time(now)
+ absolute_now = self.ap.get_absolute_time()
index = self.led_index
repeat_field = bytearray(2) # initialised to "LED Off"
period = self.led_period.copy()
gamut = self.led_gamut
- if arg.choice == 'on':
- repeat_field[0] |= 1
+ if arg.choice in ['on', 'off']:
+ if any(opt for opt in [arg.on_period, arg.off_period, arg.pattern, arg.default, arg.repeats, arg.duration]):
+ self.log.warning("Arguments controlling flashing parameters are ignored for 'on' and 'off' commands!")
+ if arg.choice == 'on':
+ repeat_field[0] |= 1
+ elif any(opt for opt in [arg.brightness, arg.color]):
+ self.log.warning("Color and brightness control parameters are useless for 'off' command!")
elif arg.choice == 'flash':
pattern = self.led_pattern.copy()
esl_id = arg.esl_id
@@ -410,16 +478,16 @@ def do_led(self, arg):
else:
gamut = ((int.from_bytes(gamut, byteorder="little") & 0x3f) | (brightness << 6)).to_bytes(1, "little")
if arg.color is not None:
- if (int(arg.color[0]) not in range(0,4)) or (int(arg.color[1]) not in range(0,4)) or (int(arg.color[2]) not in range(0,4)) or (len(arg.color>3)):
+ r,g,b = struct.unpack('ccc', str(arg.color).encode())
+ if any(c < 0 or c > 3 for c in [int(r),int(g),int(b)]):
self.log.error("Color has to be between [0,3], aborting")
input_error = True
else:
- r,g,b = struct.unpack('ccc', int(arg.color))
color = (int(b) << 4) | (int(g) << 2) | int(r)
gamut = ((int.from_bytes(gamut, byteorder="little") & 0xc0) | color).to_bytes(1, "little")
if arg.delay is not None:
delay_ms += arg.delay
- absolute_base = self.ap.absolute_now
+ absolute_base = absolute_now
if arg.time is not None:
set_date_input_time = arg.time
if not arg.date:
@@ -427,8 +495,8 @@ def do_led(self, arg):
else:
set_date = dt.combine(arg.date, set_date_input_time.time())
try:
- delay_ms += self.ap.calculate_exec_time(now, set_date.hour, set_date.minute, set_date.second, set_date.microsecond, set_date.date())
- absolute_base = self.ap.absolute_now
+ delay_ms += self.ap.calculate_exec_time(now, set_date.hour, set_date.minute, set_date.second, set_date.microsecond, arg.date)
+ absolute_base = absolute_now
except:
self.log.error("Requested delay can't be set, command ignored!")
input_error = True
@@ -509,63 +577,85 @@ def arg_config(self):
formatter_class=lambda prog: argparse.RawDescriptionHelpFormatter(prog, max_help_position=30),
description=self.do_config.__doc__,
epilog='''
- Note: Either the option '--full' or at least one of the optional parameters shall be given.''')
- parser_config.add_argument('device', nargs='?', type=ble_address_type, help="Bluetooth address of the target device (e.g. 'AA:BB:CC:DD:EE:22') in case insensitive format")
- parser_config.add_argument('--full', '-f', action='store_true', help="Configure everything in one step.")
+ Notes: Either the option '--full' or at least one of the other optional parameters shall be given.
+
+ The 'all' keyword can be used to configure a number of connected ESLs, but the ESL ID can't be specified
+ in turn, as this would make the command ambiguous.
+ However, the same ESL group ID can be specified for multiple connected devices - but use this with care,
+ as this command doesn't check against existing ESL configurations, so the network may end up broken!
+ ''')
+ parser_config.add_argument('device', nargs='?', type=ble_address_type_all, help="Bluetooth address of the target device (e.g. 'AA:BB:CC:DD:EE:22') in case insensitive format or 'all'")
+ parser_config.add_argument('--full', '-f', action='store_true', help="Configure everything in one step. ESL ID and group can be specified to override default values - see notes.")
parser_config.add_argument('--esl_id', '-i', metavar='', type=esl_id_type, help='New ESL ID of the connected tag.')
- parser_config.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)", default=0)
+ parser_config.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)")
parser_config.add_argument('--sync_key', '-sk', action='store_true', help="Set current Access Point Sync Key Material.")
parser_config.add_argument('--response_key', '-rk', action='store_true', help="Generate then set new Response Key Material.")
- parser_config.add_argument('--time', '-t', action='store_true', help="Set current Absolute Time of the ESL Access Point.")
- parser_config.add_argument('--absolute', '-a', metavar='', type=int, help='''Set custom Absolute Time epoch value - use with care! Mutually exclusive with the 'time' parameter.''')
+ time_group_parser_config = parser_config.add_mutually_exclusive_group()
+ time_group_parser_config.add_argument('--time', '-t', action='store_true', help="Set current Absolute Time of the ESL Access Point.")
+ time_group_parser_config.add_argument('--absolute', '-a', metavar='', type=int, help='''Set custom Absolute Time epoch value - use with care! Mutually exclusive with the 'time' parameter.''')
def do_config(self, arg):
"""
Configure the writable mandatory GATT characteristics of the ESL tag.
"""
params = {}
- if arg.full:
- params['full'] = True
- if arg.esl_id is not None:
- params['esl_addr'] = int(arg.esl_id)
- params['group_id'] = arg.group_id
- if arg.sync_key:
- params['sync_key'] = True
- if arg.response_key:
- params['response_key'] = True
- if arg.time:
- params['time'] = True
- if arg.absolute is not None:
- params['absolute_time'] = arg.absolute
-
+ if arg.device == 'all' and arg.esl_id is not None:
+ self.log.error("ESL ID can't be specified for 'all' in one step, command ignored!")
+ return
+ else:
+ if arg.full:
+ params['full'] = True
+ if arg.esl_id is not None:
+ params['esl_addr'] = int(arg.esl_id)
+ if arg.group_id is not None:
+ params['group_id'] = arg.group_id
+ if arg.sync_key:
+ params['sync_key'] = True
+ if arg.response_key:
+ params['response_key'] = True
+ if arg.time:
+ params['time'] = True
+ if arg.absolute is not None:
+ params['absolute_time'] = arg.absolute
+
if params == {}:
- self.log.error("Either the keyword 'all' or at least one of the optional parameters shall be given.")
+ self.log.error("Either the option '--full' or at least one of the optional parameters shall be given!")
else:
- self.ap.ap_config(params)
+ self.ap.ap_config(params, arg.device)
def arg_image_update(self):
parser_image_update = self.subparsers.add_parser('image_update',
- formatter_class=lambda prog: argparse.RawDescriptionHelpFormatter(prog, max_help_position=30),
+ formatter_class=lambda prog: argparse.RawDescriptionHelpFormatter(prog, max_help_position=33),
description=self.do_image_update.__doc__,
epilog='''
- Notes: To allow spaces or other special characters including some escapes equences in either the file name or
- the label, please put these strings into double quote.
- Example: image_update 0 ./img2.png label=\"Line 1\\nLine 2\"\n"''')
- parser_image_update.add_argument('image_index', type=int, help="Image index to update.")
- parser_image_update.add_argument('imagefile_path', help="Path of the image file.")
- parser_image_update.add_argument('--address', '-a', metavar='', type=address_type, help="Bluetooth address of the target device or ESL ID if there are more ESLs connected")
- parser_image_update.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)", default=0)
- parser_image_update.add_argument('--raw', '-r', action='store_true', help="Upload raw image file without any conversion.")
- parser_image_update.add_argument('--display_index', '-d', metavar='', type=int, help="Try auto-conversion image for this display.")
- parser_image_update.add_argument('--label', '-l', metavar='',help="Text label overlay to be written on the image.")
- parser_image_update.add_argument('--cw', '-rr', action='store_true', help="Clockwise (right) rotation.")
- parser_image_update.add_argument('--ccw', '-rl', action='store_true', help="Counter clockwise (left) rotation.")
- parser_image_update.add_argument('--flip', '-f', action='store_true', help="Turn the image upside down.")
+ Notes: To use space or backslash in the filename or other special characters, such as line break escape
+ sequences in the text caption, please enclose these strings in quotes.
+ The modifiers like rotation, fitting and and labeling are mutually exclusive with raw data input.
+ If the group is specified along with the keyword `all`, then only connected devices in the group will be affected.
+
+ Examples: image_update 0 ./image/banana.png --label=\"Line 1\\nLine 2\"
+ Send an image to index 0 on the single connected ESL with two lines of label.
+
+ image_update 1 "/user/home/path with space/img.jpg" all
+ Use the 'all' keyword as special address to send the same image to slot 1 on all connected ESLs.
+ ''')
+ parser_image_update.add_argument('image_index', type=int, help="Image storage index of the ESL tag to be updated.")
+ parser_image_update.add_argument('imagefile_path', type=str, help="Relative or full path to the selected image file. Use quotation marks if the path contains spaces.")
+ parser_image_update.add_argument('address', nargs="?", metavar='[address]', type=address_type, help="Bluetooth address of the target device or ESL ID or 'all' if there are more ESLs connected.")
+ parser_image_update.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)")
+ parser_image_update.add_argument('--label', '-l', metavar='', type=str, help="Caption to be written over the image. Use quotation marks if it includes spaces or line breaks.")
parser_image_update.add_argument('--cropfit', '-c', action='store_true', help="Fit the image to the display proportions by cropping.")
+ parser_image_update_converter_group = parser_image_update.add_mutually_exclusive_group()
+ parser_image_update_converter_group.add_argument('--raw', '-r', action='store_true', help="Upload raw image file without any conversion.")
+ parser_image_update_converter_group.add_argument('--display_index', '-d', metavar='', type=int, help="Try auto-conversion image for this display. Mutuall exclusive with '--raw' argument.")
+ parser_image_update_rotator_group = parser_image_update.add_mutually_exclusive_group()
+ parser_image_update_rotator_group.add_argument('--cw', '-rr', action='store_true', help="Clockwise (right) rotation.")
+ parser_image_update_rotator_group.add_argument('--ccw', '-rl', action='store_true', help="Counter clockwise (left) rotation.")
+ parser_image_update_rotator_group.add_argument('--flip', '-f', action='store_true', help="Turn the image upside down.")
def do_image_update(self, arg):
"""
- Update tag image.
+ Update single image on one or more connected Tags.
"""
raw_img = input_error = False
display_index = None
@@ -576,17 +666,15 @@ def do_image_update(self, arg):
if arg.image_index in range(0,256):
image_index = arg.image_index
else:
- self.log.warning("Image index must be between 0 and 255")
+ self.log.error("Image index must be between 0 and 255")
+ input_error = True
filename = arg.imagefile_path
if arg.raw:
raw_img = True
if arg.display_index is not None:
display_index = arg.display_index
- if arg.raw and arg.display_index is not None:
- self.log.error("Raw and display_index are mutually exclusive")
- input_error = True
- if (arg.cw and arg.ccw) or (arg.cw and arg.flip) or (arg.ccw and arg.flip):
- self.log.error("Only one rotating option can be given")
+ if arg.raw and (arg.cw or arg.ccw or arg.flip or arg.label or arg.cropfit):
+ self.log.error("Raw input can't be rotated, fitted or labelled - command ignored!")
input_error = True
if arg.cw:
rotation = Image.ROTATE_270
@@ -597,10 +685,11 @@ def do_image_update(self, arg):
if arg.cropfit:
cropfit = True
if arg.label:
- label = arg.label
-
+ # arg.label is a raw string: decode escapes before passing to ap_imageupdate!
+ label = arg.label.encode().decode('unicode-escape')
+
if not input_error:
- self.ap.ap_imageupdate(image_index, filename, True, raw_img, display_index, label, rotation, cropfit)
+ self.ap.ap_imageupdate(image_index, filename, raw_img, display_index, label, rotation, cropfit, arg.address, arg.group_id)
def arg_unassociate(self):
parser_unassociate = self.subparsers.add_parser('unassociate',
@@ -608,7 +697,7 @@ def arg_unassociate(self):
description=self.do_unassociate.__doc__,
epilog="Note: the keyword 'all' can be used as a substitute for the ESL broadcast address (0xff)")
parser_unassociate.add_argument('address', type=address_type, help="Bluetooth address (e.g. 'AA:BB:CC:DD:EE:22') in case insensitive format or ESL ID of the tag.")
- parser_unassociate.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)", default=0)
+ parser_unassociate.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)")
def do_unassociate(self, arg):
"""
@@ -623,16 +712,21 @@ def do_unassociate(self, arg):
def arg_mode(self):
parser_mode = self.subparsers.add_parser('mode',
- description=self.do_mode.__doc__)
+ formatter_class=lambda prog: argparse.RawDescriptionHelpFormatter(prog, max_help_position=20),
+ description=self.do_mode.__doc__,
+ epilog='''
+ Note: To check current mode you can issue the command without argument.
+ ''')
+
parser_mode.add_argument('choice', nargs='?', choices=['auto', 'manual'], help="Switch to automatic or manual mode", default=None)
def do_mode(self, arg):
"""
Changes ESL Access Point operation mode.
"""
- if arg.choice == 'auto':
+ if arg.choice == 'auto':
arg.choice = True
- elif arg.choice == 'manual':
+ elif arg.choice == 'manual':
arg.choice = False
self.ap.ap_mode(arg.choice)
@@ -642,7 +736,7 @@ def arg_read_sensor(self):
description=self.do_read_sensor.__doc__)
parser_read_sensor.add_argument('esl_id', type=int, help='ESL ID') # esl_id is int because all is not accepted
parser_read_sensor.add_argument('sensor_index', type=int, help="Sensor index.")
- parser_read_sensor.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)", default=0)
+ parser_read_sensor.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)")
def do_read_sensor(self, arg):
"""
@@ -663,7 +757,7 @@ def arg_factory_reset(self):
epilog='''
Note: the keyword 'all' can be used as a substitute for the ESL broadcast address (0xff)''')
parser_factory_reset.add_argument('address', type=address_type, help="Bluetooth address (e.g. 'AA:BB:CC:DD:EE:22') in case insensitive format or ESL ID of the tag.")
- parser_factory_reset.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)", default=0)
+ parser_factory_reset.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)")
parser_factory_reset.add_argument('--pawr', '-p', action='store_true', help='''Force command through PAwR sync train even if the addressed ESL is currently connected''')
def do_factory_reset(self, arg):
@@ -687,7 +781,7 @@ def arg_delete_timed(self):
parser_delete_timed.add_argument('led_display', choices=['led', 'display'], help="Delete timed led or display_image command.")
parser_delete_timed.add_argument('esl_id', type=esl_id_type, help='ESL ID or all')
parser_delete_timed.add_argument('index', type=int, help="Index of the LED or the display")
- parser_delete_timed.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)", default=0)
+ parser_delete_timed.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)")
def do_delete_timed(self, arg):
"""
@@ -707,7 +801,7 @@ def arg_refresh_display(self):
description=self.do_refresh_display.__doc__)
parser_refresh_display.add_argument('esl_id', type=esl_id_type, help='ESL ID or all')
parser_refresh_display.add_argument('display_index', type=int, help="Display index")
- parser_refresh_display.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)", default=0)
+ parser_refresh_display.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)")
def do_refresh_display(self, arg):
"""
@@ -732,7 +826,7 @@ def arg_display_image(self):
parser_display_image.add_argument('esl_id', type=esl_id_type, help='ESL ID or all')
parser_display_image.add_argument('image_index', type=int, help="Image index to update.")
parser_display_image.add_argument('display_index', type=int, help="Display index")
- parser_display_image.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)", default=0)
+ parser_display_image.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)")
group_display_image_delay_absolute.add_argument('--time', '-t', metavar="", type=time_type, help='''Execution time of the command in hour:min:sec format. (optional) Note: If <--delay> is specified then it is also added to the calculated value as an additional delay.''')
group_display_image_delay_absolute.add_argument('--absolute', '-a', metavar='', type=int, help='''Execution time of the command in ESL Absolute Time epoch value. Mutually exclusive with timed delay.''')
parser_display_image.add_argument('--delay', '-dy', metavar='', type=int, help="Delay in milliseconds (optional)")
@@ -743,7 +837,7 @@ def do_display_image(self, arg):
Display tag image.
"""
now = dt.now()
- self.ap.absolute_now = self.ap.get_absolute_time(now)
+ absolute_now = self.ap.get_absolute_time()
esl_id = image_idx = display_idx = None
group_id = delay_ms = 0
absolute_base = absolute_value = None
@@ -764,15 +858,15 @@ def do_display_image(self, arg):
else:
set_date = dt.combine(arg.date, set_date_input_time.time())
try:
- delay_ms += self.ap.calculate_exec_time(now, set_date.hour, set_date.minute, set_date.second, set_date.microsecond, set_date.date())
- absolute_base = self.ap.absolute_now
+ delay_ms += self.ap.calculate_exec_time(now, set_date.hour, set_date.minute, set_date.second, set_date.microsecond, arg.date)
+ absolute_base = absolute_now
except:
self.log.error("Requested delay can't be set, command ignored!")
input_error = True
if arg.delay is not None:
try:
delay_ms += arg.delay
- absolute_base = self.ap.absolute_now
+ absolute_base = absolute_now
except:
self.log.error("Invalid argument [delay=]: Delay in milliseconds (optional)")
input_error = True
@@ -790,7 +884,7 @@ def do_display_image(self, arg):
absolute_value = int(absolute_base + delay_ms) & 0xFFFFFFFF
if absolute_value == 0: # do not send any unsolicited delete on overflow!!
absolute_value += 1
-
+
if not input_error:
self.ap.ap_display_image(esl_id, group_id, image_idx, display_idx, absolute_value)
@@ -803,7 +897,7 @@ def arg_ping(self):
(Although it still makes no sense as broadcast messages doesn't solicit any
response by the specification!)''')
parser_ping.add_argument('esl_id', type=esl_id_type, help='ESL ID or all')
- parser_ping.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)", default=0)
+ parser_ping.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)")
def do_ping(self, arg):
"""
@@ -826,7 +920,7 @@ def arg_vendor_opcode(self):
Please note that if the payload string has odd number of ASCII hex bytes, then a single leading zero will
be added.''')
parser_vendor_opcode.add_argument('esl_id', type=esl_id_type, help='ESL ID or all')
- parser_vendor_opcode.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)", default=0)
+ parser_vendor_opcode.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)")
parser_vendor_opcode.add_argument('--data', '-d', metavar='', type=data_type, help="ASCII hexadecimal data stream up to 16 bytes overall - an appropriate TLV to the given length will be built automatically.")
def do_vendor_opcode(self, arg):
@@ -851,7 +945,7 @@ def arg_service_reset(self):
formatter_class=lambda prog: argparse.RawDescriptionHelpFormatter(prog, max_help_position=35),
description=self.do_service_reset.__doc__)
parser_service_reset.add_argument('esl_id', type=esl_id_type, help='ESL ID or all can be used as a broadcast address (0xff)')
- parser_service_reset.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)", default=0)
+ parser_service_reset.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)")
def do_service_reset(self, arg):
"""
@@ -929,8 +1023,9 @@ def arg_sync(self):
epilog='''
Notes: Using the optional '-ms' argument with the 'config' subcommand allows you to specify timing parameters in
milliseconds instead of their natural units, but this may introduce rounding errors. Please also note
- that with this option the fractional milliseconds can't be specified precisely.''')
- parser_sync.add_argument('choice', choices=['start', 'stop', 'config'], help="Start/stop sending or config periodic synchronization packets")
+ that with this option the fractional milliseconds can't be specified precisely.
+ You can ask for the current status of the PAwR train by omitting the choice.''')
+ parser_sync.add_argument('choice', nargs='?', choices=['start', 'stop', 'config'], help="Start/stop sending or config periodic synchronization packets")
parser_sync.add_argument('--millis', '-ms', action='store_true', help="Specify timing parameters in milliseconds")
parser_sync.add_argument('--in_max', '-max', metavar='', type=int, help="Maximum periodic advertising interval in units of 1.25ms.")
parser_sync.add_argument('--in_min', '-min', metavar='', type=int, help="Minimum periodic advertising interval in units of 1.25ms.")
@@ -944,32 +1039,10 @@ def do_sync(self, arg):
"""
Start / stop sending synchronization packets.
"""
- start = False
- arg_list = []
- if arg.choice == 'start':
- start = True
- arg_list.append('start')
- if arg.in_min is not None:
- arg_list.append(round(float(arg.int_min) / 1.25))
- if arg.in_max is not None:
- arg_list.append(round(float(arg.int_max) / 1.25))
- if len(arg_list) > 1:
- # Check interval limits
- if len(arg_list) > 3:
- if arg_list[1] > arg_list[2]:
- self.log.error("Wrong periodic advertising interval values!")
- for val in arg_list:
- if (val > PA_INTERVAL_ABS_MAX) or (val < PA_INTERVAL_ABS_MIN):
- self.log.error("Wrong periodic advertising interval values!"
- " Time range: 7.5 ms to 81.92 s")
- return
- self.log.info("Request Periodic Synchronization Transfer start as follows:")
- self.sync_config(arg_list)
- if len(arg_list) == 0:
- arg_list = None
- self.ap.ap_sync(start, arg_list)
- elif arg.choice == "config": #sync config ms 1234 1234 28 30 235 4 2
- arg_list.append('config')
+ start = None
+ arg_list = None
+ if arg.choice == "config":
+ arg_list = ['config']
arg_list[1:] = [arg.in_min, arg.in_max, arg.se_count, arg.se_interval, arg.rs_delay, arg.rs_spacing, arg.rs_count]
if all(v is None for v in arg_list[1:]):
arg_list.clear()
@@ -986,8 +1059,36 @@ def do_sync(self, arg):
self.sync_config(ms_values)
else:
self.sync_config(arg_list)
- elif arg.choice == 'stop':
- self.ap.ap_sync(start, None)
+ else:
+ if arg.choice == 'stop':
+ start = False
+ elif arg.choice == 'start':
+ start = True
+ arg_list = ['start']
+ if arg.in_min is not None:
+ arg_list.append(round(float(arg.in_min) / 1.25))
+ if arg.in_max is not None:
+ arg_list.append(round(float(arg.in_max) / 1.25))
+ if arg.se_count is not None or arg.se_interval is not None or arg.rs_delay is not None or arg.rs_spacing is not None or arg.rs_count is not None:
+ self.log.warning("Only the interval can be specified with the 'sync start' command, the rest is ignored.")
+ if not arg.millis and (arg.in_min is not None or arg.in_max is not None):
+ self.log.warning("The interval given with the 'sync start' command will always be interpreted as milliseconds!")
+ if len(arg_list) > 1:
+ # Check interval limits
+ if len(arg_list) >= 3:
+ if arg_list[1] > arg_list[2]:
+ self.log.error("Wrong periodic advertising interval values!")
+ return
+ for val in arg_list[1:]:
+ if (1.25 * val > PA_INTERVAL_ABS_MAX) or (1.25 * val < PA_INTERVAL_ABS_MIN):
+ self.log.error("Wrong periodic advertising interval values!"
+ " Time range: 7.5 ms to 81.92 s")
+ return
+ self.log.info("Request Periodic Synchronization Transfer start as follows:")
+ self.sync_config(arg_list)
+ if len(arg_list) == 0:
+ arg_list = None
+ self.ap.ap_sync(start, arg_list)
def arg_set_rssi_threshold(self):
parser_set_rssi_threshold = self.subparsers.add_parser('set_rssi_threshold',
@@ -999,17 +1100,17 @@ def do_set_rssi_threshold(self, arg):
"""
Set RSSI filter threshold value.
"""
- if arg.rssi < 0:
+ if arg.rssi < 0:
self.ap.ap_set_rssi_threshold(int(arg.rssi))
else: self.log.error("Invalid RSSI parameter, only negative integers are allowed")
- def precmd(self, line):
+ def precmd(self, command):
""" Optionally log commands to record_file """
- if self.record_file and "script" and "record" not in line:
- print(line, file=self.record_file)
+ if self.record_file and "script" and "record" not in command:
+ print(command, file=self.record_file)
self.record_file.flush()
- return line
+ return command
def arg_script(self):
parser_script = self.subparsers.add_parser('script',
@@ -1050,34 +1151,47 @@ def arg_update_complete(self):
parser_update_complete = self.subparsers.add_parser('update_complete',
formatter_class=lambda prog: argparse.RawDescriptionHelpFormatter(prog, max_help_position=35),
description=self.do_update_complete.__doc__,
- epilog="Note: This command used only for testing purposes.")
- parser_update_complete.add_argument('address', type=address_type, nargs='?', default='', help="Bluetooth address (e.g. 'AA:BB:CC:DD:EE:22') in case insensitive format or ESL ID of the tag.")
- parser_update_complete.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)", default=0)
+ epilog="""
+ Notes: This command used only for testing purposes in IOP test mode.
+ If the group is specified along with the keyword `all`, then only devices in the group will be affected.
+ """)
+ parser_update_complete.add_argument('address', type=address_type, nargs='?', default='', help="Bluetooth address (e.g. 'AA:BB:CC:DD:EE:22') in case insensitive format or ESL ID of the tag or 'all'.")
+ parser_update_complete.add_argument('--group_id', '-g', metavar='', type=int, help="ESL group ID (optional, default is group 0)")
def do_update_complete(self, arg):
"""
Send update complete command.
"""
if IOP_TEST:
- group_id = arg.group_id
- address = None
- if arg.address == "all":
- self.log.error("Address cannot be 'all'")
- else:
- if arg.address.isnumeric():
- address = int(arg.address)
- else:
- address = arg.address.lower()
- self.ap.ap_update_complete(address, group_id)
+ self.ap.ap_update_complete(arg.address.lower(), arg.group_id)
else:
self.log.warning("The update_complete command works only in IOP test mode!")
-
+
+ def arg_verbosity(self):
+ parser_verbosity = self.subparsers.add_parser('verbosity',
+ formatter_class=lambda prog: argparse.RawDescriptionHelpFormatter(prog, max_help_position=17),
+ description=self.do_verbosity.__doc__,
+ epilog='''
+ Notes: To check current verbosity level you can issue the command without argument.
+ NOTSET can be used to display debugging messages not only for AP code, but also for all python modules that may utilze logging.
+ ''')
+ parser_verbosity.add_argument('choice', nargs='?', type = str.upper, choices=list(LEVELS), help='Level to apply')
+
+ def do_verbosity(self, arg):
+ """
+ Set Access Point logging verbosity level at runtime
+ """
+ if arg.choice is None:
+ log("Current logging level:", logLevelName())
+ else:
+ setLogLevel(LEVELS[arg.choice])
+
def all_help(self):
"""
Print detailed help messages for all commands.
"""
subparsers_actions = [
- action for action in self.command_parser._actions
+ action for action in self.command_parser._actions
if isinstance(action, argparse._SubParsersAction)]
# there will probably only be one subparser_action,
# but better safe than sorry
@@ -1099,7 +1213,7 @@ def subparser_help(self, subparser_given):
self.all_help()
else:
subparsers_actions = [
- action for action in self.command_parser._actions
+ action for action in self.command_parser._actions
if isinstance(action, argparse._SubParsersAction)]
for subparsers_action in subparsers_actions:
for choice, subparser in subparsers_action.choices.items():
@@ -1107,7 +1221,7 @@ def subparser_help(self, subparser_given):
print(subparser.format_help())
subp.append(choice)
if subparser_given not in subp:
- self.log.error("Help not available for unknown command: " + subparser_given)
+ self.log.error("Help not available for unknown command: " + subparser_given)
def ap_wait(self, w_time):
"""
@@ -1147,7 +1261,7 @@ def record_commands(self, fname):
self.log.info("There's no recording to stop!")
def do_exit(self, arg):
- """
+ """
Exit from application
"""
return True
diff --git a/app/bluetooth/example_host/bt_host_esl_ap/ap_config.py b/app/bluetooth/example_host/bt_host_esl_ap/ap_config.py
index 7fd0cd6bd56..22fb1e17de1 100644
--- a/app/bluetooth/example_host/bt_host_esl_ap/ap_config.py
+++ b/app/bluetooth/example_host/bt_host_esl_ap/ap_config.py
@@ -41,6 +41,9 @@
# Retry count for ESL command opcodes re-sending
ESL_CMD_MAX_RETRY_COUNT = 3
+# Pending count for connection requests: 1 is the minmum ad also the safest value, but auto provisioning will be the slowest
+ESL_CMD_MAX_PENDING_CONNECTION_REQUEST_COUNT = 2
+
# Tags in a group in automated mode addressing
ESL_MAX_TAGS_IN_AUTO_GROUP = 16
diff --git a/app/bluetooth/example_host/bt_host_esl_ap/ap_constants.py b/app/bluetooth/example_host/bt_host_esl_ap/ap_constants.py
index d3a2698813c..05d2928113c 100644
--- a/app/bluetooth/example_host/bt_host_esl_ap/ap_constants.py
+++ b/app/bluetooth/example_host/bt_host_esl_ap/ap_constants.py
@@ -46,15 +46,6 @@
# Address types
ADDRESS_TYPE_PUBLIC_ADDRESS = 0
ADDRESS_TYPE_STATIC_ADDRESS = 1
-ADDRESS_TYPE_RANDOM_RESOLVABLE_ADDRESS = 2
-ADDRESS_TYPE_RANDOM_NONRESOLVABLE_ADDRESS = 3
-
-# Tag states
-ST_UNASSOCIATED = 0
-ST_CONFIGURING = 1
-ST_SYNCHRONIZED = 2
-ST_UPDATING = 3
-ST_UNSYNCHRONIZED = 4
# Display type Assigned Numbers
ESL_DISPLAY_TYPE_BLACK_WHITE = 0x01
@@ -131,10 +122,10 @@
TLV_RESPONSE_DISPLAY_STATE, TLV_RESPONSE_READ_SENSOR]
RESPONSE_STRINGS = {
- TLV_RESPONSE_ERROR: "Error response",
- TLV_RESPONSE_LED_STATE: "LED State response",
- TLV_RESPONSE_BASIC_STATE: "Basic state response",
- TLV_RESPONSE_DISPLAY_STATE: "Display state response",
+ TLV_RESPONSE_ERROR: "ESL Error",
+ TLV_RESPONSE_LED_STATE: "LED State",
+ TLV_RESPONSE_BASIC_STATE: "Basic state",
+ TLV_RESPONSE_DISPLAY_STATE: "Display state",
TLV_RESPONSE_READ_SENSOR: "Sensor value",
TLV_RESPONSE_SILABS_SKIP: "New PAwR skip parameter"
}
@@ -206,31 +197,7 @@
SUBEVENT_START = "subevent_start"
SUBEVENT_DATA_COUNT = "subevent_data_count"
-# Main states
-BOOT = 0
-CONNECTING = 2
-IDLE = 11
-PARSING_GATT_SERVICES = 12
-PARSING_GATT_CHARS = 13
-CONNECTED = 14
-READING_VALUES = 15
-WRITING_VALUES = 16
-SUBSCRIBING = 17
-
-STATE_STRINGS = {
- BOOT: "Boot",
- CONNECTING: "Connecting",
- IDLE: "Idle",
- PARSING_GATT_SERVICES: "Parsing GATT Services",
- PARSING_GATT_CHARS: "Parsing characteristics",
- CONNECTED: "Connected",
- READING_VALUES: "Reading GATT values",
- WRITING_VALUES : "Writing values",
- SUBSCRIBING : "Subscribing to ESL CP notification"
-}
-
# General command codes
-CMD_AP_CONTROL_INIT_GATTDB = 100
CMD_AP_CONTROL_ADV_ENABLE = 101
CMD_AP_CONTROL_CP_RESPONSE = 102
CMD_AP_CONTROL_IT_RESPONSE = 103
@@ -265,3 +232,6 @@
CONTROLLER_REQUEST_LAST_DATA = 1
CONTROLLER_COMMAND_SUCCESS = 0
CONTROLLER_COMMAND_FAIL = 1
+
+# Advertising timeout [s]
+ADVERTISING_TIMEOUT = 30
diff --git a/app/bluetooth/example_host/bt_host_esl_ap/ap_core.py b/app/bluetooth/example_host/bt_host_esl_ap/ap_core.py
index dbcaee5d9c1..09918e3c450 100644
--- a/app/bluetooth/example_host/bt_host_esl_ap/ap_core.py
+++ b/app/bluetooth/example_host/bt_host_esl_ap/ap_core.py
@@ -24,22 +24,21 @@
# misrepresented as being the original software.
# 3. This notice may not be removed or altered from any source distribution.
-import string
import threading
-import secrets
import re
import os
import queue
import random
import struct
import traceback
+from datetime import datetime, timedelta
from ap_config import *
from ap_constants import *
from ap_sensor import *
-from ap_logger import getLogger, log
-from image_converter import XbmConverter
-from ap_response_parser import parse_response_data
-from esl_tag import Tag
+from ap_logger import getLogger, log, logLevel, LEVELS
+from ap_response_parser import ResponseParser
+from esl_tag import Tag, TagState, EslState, InvalidTagStateError, ImageUpdateFailed, ImageTypeRequired
+from esl_tag_db import TagDB
from ap_ead import KeyMaterial, EAD
from esl_command import ESLCommand
import esl_key_lib
@@ -52,10 +51,7 @@
class AccessPoint():
""" Access Point """
- def __init__(self, config, cmd_mode=False, demo_mode=False):
- # Logger
- self.log = getLogger()
-
+ def __init__(self, config, unsecure, cmd_mode=False, demo_mode=False):
self.scan_runs = False
self.pawr_active = False
self.auto_override = False
@@ -63,43 +59,33 @@ def __init__(self, config, cmd_mode=False, demo_mode=False):
self.demo_mode = demo_mode
self.event_handler_prefix_list = [""] # TODO: this list should be thread safe!
self.set_mode_handlers()
- # PAST timer, Note: interval is re-initialized properly after PA interval is set below
- self.past_timer = threading.Timer(10, self.past_timeout)
- self.past_timer.daemon = True
- self.past_initiated = False
# some PAwR stuff
self.cli_queue = None
self.ead = EAD()
+ if not unsecure:
+ config += ' -secure'
+ else:
+ self.log.warning("Starting with NCP encryption disabled!")
+
self.lib = esl_lib.Lib(config)
self.rssi_threshold = RSSI_THRESHOLD
- self.connection_dict = {}
-
- self.tags = {}
- self.ap_key = self.generate_key_material()
- self.ble_addresses = []
- self.blocked_list = []
- self.xbm_converter = XbmConverter()
+ self.tag_db = TagDB()
+ self.ap_key = self.ead.generate_key_material()
self.image_path = 'image/'
self.image_files = [f for f in os.listdir(self.image_path) if os.path.isfile(os.path.join(self.image_path, f))]
- self.selected_image = 0
- self.raw_image = b""
- self.image_file = None
- self.label = None
- self.rotation = None
- self.state = IDLE
- self.active_address = None # used temporarily, until tag management is implemented
- self.start_time = dt.now()
- self.absolute_now = self.get_absolute_time(self.start_time)
+ self.start_time = datetime.now()
+ self.auto_config_start_time = None
+ self.auto_configured_tags_in_single_run = 0
# ESL command dictionary of lists in a format: {group_id : [tlv0, tlv1, ..., tlvN]}
self.esl_queued_commands = {}
self.esl_pending_commands = {}
self.esl_command_queue_lock = threading.Lock()
self.esl_pending_commands_lock = threading.Lock()
# Shutdown timer is executed if boot event fails
- self.shutdown_timer = threading.Timer(3.0, self.shutdown_cli)
+ self.shutdown_timer = threading.Timer(3.0, self.shutdown_timeout)
self.shutdown_timer.daemon = True
self.shutdown_timer.start()
# ESL Demo controller related attributes
@@ -122,11 +108,20 @@ def __init__(self, config, cmd_mode=False, demo_mode=False):
self.response_slot_count = elw.ESL_LIB_PAWR_RESPONSE_SLOT_COUNT_DEFAULT
self.set_pawr_interval()
+ # State of connection count for demo/auto modes
+ self.max_conn_count_reached = False
+ self.bonding_finished = True
+
self.consumer = threading.Thread(target=self.dequeue, daemon=True)
self.consumer.start()
self.key_db = esl_key_lib.Lib()
+ # Logger
+ @property
+ def log(self):
+ return getLogger()
+
##################### CLI Handler methods #####################
def ap_adv_start(self):
@@ -155,43 +150,82 @@ def ap_scan(self, start, active=False):
input:
- start: 'True': start scanning, 'False': stop scanning
"""
- if start:
+ if start is None:
+ log(f"Scanning is currently{' ' if self.scan_runs else ' not '}in progress.")
+ elif start:
self.start_scan(active, clear_lists=True)
else:
self.stop_scan()
- def ap_connect(self, esl_id, bt_addr, group_id, address_type):
+ def ap_connect(self, esl_id, bt_addr: str, group_id, address_type):
"""
Connect to an ESL device with the specified address.
input:
- - esl_id: ESL ID
+ - esl_id: ESL ID or 'all' - the latter with special meaning: try connectin to more advertising tags at once
- bt_addr: Bluetooth address
- group_id: ESL group ID
"""
- if self.active_address is not None:
- self.log.warning(f"Already connected to {self.active_address}, request ignored.")
- return
-
+ connecting_to = []
if esl_id is not None:
- address = self.bt_addr_from_esl(esl_id, group_id)
- if address is None:
- self.log.warning("Unknown tag: ESL ID: " + str(esl_id)
- + ", Group ID: " + str(group_id))
- return
+ if esl_id == "all":
+ connecting_to = [tag for tag in self.tag_db.list_state(TagState.IDLE) if (tag.advertising and (group_id is None or tag.group_id == group_id))]
+ else:
+ if group_id is None:
+ group_id = 0
+ tag = self.tag_db.find((esl_id, group_id))
+ if tag is None:
+ self.log.error("Can't connect to unknown tag: ESL ID: %u, Group ID: %u", esl_id, group_id)
+ return
+ else:
+ connecting_to.append(tag)
+ elif bt_addr is not None:
+ if address_type is None:
+ bt_address_public = esl_lib.Address.from_str(bt_addr, ADDRESS_TYPE_PUBLIC_ADDRESS)
+ bt_address_static = esl_lib.Address.from_str(bt_addr, ADDRESS_TYPE_STATIC_ADDRESS)
+ tags = [self.tag_db.find(bt_address_public), self.tag_db.find(bt_address_static)]
+ tag_count = sum(x is not None for x in tags)
+ if tag_count == 0:
+ self.log.debug("No address type given - using default public address type.") # Will result in using the default ADDRESS_TYPE_PUBLIC_ADDRESS
+ elif tag_count != 1:
+ self.log.error("There are more tags in the database with same address but different address type, please specify the address type!")
+ return
+ else:
+ tag = next(item for item in tags if item is not None)
+ address_type = tag.ble_address.address_type
+ bt_address = esl_lib.Address.from_str(bt_addr, address_type)
+ tag = self.tag_db.find(bt_address)
+ if tag is None or (address_type is not None and tag.ble_address.address_type != address_type):
+ tag = self.tag_db.add(self.lib, bt_address)
+ connecting_to.append(tag)
else:
- address = self.address_auto_type(bt_addr, address_type)
+ connecting_to = [tag for tag in self.tag_db.list_state(TagState.IDLE) if (tag.advertising and (group_id is None or tag.group_id == group_id))]
+ if len(connecting_to) > 1:
+ if group_id is None:
+ self.log.warning("There are more than one tags advertising, please specify one or issue command with argument: 'all'!")
+ else:
+ self.log.warning("There are more than one advertising tag in group %d, please specify one or issue command with argument: 'all -g %d'!", group_id, group_id)
+ self.ap_list(["advertising"], group_id=group_id)
+ return
- _, subevent = self.esl_addr_from_bt(address)
+ if len(connecting_to) == 0:
+ self.log.warning("There's no advertising tag to connect to!")
+ return
- if subevent is not None and self.tags[address].state != ST_SYNCHRONIZED:
- subevent = None
+ for tag in connecting_to:
+ if tag.state in (TagState.CONNECTED, TagState.CONNECTING):
+ self.log.warning("%s already to %s, request ignored.", tag.state, tag.ble_address)
+ continue
- if not self.cmd_mode:
- self.auto_override = True
+ if not self.cmd_mode and not self.auto_override:
+ self.auto_override = True
- self.connect(address, subevent)
+ if not self.max_conn_count_reached:
+ self.connect(tag)
+ else:
+ self.log.warning("Maximum number of available connections reached, connecting to 'all' halted!")
+ return
- def ap_disconnect(self, esl_id, bt_addr, group_id):
+ def ap_disconnect(self, esl_id, bt_addr: str, group_id):
"""
Disconnect from an ESL device with the specified address.
Do Periodic Advertisement Sync Transfer during the procedure.
@@ -200,95 +234,127 @@ def ap_disconnect(self, esl_id, bt_addr, group_id):
- bt_addr: Bluetooth address
- group_id: ESL group ID
"""
- do_past = False
- if esl_id is not None or bt_addr is not None:
- if esl_id is not None:
- address = self.bt_addr_from_esl(esl_id, group_id)
- if address is None:
- self.log.warning("Unknown tag: ESL ID: " + str(esl_id)
- + ", Group ID: " + str(group_id))
- return
+ disconnect_from = []
+
+ if esl_id is not None:
+ if esl_id == "all":
+ disconnect_from = [tag for tag in self.tag_db.list_state((TagState.CONNECTING, TagState.CONNECTED)) if (group_id is None or tag.group_id == group_id)]
+ if not disconnect_from:
+ self.log.error("No connected tag present!")
+ else:
+ if group_id is None:
+ group_id = 0
+ tag = self.tag_db.find((esl_id, group_id))
+ if tag is None:
+ self.log.error("Can't disconnect from unknown tag: ESL ID: %u, Group ID: %u", esl_id, group_id)
else:
- do_past = self.tags[address].provisioned
+ disconnect_from.append(tag)
+ elif bt_addr is not None:
+ # Also checking CONNECTING state, because it is not possible to abort connection process.
+ tag = self.tag_db.find(bt_addr)
+ if tag is not None and tag.state != TagState.CONNECTED:
+ tag = None
+ if tag is None:
+ self.log.error("Can't disconnect from address %s!", bt_addr)
else:
- address = self.address_auto_type(bt_addr)
- esl_id, group_id = self.esl_addr_from_bt(address)
- if address in self.tags:
- do_past = self.tags[address].provisioned
- elif self.active_address in self.tags:
- address = self.active_address
- esl_id, group_id = self.esl_addr_from_bt(address)
- do_past = self.tags[address].provisioned
+ disconnect_from.append(tag)
else:
- self.log.error("No active connection present!")
- return
+ tag = self.get_active_tag()
+ if tag is not None:
+ disconnect_from.append(tag)
- if do_past:
- if esl_id is not None:
- self.ap_update_complete(esl_id, group_id)
+ if len(disconnect_from) == 0:
+ if self.controller_command == CCMD_DISCONNECT:
+ self.notify_controller(CCMD_DISCONNECT, CONTROLLER_COMMAND_FAIL)
+
+ for tag in disconnect_from:
+ if tag.provisioned:
+ if tag.esl_id is not None:
+ self.ap_update_complete(tag.esl_id, tag.group_id)
+ else:
+ self.past(tag)
else:
- self.past(address)
- else:
- self.disconnect(address)
+ self.disconnect(tag)
- def ap_config(self, params):
+ def ap_config(self, params: dict, bt_addr: str=None):
"""
- Configure the writable mandatory GATT characteristics of the ESL tag.
+ Configure the writable mandatory GATT characteristics of the ESL tag(s).
input:
- params: Configuration parameter dictionary. For further details see
the parameters of config command.
"""
- if self.active_address is None or self.active_address not in self.tags:
- self.log.warning("No tag connected to configure!")
- return
- tag = self.tags[self.active_address]
- values = {}
- esl_addr = None
- group_id = None
- for key in params:
- # All
- if key == "full":
- values = self.configure(tag)
- # ESL ID
- elif key == "esl_addr":
- esl_addr = params[key]
- if tag.esl_address is not None:
- group_id = tag.group_id
- else:
- group_id = 0
- # Group
- elif key == "group_id":
- group_id = params[key]
- if esl_addr is None:
- if tag.esl_address is not None:
- esl_addr = tag.esl_id
- else:
- esl_addr = 0
- # Sync Key
- elif key == "sync_key":
- values[elw.ESL_LIB_DATA_TYPE_GATT_AP_SYNC_KEY] = self.ap_key
- # Response Key
- elif key == "response_key":
- values[elw.ESL_LIB_DATA_TYPE_GATT_RESPONSE_KEY] = self.generate_key_material()
- # Raw Absolute Time value
- elif key == "absolute_time":
- absolute_time = params[key]
- values[elw.ESL_LIB_DATA_TYPE_GATT_CURRENT_TIME] = absolute_time.to_bytes(4, "little")
- # Time
- elif key == "time":
- self.absolute_now = self.get_absolute_time()
- values[elw.ESL_LIB_DATA_TYPE_GATT_CURRENT_TIME] = self.absolute_now.to_bytes(4, "little")
- if group_id is not None and esl_addr is not None:
- values[elw.ESL_LIB_DATA_TYPE_GATT_ESL_ADDRESS] = bytes([esl_addr & 0xff, group_id & 0x7f])
- if len(values):
- self.write_values(tag, values)
+ ALL = 'all'
+ tags_to_configure = []
+ if bt_addr == ALL:
+ tags_to_configure = self.tag_db.list_esl_state((EslState.UPDATING, EslState.CONFIGURING))
+ if not tags_to_configure:
+ self.log.error("No connected tag present!")
else:
- self.log.error("No characteristic to configure, request ignored!")
+ tag = self.get_active_tag(bt_addr)
+ if tag is None:
+ if bt_addr is not None:
+ self.log.error("ESL at address: %s is not connected, nothing to configure.", bt_addr)
+ return
+ else:
+ tags_to_configure.append(tag)
+
+ for tag in tags_to_configure:
+ values = {}
+ esl_addr = None
+ group_id = None
+ # Check all first
+ if "full" in params.keys():
+ values = self.configure(tag)
+ esl_addr = values[elw.ESL_LIB_DATA_TYPE_GATT_ESL_ADDRESS][0]
+ for key, param in params.items():
+ # ESL ID
+ if key == "esl_addr" and bt_addr != ALL:
+ esl_addr = param
+ self.log.info("Set ESL ID to %d.", esl_addr & 0xff)
+ if group_id is None:
+ if tag.group_id is not None:
+ group_id = tag.group_id
+ else:
+ group_id = 0
+ # Group
+ elif key == "group_id":
+ group_id = param
+ self.log.info("Set group ID to %d.", group_id)
+ if esl_addr is None:
+ if tag.esl_id is not None:
+ esl_addr = tag.esl_id
+ else:
+ esl_addr = self.new_auto_address(tag.id)
+ self.log.warning("ESL group entered without a valid ESL ID - the ESL ID set to %d automatically to avoid ambiguous network configuration.", esl_addr & 0xff)
+ # Sync Key
+ elif key == "sync_key":
+ values[elw.ESL_LIB_DATA_TYPE_GATT_AP_SYNC_KEY] = self.ap_key
+ # Response Key
+ elif key == "response_key":
+ values[elw.ESL_LIB_DATA_TYPE_GATT_RESPONSE_KEY] = self.ead.generate_key_material()
+ # Raw Absolute Time value
+ elif key == "absolute_time":
+ absolute_time = param
+ values[elw.ESL_LIB_DATA_TYPE_GATT_CURRENT_TIME] = absolute_time.to_bytes(4, "little")
+ # Time
+ elif key == "time":
+ absolute_time = self.get_absolute_time()
+ values[elw.ESL_LIB_DATA_TYPE_GATT_CURRENT_TIME] = absolute_time.to_bytes(4, "little")
+
+ if group_id is not None and esl_addr is not None:
+ values[elw.ESL_LIB_DATA_TYPE_GATT_ESL_ADDRESS] = bytes([esl_addr & 0xff, group_id & 0x7f])
+ if len(values):
+ # Always use GATT Write With Response with manual config. IOP_TEST will randomly override if set.
+ self.write_values(tag, values, True if not IOP_TEST else random.choice([True, False]))
+ else:
+ self.log.error("No characteristic to configure, request ignored!")
- def ap_imageupdate(self, image_index, file, from_console=True, raw=False, display_ind=None, label=None, rotation=None, cropfit=False):
+ def ap_imageupdate(self, image_index, file, raw=False, display_ind=None, label=None, rotation=None, cropfit=False, address=None, group_id=None):
"""
Update tag image.
inputs:
+ - address: Either Bluetooth address or ESL ID or 'all'
+ - group_id: ESL group ID
- image_index: Image index
- filename: Filename with path
- raw: Open and load file without conversion
@@ -296,22 +362,41 @@ def ap_imageupdate(self, image_index, file, from_console=True, raw=False, displa
- label Label to be printed as an overlay to the image
- rotation Clockwise (cw), Counter-clockwise (ccw), flip
"""
-
- # If image file is from console, check validity
- if from_console and not self.controller_command:
+ # If image file is from console, check validity
+ if isinstance(file, str) and not self.controller_command:
self.raw_image = b""
try:
image_file = open(file, "rb")
self.raw_image = image_file.read()
image_file.close()
except FileNotFoundError:
- self.log.error("Cannot open image file: %s", file)
+ raise ImageUpdateFailed(f"Cannot open image file: {file}")
+
+ tags_to_update = []
+ tag = None
+ if address is None: # if no address is given, then check if there's only one active connection
+ tag = self.get_active_tag()
+ if tag is None:
return
+ else:
+ if not isinstance(address, esl_lib.Address): # Check for non-address datatype
+ esl_id, gid = self.get_esl_address(address, group_id)
+ if esl_id == BROADCAST_ADDRESS:
+ tags_to_update = [tag for tag in self.tag_db.list_state(TagState.CONNECTED) if (group_id is None or tag.group_id == group_id)]
+ if len(tags_to_update) == 0:
+ self.log.error("No ESL from group %d seems to be connected, image upload failed. Please try another group.", gid)
+ elif esl_id is None:
+ tag = self.tag_db.find(address)
+ if tag is None:
+ self.log.error("Tag at address %s not found in any group, command not sent", address)
+ return
+ else:
+ tag = self.tag_db.find((esl_id, gid))
+ else:
+ tag = self.tag_db.find(address)
- # Check for connection
- if not self.active_address in self.tags:
- self.log.error("Image update needs an active connection!")
- return
+ if tag is not None:
+ tags_to_update.append(tag)
# If command is from AP remote controller (usually mobile running the demo application), send the notification
if self.controller_command == CCMD_IMAGE_UPDATE:
@@ -321,65 +406,17 @@ def ap_imageupdate(self, image_index, file, from_console=True, raw=False, displa
self.notify_controller(CCMD_REQUEST_DATA, CONTROLLER_COMMAND_SUCCESS, REQUEST_IMAGE_DATA_HEADER, self.image_data_offset, REQUEST_IMAGE_DATA_RESERVED)
return
- if raw:
- self.log.info("Raw image file opened: %s", file)
- #nothing to do with raw files except upload!
- else:
- # Open and convert image file, otherwise
- ots_object_type = None
- if display_ind is None:
- self.image_file = file
- self.rotation = rotation
- self.label = label
- ots_object_type = self.ots_get_object_type(self.active_address, image_index)
- if ots_object_type == "unknown":
- self.log.error("Unable to read ots object type")
- elif ots_object_type == None:
- return # callback will take place in this case from OTS_RETURN_TYPE event handler!
- else:
- display_ind = self.find_type_matching_display_index(self.active_address, ots_object_type)
- if display_ind is None:
- self.log.error("Unable to find a valid display index for ots_object_type: " + hex(ots_object_type))
- eid, gid = self.esl_addr_from_bt(self.active_address)
- self.ap_update_complete(eid, gid)
- return
- else:
- if self.tags[self.active_address].display_count is None or display_ind >= self.tags[self.active_address].display_count:
- self.log.error("Invalid display index: " + str(display_ind))
- return
- _, ots_object_type = self.get_display_info(self.active_address, display_ind)
-
- disp_size, disp_type = self.get_display_info(self.active_address, display_ind)
-
- if ots_object_type and disp_type == ots_object_type:
- self.log.info("Display type matches object type")
- if type(file) == str:
- self.xbm_converter.open(file)
- if self.xbm_converter.image is not None:
- self.log.info("Image file opened: %s", file)
- elif type(file) == bytes:
- self.xbm_converter.open_frombytes(file)
- if ots_object_type == ESL_WSTK_DISPLAY_TYPE:
- self.raw_image = self.xbm_converter.convert(display_size=disp_size, bw=True, label=label, rotation=rotation, cropfit=cropfit) # bw=true if object type FF, bw=False if FE
- elif ots_object_type == ESL_EPAPER_DISPLAY_TYPE:
- self.raw_image = self.xbm_converter.convert(display_size=disp_size, bw=False, label=label, rotation=rotation, cropfit=cropfit)
- else:
- self.raw_image = b""
- self.log.error("Unknown OTS object type, automatic conversion can't be done. Please upload raw image data.")
- else:
- if self.controller_command == CCMD_IMAGE_UPDATE:
- self.notify_controller(CCMD_IMAGE_UPDATE, CONTROLLER_COMMAND_FAIL)
- self.log.error("Cannot upload file: display type is not the same as object type!")
-
- # Send file if raw input or converted result seems OK
- if len(self.raw_image) != 0:
- if image_index > self.tags[self.active_address].auto_image_count:
- self.tags[self.active_address].auto_image_count = image_index
- self.update_image(image_index)
- else:
- self.log.error("Cannot upload file: image conversion failed!")
- if self.controller_command == CCMD_IMAGE_UPDATE:
- self.notify_controller(CCMD_IMAGE_UPDATE, CONTROLLER_COMMAND_FAIL)
+ for tag in tags_to_update:
+ try:
+ tag.image_update(image_index, file, raw, display_ind, label, rotation, cropfit)
+ self.log.info("Image update started for tag at %s to image slot %d", tag.ble_address, image_index)
+ except ImageUpdateFailed as ex:
+ self.log.error("Image update failed for tag at %s to image slot %d", tag.ble_address, image_index)
+ self.log.error(ex)
+ continue
+ except ImageTypeRequired:
+ self.log.debug("Type info required for image %d on tag at %s - request readout", image_index, tag.ble_address)
+ continue
def ap_unassociate(self, address, group_id):
"""
@@ -389,155 +426,98 @@ def ap_unassociate(self, address, group_id):
- group_id: ESL group ID
"""
tlv = TLV_OPCODE_UNASSOCIATE
- esl_id = self.get_esl_id(address, group_id)
-
+ esl_id, group_id = self.get_esl_address(address, group_id)
data = bytearray(self.get_opcode_len(tlv))
if esl_id is not None:
+ data[0:2] = tlv, esl_id
+ self.route_command(esl_id, group_id, data)
if esl_id == BROADCAST_ADDRESS:
- self.remove_tag(esl_id, group_id)
+ synced_in_group = [tag for tag in self.tag_db.list_esl_state(EslState.SYNCHRONIZED) if tag.group_id == group_id]
+ for tag in synced_in_group:
+ tag.block(elw.ESL_LIB_STATUS_UNASSOCITED) # blocking before remove_tag() call will preserve tag object in memory but still clears LTK!
+ if tag.state != TagState.CONNECTING:
+ self.remove_tag(tag=tag)
if self.controller_command == CCMD_UNASSOCIATE:
self.notify_controller(CCMD_UNASSOCIATE, CONTROLLER_COMMAND_SUCCESS, esl_id)
else:
- tag = self.tags[self.bt_addr_from_esl(esl_id, group_id)]
- if tag.state not in [ST_UNASSOCIATED, ST_UNSYNCHRONIZED]:
- tag.pending_unassociate = True
- data[0:2] = tlv, esl_id
- # Handle the connected ESL tag separately, if any
- if esl_id == BROADCAST_ADDRESS and self.active_address in self.tags:
- eid, gid = self.esl_addr_from_bt(self.active_address)
- if gid == group_id:
- # broadcast addresses are otherwise routed over periodic advertisement, always!
- self.route_command(eid, gid, data) # therefore the eid needs to be overridden for connected tags
- self.log.warning("Unassociate connected ESL Tag results in disconnection!")
- else:
- self.route_command(esl_id, group_id, data)
+ tag = self.tag_db.find((esl_id, group_id))
+ if tag is not None and tag.esl_state not in [EslState.UNASSOCIATED, EslState.UNSYNCHRONIZED]:
+ tag.pending_unassociate = True # need to set explicitly for tags in synchronized state
elif IOP_TEST and re.fullmatch(VALID_ESL_ID_NUMBER_REGEX, address) is not None:
data[0:2] = tlv, int(address)
self.queue_pawr_command(group_id, data)
- self.log.warning("Tag with address %s not found in group %d, send over PawR due IOP_TEST mode", address, group_id)
+ self.log.warning("Tag at address %s not found in group %d, send over PawR due IOP_TEST mode", address, group_id)
else:
try:
bt_address = esl_lib.Address.from_str(address)
if self.key_db.find_ltk(bt_address) is None:
raise esl_key_lib.Error(elw.SL_STATUS_NOT_FOUND)
self.key_db.delete_ltk(bt_address)
- self.log.warning("Currently unconfigured Tag with address %s removed from the bonding database", address)
+ self.log.warning("Currently unconfigured tag at address %s removed from the bonding database", address)
except (ValueError, esl_key_lib.Error):
- self.log.error("Tag with address %s not found in any group nor in bonding database, command ignored", address)
+ self.log.error("Tag at address %s not found in any group nor in bonding database, command ignored", address)
if self.controller_command == CCMD_UNASSOCIATE:
self.notify_controller(CCMD_UNASSOCIATE, CONTROLLER_COMMAND_FAIL)
- def ap_list(self, param_list, verbose=False, group_id = None):
+ def ap_list(self, param_list, verbose=False, group_id=None):
"""
List tag information.
- inputs:
- - param_list: List of parameters, possible values:
- - 'advertising': List advertising tags
- - 'synchronized': List synchronized tags
- - 'unsynchronized': List unsynchronized tags
- - 'connected': List connected tags
- - verbose: Print more verbose information
"""
- list_of_tags = []
+ list_of_tags: list[Tag] = []
for param in param_list:
- count = 0
+ verbose_param = verbose
# Advertising
if param == "advertising":
- count = len(self.ble_addresses)
- for tag_addr in self.ble_addresses:
- if tag_addr in self.tags:
- log(self.tags[tag_addr])
- else:
- log(f"BLE Address: {tag_addr}")
- if count == 0:
- log("There's no advertising tag.")
- elif count == 1:
- log("There's one advertising tag.")
- else:
- log(f"There are {count} advertising tags.")
+ tags = self.tag_db.list_advertising()
+ verbose_param = False
# Synchronized
elif param == "synchronized":
- for key, tag in self.tags.items():
- if tag.state == ST_SYNCHRONIZED and (group_id is None or group_id == tag.group_id):
- if verbose:
- log(tag.get_info())
- log("-" * 36)
- else:
- log(tag)
- count = count + 1
- list_of_tags.append(tag.ble_address)
- if count == 0:
- log("There's no synchronized tag.")
- elif count == 1:
- log("There's one synchronized tag.")
- else:
- log(f"There are {count} synchronized tags.")
+ tags = self.tag_db.list_esl_state(EslState.SYNCHRONIZED)
+ list_of_tags += tags
# Unsynchronized
elif param == "unsynchronized":
- for key, tag in self.tags.items():
- if key == self.active_address:
- continue
- if tag.state == ST_UNSYNCHRONIZED and (group_id is None or group_id == tag.group_id):
- if verbose:
- log(tag.get_info())
- log("-" * 36)
- else:
- log(tag)
- count = count + 1
- if count == 0:
- log("There's no unsynchronized tag.")
- elif count == 1:
- log("There's one unsynchronized tag.")
- else:
- log(f"There are {count} unsynchronized tags.")
+ tags = self.tag_db.list_esl_state(EslState.UNSYNCHRONIZED)
# Connected
elif param == "connected":
- if self.active_address in self.tags:
- tag = self.tags[self.active_address]
- if verbose:
- log(tag.get_info())
- log("-" * 36)
- else:
- log(tag)
- count = count + 1
- if count == 0:
- log("There's no connected tag.")
- elif count == 1:
- log("There's one connected tag.")
- else:
- log(f"There are {count} connected tags.")
+ tags = self.tag_db.list_state((TagState.CONNECTED, TagState.CONNECTING))
# Blocked
elif param == "blocked":
- count = len(self.blocked_list)
- for tag_addr in self.blocked_list:
- if tag_addr in self.tags:
- log(self.tags[tag_addr])
- else:
- log(f"BLE Address: {tag_addr}")
- if count == 0:
- log("There's no blocked tag.")
- elif count == 1:
- log("There's one blocked tag.")
+ tags = self.tag_db.list_blocked()
+ verbose_param = False
+ # Invalid
+ else:
+ continue
+
+ if group_id is not None:
+ tags = [tag for tag in tags if tag.group_id == group_id]
+
+ for tag in tags:
+ if verbose_param:
+ log(tag.get_info())
+ log("-" * 36)
else:
- log(f"There are {count} blocked tags that may or may not advertising.")
+ if tag.blocked and verbose:
+ log(str(tag) + f", blocked by {esl_lib.get_enum('ESL_LIB_STATUS_', tag.blocked)} on {dt.fromtimestamp(tag.last_req_timestamp).strftime('%d/%b %H:%M:%S.%f')[:-3]}")
+ else:
+ log(tag)
+ if len(tags) == 0:
+ log(f"There's no {param} tag" + (f" in group {group_id}" if group_id is not None else " at all."))
+ elif len(tags) == 1:
+ log(f"There's one {param} tag" + (f" in group {group_id}" if group_id is not None else " overall."))
+ else:
+ log(f"There are {len(tags)} {param} tags" + (f" in group {group_id}" if group_id is not None else " overall."))
if self.controller_command == CCMD_LIST:
if len(list_of_tags) != 0:
- for addr in list_of_tags:
- if addr != list_of_tags[-1]:
- self.notify_controller(CCMD_LIST, CONTROLLER_COMMAND_SUCCESS, CONTROLLER_REQUEST_MORE_DATA, self.tags[addr].esl_address,
- str(self.tags[addr].ble_address),
- self.tags[addr].max_image_index + 1 if self.tags[addr].max_image_index is not None else 0,
- len(self.tags[addr].display_info),
- struct.pack(' 0:
+ self.log.error("Factory reset command is invalid and thus ignored by tags in Synchronized state!")
else:
- self.log.warning("Tag with address %s not found in any group, command not sent", address)
+ self.log.warning("Tag at address %s not found in any group, command not sent", address)
def ap_update_complete(self, address, group_id):
"""
- Issue an Update Complete command to a connected tag
+ Issue an Update Complete command to connected tag(s)
inputs:
- address: Either Bluetooth address or ESL ID
- group_id: ESL group ID
"""
-
- # Check for connection
- if not self.active_address in self.tags:
- self.log.error("Update Complete can be used only with active connection!")
- if not IOP_TEST:
- return
-
+ tag = None
tlv = TLV_OPCODE_UPDATE_COMPLETE
- esl_id = self.get_esl_id(address, group_id)
+ esl_id, gid = self.get_esl_address(address, group_id)
if esl_id is not None:
- state = self.tags[self.bt_addr_from_esl(esl_id, group_id)].state
- if state == ST_SYNCHRONIZED:
- self.log.warning("Update complete command is invalid in Synchronized state!")
- pass
- elif re.fullmatch(VALID_ESL_ID_NUMBER_REGEX, str(address)) is not None:
- esl_id = int(address)
+ if esl_id != BROADCAST_ADDRESS:
+ tag = self.tag_db.find((esl_id, gid))
+ if tag.esl_state == EslState.SYNCHRONIZED:
+ self.log.warning("Update complete command is invalid in Synchronized state!")
else:
- self.log.error("Tag with address %s not found in any group, command not sent", address)
+ self.log.error("Tag at address %s not found in any group, command not sent", address)
return
+ # Check for connection
+ if tag is not None and tag.state != TagState.CONNECTED:
+ self.log.error("Update Complete can be used only with active connection!")
+ if not IOP_TEST:
+ return
+
data = bytearray(self.get_opcode_len(tlv))
data[0:2] = tlv, esl_id
self.route_command(esl_id, group_id, data)
@@ -659,14 +632,10 @@ def ap_refresh_display(self, address, group_id, display_idx):
- group_id: ESL group ID
"""
tlv = TLV_OPCODE_REFRESH_DISPLAY
- esl_id = self.get_esl_id(address, group_id)
+ esl_id, group_id = self.get_esl_address(address, group_id)
- if esl_id is not None:
- pass
- elif re.fullmatch(VALID_ESL_ID_NUMBER_REGEX, str(address)) is not None:
- esl_id = int(address)
- else:
- self.log.error("Tag with address %s not found in any group, command not sent", address)
+ if esl_id is None:
+ self.log.error("Tag at address %s not found in any group, command not sent", address)
return
data = bytearray(self.get_opcode_len(tlv))
@@ -685,16 +654,10 @@ def ap_display_image(self, address, group_id, image_idx, display_idx, absolute_v
- absolute_base: ESL Absolute Time epoch value
"""
tlv = TLV_OPCODE_DISPLAY_IMAGE
- esl_id = self.get_esl_id(address, group_id)
+ esl_id, group_id = self.get_esl_address(address, group_id)
- if esl_id is not None:
- pass
- elif re.fullmatch(VALID_ESL_ID_NUMBER_REGEX, str(address)) is not None:
- esl_id = int(address)
- else:
- if self.controller_command != None:
- self.notify_controller(self.controller_command, CONTROLLER_COMMAND_FAIL)
- self.log.error("Tag with address %s not found in any group, command not sent", address)
+ if esl_id is None:
+ self.log.error("Tag at address %s not found in any group, command not sent", address)
return
if absolute_value is not None:
@@ -722,17 +685,11 @@ def ap_ping(self, address, group_id):
- group_id: ESL group ID
"""
tlv = TLV_OPCODE_PING
- esl_id = self.get_esl_id(address, group_id)
+ esl_id, group_id = self.get_esl_address(address, group_id)
if esl_id is None:
- self.log.warning("Tag with address %s not found in any group", address)
- if re.fullmatch(VALID_ESL_ID_NUMBER_REGEX, address) is not None:
- esl_id = int(address)
- else:
- self.log.error("Unknown address can be a valid ESL ID only, command ignored!")
- if self.controller_command != None:
- self.notify_controller(self.controller_command, CONTROLLER_COMMAND_FAIL)
- return
+ self.log.error("Unknown address can be a valid ESL ID only, command ignored!")
+ return
elif esl_id == BROADCAST_ADDRESS and not IOP_TEST:
self.log.error("Using broadcast with ping makes no sense, command ignored!")
if self.controller_command != None:
@@ -754,7 +711,11 @@ def ap_vendor_opcode(self, address, group_id, vendor_data=None):
- data: ESL vendor specific TLV
"""
tlv = TLV_OPCODE_VENDOR_SPECIFIC
- esl_id = self.get_esl_id(address, group_id)
+ esl_id, group_id = self.get_esl_address(address, group_id)
+
+ if esl_id is None:
+ self.log.error("Unknown address can be a valid ESL ID only, command ignored!")
+ return
data_length = self.get_opcode_len(tlv)
@@ -763,14 +724,6 @@ def ap_vendor_opcode(self, address, group_id, vendor_data=None):
data_length += extra_len
tlv = self.set_tlv_len(tlv, extra_len)
- if esl_id is None:
- self.log.warning("Tag with address %s not found in any group", address)
- if re.fullmatch(VALID_ESL_ID_NUMBER_REGEX, address) is not None:
- esl_id = int(address)
- else:
- self.log.error("Unknown address can be a valid ESL ID only, command ignored!")
- return
-
data = bytearray(data_length)
data[0:2] = tlv, esl_id
if vendor_data is not None:
@@ -785,14 +738,10 @@ def ap_service_reset(self, address, group_id):
- group_id: ESL group ID
"""
tlv = TLV_OPCODE_SERVICE_RST
- esl_id = self.get_esl_id(address, group_id)
+ esl_id, group_id = self.get_esl_address(address, group_id)
- if esl_id is not None:
- pass
- elif re.fullmatch(VALID_ESL_ID_NUMBER_REGEX, str(address)) is not None:
- esl_id = int(address)
- else:
- self.log.error("Tag with address %s not found in any group, command not sent", address)
+ if esl_id is None:
+ self.log.error("Tag at address %s not found in any group, command not sent", address)
return
data = bytearray(self.get_opcode_len(tlv))
@@ -807,11 +756,13 @@ def ap_sync(self, start, pa_interval=None):
- pa_interval: Periodic advertising interval list in ms.
See 'sync' command for more details.
"""
- if self.is_there_tag(ST_SYNCHRONIZED):
+ syncronized_tags = self.tag_db.list_esl_state(EslState.SYNCHRONIZED)
+ if len(syncronized_tags):
self.log.warning("There are already synchronized tags, they will lose sync!")
- self.set_tags_state(ST_UNSYNCHRONIZED, ST_SYNCHRONIZED)
- if not start:
+ if start is None:
+ log(f"PAwR sync is currently{' ' if self.pawr_active else ' not '}running")
+ elif not start:
self.stop_pawr_train()
# Clear unsent commands
self.esl_command_queue_lock.acquire()
@@ -873,47 +824,7 @@ def set_mode_handlers(self):
if self.demo_mode:
self.event_handler_prefix_list.append("demo_")
- def arg_check_ok(self, arg, pattern):
- """ Check command line arguments """
- return bool(re.fullmatch(pattern, arg) is not None)
-
- def address_auto_type(self, address: str, address_type=None):
- """ Guess address type if not provided """
- if address_type is None:
- for i in self.ble_addresses:
- if i == address:
- return i
- return esl_lib.Address.from_str(address, address_type)
-
##################### ESL methods #####################
- def get_display_info(self, ble_address, display_ind):
- disp_size = None
- disp_type = None
- if ble_address in self.tags:
- disp_size = (self.tags[ble_address].display_info[display_ind].width, self.tags[ble_address].display_info[display_ind].height)
- disp_type = self.tags[ble_address].display_info[display_ind].type
- return disp_size, disp_type
-
- def find_type_matching_display_index(self, ble_address, display_type):
- if ble_address in self.tags:
- for x in self.tags[ble_address].display_info:
- if x.type == display_type:
- return self.tags[ble_address].display_info.index(x)
- return None
-
- def ots_get_object_type(self, ble_address, image_index):
- obj_type = None
- if ble_address in self.tags:
- type_dict = self.tags[ble_address].ots_image_type
- try:
- obj_type = type_dict[image_index]
- except KeyError:
- pass
-
- if obj_type is None:
- self.lib.get_image_type(self.conn_handle_from_bt(ble_address), image_index)
-
- return obj_type
def new_auto_address(self, id):
esl_id = id % ESL_MAX_TAGS_IN_AUTO_GROUP
@@ -921,30 +832,24 @@ def new_auto_address(self, id):
esl_address = (esl_id & BROADCAST_ADDRESS) | (group_id & 0x7f) << 8
return esl_address
- def sync_fake_tags(self, count, state=ST_SYNCHRONIZED):
- for i in range(count):
- address = "{}{}:{}{}:{}{}:{}{}:{}{}:{}{}".format(*random.choices(string.ascii_letters[:6] + string.digits, k=12))
- self.init_tag(address)
- values = self.configure(self.tags[address])
- self.tags[address].state = state
- self.tags[address].gatt_values.update(values)
+ def add_fake_tags(self, count):
+ for _ in range(count):
+ address = esl_lib.Address(bytes(random.choices(range(256), k=6)))
+ tag = self.tag_db.add(self.lib, address)
+ values = self.configure(tag)
+ tag.gatt_values.update(values)
- def get_absolute_time(self, now = None):
+ def get_absolute_time(self):
""" Get absolute time in milliseconds """
- delta_time = 0
- if now is None:
- delta_time = dt.now() - self.start_time
- else:
- delta_time = now - self.start_time
- ms = (delta_time.days * 24 * 60 * 60 + delta_time.seconds) * 1000 + delta_time.microseconds / 1000.0
- int_ms = int(ms)
- return int_ms
+ delta_time = datetime.now() - self.start_time
+ ms = delta_time / timedelta(microseconds=1000)
+ return int(ms)
- def calculate_exec_time(self, now, d_hour, d_min, d_sec, d_msec, date = None):
+ def calculate_exec_time(self, now, d_hour, d_min, d_sec, d_micsec, date=None):
""" Calculate command execution time delay in milliseconds """
delay_ms = None
delay_time = now
- delay_time = delay_time.replace(hour=d_hour, minute=d_min, second=d_sec, microsecond=d_msec*1000)
+ delay_time = delay_time.replace(hour=d_hour, minute=d_min, second=d_sec, microsecond=d_micsec)
if date is not None:
delay_time = delay_time.replace(year=date.year, month=date.month, day=date.day)
delta_time = delay_time - now
@@ -961,18 +866,6 @@ def calculate_exec_time(self, now, d_hour, d_min, d_sec, d_msec, date = None):
self.log.error("Requested date and time has passed already!")
return delay_ms
- def generate_key_material(self):
- """ Generate AP key """
- return self.ead.generate_key() + secrets.token_bytes(EAD_IV_SIZE)
-
- def update_state(self, state):
- """ Update state """
- self.state = state
- if state in STATE_STRINGS:
- self.log.debug("[State] " + str(STATE_STRINGS[state]))
- else:
- self.log.warning("[State] unknown: %s", str(state))
-
def queue_pawr_command(self, gid, data):
""" Prepare periodic advertisement with responses payload by appending to esl command queue """
self.esl_command_queue_lock.acquire()
@@ -1022,7 +915,7 @@ def send_pawr_commands(self, subevents):
data = self.create_sync_packet(gid, commands)
if data is not None:
- self.lib.pawr_set_data(self.pawr_handle, gid, data)
+ self.lib.pawr_set_data(self.pawr_handle, gid, len(commands), data)
self.update_pending_commands_list(gid, commands)
for gid in empty_gid_list:
@@ -1039,83 +932,73 @@ def datasize(self, cmd_list):
def synchronization_handler(self):
""" Handle Tag synchronization """
- if self.pawr_active:
- # Remove outdated commands
- calculated_timeout = self.pa_timer_interval * AUX_SYNC_IND_PDU_MAX_SKIP_COUNT
- max_timeout = 164 # From BLE spec: the maximum permitted time between successful receives of periodic advertisement packages is 163.84 seconds.
- self.remove_outdated_commands(calculated_timeout if calculated_timeout < max_timeout else max_timeout)
-
- for address, tag in self.tags.items():
- already_sent = [] # List of slots for resent tag commands
-
- if tag.state == ST_SYNCHRONIZED:
- tnow = dt.now().timestamp()
- req_timestamp_diff, resp_timestamp_diff = tag.timestamps_diff(tnow)
-
- # Send NOP to keep tag synchronized
- if req_timestamp_diff >= TAG_SYNC_KEEPING_INTERVAL - self.pa_timer_interval:
- data = bytearray(self.get_opcode_len(TLV_OPCODE_PING))
- data[0:2] = TLV_OPCODE_PING, tag.esl_id
- self.queue_pawr_command(tag.group_id, data)
- tag.update_request_timestamp()
- tag.unresp_command_number += 1
- # Check tag timeout
- elif resp_timestamp_diff > TAG_SYNC_TIMEOUT + self.pa_timer_interval:
- self.log.info("Set tag %s to Unsynchronized because of synchronization timeout", address)
- tag.state = ST_UNSYNCHRONIZED
- tag.update_flags(BASIC_STATE_FLAG_SYNCHRONIZED, False)
- continue
-
- # Resend unresponded commands
- current_pending_commands = self.esl_pending_commands.copy()
- for key in current_pending_commands:
- for cmd in current_pending_commands[key]:
- if cmd.esl_id == tag.esl_id and cmd.group_id == tag.group_id \
- and ((tnow - cmd.timestamp) > 1.5 * self.pa_timer_interval + .1):
- self.remove_esl_pending_command(cmd) # this will remove given command from self.esl_pending_commands
- if self.bt_addr_from_esl(cmd.esl_id, cmd.group_id) != self.active_address and tag.unresp_command_number < ESL_CMD_MAX_RETRY_COUNT:
- self.log.info("Resending unresponded command: (0x%s)", cmd.params.hex())
- self.resend_pawr_command(cmd) # and then re-queuing it to the bottom of the "FIFO"
- if cmd.slot_number not in already_sent:
- tag.unresp_command_number += 1
- already_sent.append(cmd.slot_number)
- elif tag.unresp_command_number >= ESL_CMD_MAX_RETRY_COUNT:
- self.log.warning("Tag at address %s does not respond to synchronization packets, stop retrying", address)
- tag.unresp_command_number = 0
- already_sent = []
-
- elif tag.state == ST_UNSYNCHRONIZED:
- tnow = dt.now().timestamp()
- _, resp_timestamp_diff = tag.timestamps_diff(tnow)
- # Check tag timeout
- if resp_timestamp_diff > TAG_UNASSOCIATE_TIMEOUT:
- self.log.info("Unassociate Tag %s because of timeout in Unsynchronized state", address)
- tag.state = ST_UNASSOCIATED
- continue
+ if not self.pawr_active:
+ return
- def resend_pawr_command(self, cmd):
- """ Resend pawr command """
- self.requeue_pawr_command(cmd.group_id, cmd.params)
+ # Remove outdated commands
+ calculated_timeout = self.pa_timer_interval * AUX_SYNC_IND_PDU_MAX_SKIP_COUNT
+ max_timeout = 164 # From BLE spec: the maximum permitted time between successful receives of periodic advertisement packages is 163.84 seconds.
+ self.remove_outdated_commands(min(calculated_timeout, max_timeout))
+
+ syncronized_tags = self.tag_db.list_esl_state(EslState.SYNCHRONIZED)
+ response_timeout = (1.5 * self.pa_timer_interval) + (0.00125 * self.response_slot_delay)
+ tnow = dt.now().timestamp()
+
+ for tag in syncronized_tags:
+ req_timestamp_diff, resp_timestamp_diff = tag.timestamps_diff(tnow)
+ # Send NOP to keep tag synchronized
+ if req_timestamp_diff >= TAG_SYNC_KEEPING_INTERVAL - self.pa_timer_interval:
+ data = bytearray(self.get_opcode_len(TLV_OPCODE_PING))
+ data[0:2] = TLV_OPCODE_PING, tag.esl_id
+ self.queue_pawr_command(tag.group_id, data)
+ tag.update_request_timestamp()
+ tag.unresp_command_number += 1
+ # Check tag timeout
+ elif resp_timestamp_diff > TAG_SYNC_TIMEOUT + self.pa_timer_interval:
+ self.log.info("Set tag %s to Unsynchronized because of synchronization timeout", tag.ble_address)
+ tag.unsynchronize()
+ continue
- def reorder_device_list(self, address: esl_lib.Address):
- """ Append address to the end of the list """
- if address in self.ble_addresses:
- self.ble_addresses.remove(address)
- if address not in self.blocked_list:
- self.ble_addresses.append(address)
+ already_sent = [] # List of slots for resent tag commands
+ # Resend unresponded commands
+ if tag.group_id in self.esl_pending_commands:
+ current_pending_commands = self.esl_pending_commands[tag.group_id]
+ for cmd in current_pending_commands:
+ if cmd.esl_id == tag.esl_id and cmd.group_id == tag.group_id \
+ and ((tnow - cmd.timestamp) > response_timeout):
+ self.remove_esl_pending_command(cmd) # this will remove given command from self.esl_pending_commands
+ # Tag must be in IDLE state to send the command
+ if tag.state == TagState.IDLE and tag.unresp_command_number < ESL_CMD_MAX_RETRY_COUNT:
+ self.log.info("Resending command: (0x%s) to ESL %d in group %d", cmd.params.hex(), tag.esl_id, tag.group_id)
+ self.requeue_pawr_command(cmd.group_id, cmd.params) # and then re-queuing it to the bottom of the "FIFO"
+ if cmd.slot_number not in already_sent:
+ tag.unresp_command_number += 1
+ already_sent.append(cmd.slot_number)
+ elif tag.unresp_command_number >= ESL_CMD_MAX_RETRY_COUNT:
+ self.log.warning("Tag at address %s does not respond to synchronization packets, stop retrying", tag.ble_address)
+ tag.unresp_command_number = 0
+
+ unsyncronized_tags = self.tag_db.list_esl_state(EslState.UNSYNCHRONIZED)
+ for tag in unsyncronized_tags:
+ tnow = dt.now().timestamp()
+ _, resp_timestamp_diff = tag.timestamps_diff(tnow)
+ # Check tag timeout
+ if resp_timestamp_diff > TAG_UNASSOCIATE_TIMEOUT:
+ self.log.info("Unassociate tag %s because of timeout in Unsynchronized state", tag.ble_address)
+ tag.unassociate()
def upload_next_image(self, tag: Tag):
if tag is None:
return
tag.auto_image_count += 1
if tag.auto_image_count < min((tag.max_image_index + 1), IMAGE_MAX_AUTO_UPLOAD_COUNT):
- self.log.info("Sending new image")
- self.upload_auto_image(tag.auto_image_count)
+ self.log.info("Sending new image to ESL at address %s", tag.ble_address)
+ self.upload_auto_image((tag.auto_image_count % len(self.image_files)), tag)
elif tag.provisioned:
disp_image = bytearray(self.get_opcode_len(TLV_OPCODE_DISPLAY_IMAGE))
disp_image[0:4] = TLV_OPCODE_DISPLAY_IMAGE, tag.esl_id , 0, 0
disp_image = bytes(disp_image)
- self.send_cp_command(tag.ble_address, disp_image)
+ self.send_cp_command(tag, disp_image)
self.log.info("Display Image command sent")
def dequeue(self):
@@ -1125,13 +1008,29 @@ def dequeue(self):
event = self.lib.event_queue.get(timeout=BLOCKING_WAIT_TIMEOUT)
except queue.Empty:
continue
+
if isinstance(event, esl_lib.EventError):
- if event.lib_status not in [elw.ESL_LIB_STATUS_PAST_INIT_FAILED, elw.ESL_LIB_STATUS_PAWR_START_FAILED]:
+ if event.lib_status not in [elw.ESL_LIB_STATUS_PAST_INIT_FAILED, elw.ESL_LIB_STATUS_PAWR_START_FAILED, elw.ESL_LIB_STATUS_PAWR_SET_DATA_FAILED]:
self.log.warning("[Event] " + str(event))
+ elif event. sl_status == elw.SL_STATUS_TRANSMIT or (event.sl_status == elw.SL_STATUS_BT_CTRL_UNKNOWN_ADVERTISING_IDENTIFIER and self.pawr_active):
+ self.log.error("[Event] " + str(event))
else:
filter_events = [elw.ESL_LIB_EVT_PAWR_DATA_REQUEST, elw.ESL_LIB_EVT_TAG_FOUND]
if not event.evt_code in filter_events:
self.log.debug("[Event] " + str(event))
+
+ if hasattr(event, "node_id"):
+ tag = self.tag_db.find(event.node_id)
+ elif hasattr(event, "address"):
+ tag = self.tag_db.find(event.address)
+ elif hasattr(event, "connection_handle"):
+ tag = self.tag_db.find(event.connection_handle)
+ else:
+ tag = None # prevent sending unsolicited / mismatching events to tag found in previous cylcle iteration!
+
+ if tag is not None:
+ tag.handle_event(event)
+
try:
enum_prefix = "ESL_LIB_EVT_"
event_name = esl_lib.get_enum(enum_prefix, event.evt_code)[len(enum_prefix):].lower()
@@ -1140,8 +1039,8 @@ def dequeue(self):
if hasattr(self, event_handler_method):
getattr(self, event_handler_method)(event)
except Exception as err:
- print(err)
- print(traceback.format_exc())
+ log(err)
+ log(traceback.format_exc())
self.shutdown_cli()
# ----------------------------------------------------------------------------------------------
@@ -1149,55 +1048,42 @@ def dequeue(self):
def esl_event_system_boot(self, evt: esl_lib.EventSystemBoot):
""" ESL event handler """
+ self.scan_runs = False
+ self.pawr_active = False
+ self.pawr_handle = None
+ self.pawr_restart = None
self.shutdown_timer.cancel()
- # Init GATT database for demo mode
- self.lib.general_command(CMD_AP_CONTROL_INIT_GATTDB)
+ self.max_conn_count_reached = False
+ self.bonding_finished = True
+ for tag in self.tag_db.all():
+ if tag.state == TagState.CONNECTING or tag.esl_state == EslState.CONFIGURING:
+ self.key_db.delete_ltk(tag.ble_address)
+ tag.reset()
def esl_event_tag_found(self, evt: esl_lib.EventTagFound):
""" ESL event handler """
- if evt.rssi > self.rssi_threshold and evt.address not in self.ble_addresses:
- if evt.address in self.tags:
- self.tags[evt.address].reset()
- if self.tags[evt.address].state == ST_SYNCHRONIZED:
- self.log.warning(f"The Tag {evt.address} lost sync!")
- self.tags[evt.address].state = ST_UNSYNCHRONIZED
- self.tags[evt.address].update_flags(BASIC_STATE_FLAG_SYNCHRONIZED, False)
- else:
- self.log.info(f"ESL service found at BLE address: {evt.address} with RSSI: {evt.rssi} dBm")
+ if evt.rssi > self.rssi_threshold:
+ tag = self.tag_db.find(evt.address)
+ if tag is None:
+ tag = self.tag_db.add(self.lib, evt.address)
+ tag.handle_event(evt)
def esl_event_connection_opened(self, evt: esl_lib.EventConnectionOpened):
""" ESL event handler """
- self.connection_dict[evt.connection_handle] = evt.address
- self.active_address = evt.address
- if evt.address in self.ble_addresses:
- self.ble_addresses.remove(evt.address) # remove the device from the unprovisioned/advertising list
- self.init_tag(evt.address, evt.gattdb_handles)
- if not self.tags[evt.address].skip_get_info:
- # read device info
- self.lib.get_tag_info(evt.connection_handle)
- self.update_state(READING_VALUES)
- else:
- self.log.info("Tag info already available, skipping discovery.")
- self.update_state(CONNECTED)
+ tag = self.tag_db.find(evt.address)
+ if tag.provisioned:
+ cmd = self.get_latest_command(tag)
+ if cmd is not None:
+ self.log.debug("Unresponded command removed from PAwR queue!")
+ self.remove_esl_pending_command(cmd)
+
+ def esl_event_bonding_finished(self, evt: esl_lib.EventBondingData):
+ """ ESL event handler """
+ self.bonding_finished = True
def esl_event_tag_info(self, evt: esl_lib.EventTagInfo):
""" ESL event handler """
- self.update_state(CONNECTED)
- tag = self.get_tag(evt.connection_handle)
- tag.gatt_values.update(evt.tlv_data)
- tag.set_valid() # make ESL tag valid from this point on
- if elw.ESL_LIB_DATA_TYPE_GATT_PNP_ID in evt.tlv_data:
- if tag.pnp_vendor_id == None:
- self.log.error("PnP characteristic not found - vendor opcodes support disabled")
- elif tag.pnp_vendor_id == SIG_VENDOR_ID_SILABS:
- self.log.info("Silabs device found - vendor opcodes are not defined")
- else:
- self.log.info(f"PnP characteristic found: {tag.pnp_vendor_id:#x}")
- if elw.ESL_LIB_DATA_TYPE_GATT_SERIAL_NUMBER in evt.tlv_data:
- self.log.info("Serial Number String found: " + str(tag.serial_number))
- values = tag.gatt_write_values
- if len(values) and not tag.provisioned:
- self.write_values(tag, values) # auto-refresh existing tag config
+ pass
def esl_event_pawr_status(self, evt: esl_lib.EventPawrStatus):
""" ESL event handler """
@@ -1219,15 +1105,14 @@ def esl_event_scan_status(self, evt: esl_lib.EventScanStatus):
else:
self.log.info("Scanning stopped.")
self.scan_runs = False
+ for tag in self.tag_db.list_advertising():
+ tag.reset_advertising()
def esl_event_configure_tag_response(self, evt: esl_lib.EventConfigureTagResponse):
""" ESL event handler """
- self.update_state(CONNECTED)
if evt.status == elw.SL_STATUS_OK:
- tag = self.get_tag(evt.connection_handle)
- tag.gatt_values[evt.type] = tag.gatt_write_values[evt.type]
+ tag = self.tag_db.find(evt.connection_handle)
if tag.provisioned:
- self.log.info("ESL Tag fully provisioned")
if self.controller_command == CCMD_CONFIG:
self.notify_controller(CCMD_CONFIG, CONTROLLER_COMMAND_SUCCESS,
tag.esl_address,
@@ -1239,70 +1124,51 @@ def esl_event_configure_tag_response(self, evt: esl_lib.EventConfigureTagRespons
def esl_event_control_point_response(self, evt: esl_lib.EventControlPointResponse):
""" ESL event handler """
- self.log.info("Command written successfully")
- esl_id, _ = self.esl_addr_from_bt(self.connection_dict[evt.connection_handle])
- if evt.data_sent[0] == TLV_OPCODE_UPDATE_COMPLETE and evt.data_sent[1] == esl_id:
- self.past()
+ tag = self.tag_db.find(evt.connection_handle)
+ if evt.data_sent[0] == TLV_OPCODE_UPDATE_COMPLETE and evt.data_sent[1] == tag.esl_id:
+ self.past(tag)
def esl_event_image_transfer_finished(self, evt: esl_lib.EventImageTransferFinished):
""" ESL event handler """
- self.log.info("Image sent to the device")
if self.controller_command == CCMD_REQUEST_DATA:
self.notify_controller(CCMD_IMAGE_UPDATE, CONTROLLER_COMMAND_SUCCESS)
def esl_event_connection_closed(self, evt: esl_lib.EventConnectionClosed):
""" ESL event handler """
- self.log.info(f"Connection to {evt.address} closed with reason " + esl_lib.get_enum("SL_STATUS_",evt.reason))
- self.past_timer.cancel()
- try:
- tag = self.tags[evt.address]
- except KeyError:
- # Create dummy tag object
- tag = Tag(evt.address, dummy=True)
- self.past_initiated = False
- if evt.reason == elw.SL_STATUS_BT_CTRL_REMOTE_USER_TERMINATED:
- if tag.esl_address is not None and not tag.pending_unassociate:
- tag.state = ST_SYNCHRONIZED
- tag.update_flags(BASIC_STATE_FLAG_SYNCHRONIZED)
- else:
- tag.state = ST_UNASSOCIATED
- tag.unresp_command_number = 0
- tag.update_timestamps()
- elif evt.reason == elw.SL_STATUS_BT_CTRL_CONNECTION_TERMINATED_BY_LOCAL_HOST:
- tag.state = ST_UNSYNCHRONIZED
- tag.update_flags(BASIC_STATE_FLAG_SYNCHRONIZED, False)
- if tag.esl_address is not None:
- log(tag.get_info())
- elif not tag.provisioned:
- self.key_db.delete_ltk(tag.ble_address)
- self.active_address = None
- del self.connection_dict[evt.connection_handle]
- self.update_state(IDLE)
+ self.max_conn_count_reached = False
+ tag = self.tag_db.find(evt.address) # Can return None after unassociate command written successfully to ESL Control Point if connected tag
+ if tag is not None:
+ if not tag.provisioned or (evt.reason == elw.SL_STATUS_BT_CTRL_REMOTE_USER_TERMINATED and tag.pending_unassociate):
+ # delete stored LTK for tags that didn't finish provisioning before the connection closed according to ESL Profile spec.
+ # or if there's a pending unassociate executed succesfully
+ self.key_db.delete_ltk(tag.ble_address)
+ tag.reset()
+ if logLevel() <= LEVELS['DEBUG'] and tag.associated:
+ log("Tag info about disconnected device:", _half_indent_log=True)
+ log(tag.get_info())
def esl_event_control_point_notification(self, evt: esl_lib.EventControlPointNotification):
""" ESL event handler """
# after an unassociate all command sent the evt.address will not be part of the list of tags!
- tag = self.get_tag(evt.connection_handle)
+ tag = self.tag_db.find(evt.connection_handle)
if tag is None:
return
- tag.update_state(evt.data, ST_UPDATING if tag.provisioned else ST_CONFIGURING)
- parse_response_data(evt.data, tag.sensor_info)
- if evt.data[0] == TLV_RESPONSE_BASIC_STATE:
- tag.basic_state_flags = int.from_bytes(evt.data[1:2], 'little')
- if tag.pending_unassociate:
- self.remove_tag(tag.esl_id, tag.group_id)
+ ResponseParser(evt.data, tag.sensor_info)
+ if evt.data[0] == TLV_RESPONSE_BASIC_STATE and tag.pending_unassociate:
+ tag.block(elw.ESL_LIB_STATUS_UNASSOCITED) # blocking before remove_tag() call will preserve tag object in memory but still clears LTK!
+ self.remove_tag(tag=tag)
if self.controller_command != None:
self.notify_controller(self.controller_command, CONTROLLER_COMMAND_SUCCESS, tag.esl_id, tag.group_id, evt.data)
def esl_event_pawr_response(self, evt: esl_lib.EventPawrResponse):
""" ESL event handler """
- address = self.identify_sender_tag(evt.response_slot, evt.subevent)
- if address is not None:
- response = self.ead.decrypt(evt.data, KeyMaterial(self.tags[address].response_key))
+ tag = self.identify_sender_tag(evt.response_slot, evt.subevent)
+ if tag is not None:
+ response = self.ead.decrypt(evt.data, KeyMaterial(tag.response_key))
if len(response):
self.handle_pawr_response(response, evt.response_slot, evt.subevent)
else:
- pass # decryption error occured
+ pass # decryption error occurred
else:
self.log.warning("PAwR response received but unable to decrypt: 0x" + evt.data.hex())
@@ -1316,44 +1182,54 @@ def esl_event_error(self, evt: esl_lib.EventError):
""" ESL event handler """
if evt.lib_status == elw.ESL_LIB_STATUS_BONDING_FAILED:
if evt.sl_status in [elw.SL_STATUS_BT_CTRL_PIN_OR_KEY_MISSING, elw.SL_STATUS_BT_SMP_PAIRING_NOT_SUPPORTED]:
- tag = self.get_tag(evt.node_id)
+ tag = self.tag_db.find(evt.node_id)
+ self.bonding_finished = True
if tag is not None:
self.key_db.delete_ltk(tag.ble_address)
- self.active_address = None
- self.update_state(IDLE)
- elif evt.lib_status in [elw.ESL_LIB_STATUS_CONN_FAILED, elw.ESL_LIB_STATUS_CONN_CLOSE_FAILED, elw.ESL_LIB_STATUS_CONN_TIMEOUT]:
- self.past_initiated = False
- if evt.lib_status != elw.ESL_LIB_STATUS_CONN_CLOSE_FAILED:
- if self.active_address in self.ble_addresses:
- self.ble_addresses.remove(self.active_address) # remove the device from the unprovisioned/advertising list
- if evt.sl_status != elw.SL_STATUS_ALREADY_EXISTS:
- # remove node from self.connection_dict
- self.connection_dict = {node:address for node, address in self.connection_dict.items() if address != self.active_address}
- self.active_address = None
- self.update_state(IDLE)
+ elif evt.lib_status == elw.ESL_LIB_STATUS_CONN_FAILED:
+ self.bonding_finished = True
+ if evt.sl_status == elw.SL_STATUS_ABORT or evt.sl_status == elw.SL_STATUS_BT_CTRL_AUTHENTICATION_FAILURE: # handle advertisers that refuse connection retry attempts - e.g. because bonded to other AP
+ tag = self.tag_db.find(evt.node_id)
+ if tag is not None and not tag.blocked and not tag.provisioned:
+ self.log.warning("ESL at address %s has been blocked due to unsuccessful connection attempt(s).", evt.node_id)
+ tag.block(elw.ESL_LIB_STATUS_BONDING_FAILED)
+ elif evt.sl_status in [elw.SL_STATUS_NO_MORE_RESOURCE, elw.SL_STATUS_BT_CTRL_CONNECTION_LIMIT_EXCEEDED] and not self.max_conn_count_reached:
+ self.max_conn_count_reached = True
+ self.log.warning("Access point connection limit reached - suspend connect requests until a connection is closed.")
+ elif evt.lib_status == elw.ESL_LIB_STATUS_CONN_TIMEOUT:
+ self.bonding_finished = True
+ tag = self.tag_db.find(evt.node_id)
+ if tag is not None and not tag.associated and not tag.blocked and evt.data == elw.ESL_LIB_CONNECTION_STATE_CONNECTING:
+ self.remove_tag(tag=tag) # keeps the tag database safe from orphaned objects which have shown no sign of existing
elif evt.lib_status == elw.ESL_LIB_STATUS_GATT_TIMEOUT:
- tag = self.get_tag(evt.node_id)
+ tag = self.tag_db.find(evt.node_id)
if tag is not None:
if not tag.provisioned:
self.key_db.delete_ltk(tag.ble_address)
- self.reorder_device_list(tag.ble_address)
- self.log.error("GATT Timeout, AP goes IDLE")
- self.update_state(IDLE)
+ self.log.error("GATT Timeout for address %s", tag.ble_address)
elif evt.lib_status == elw.ESL_LIB_STATUS_OTS_GOTO_FAILED:
- if evt.sl_status == elw.SL_STATUS_NOT_FOUND:
- self.log.error("No object found with the requested Object ID")
- return
+ tag = self.tag_db.find(evt.node_id)
+ if tag is not None:
+ if evt.sl_status == elw.SL_STATUS_NOT_FOUND:
+ self.log.error("No object found with the requested Object ID for address %s", tag.ble_address)
elif evt.lib_status == elw.ESL_LIB_STATUS_PAWR_START_FAILED:
self.pawr_active = False
elif evt.lib_status == elw.ESL_LIB_STATUS_PAST_INIT_FAILED:
if evt.sl_status == elw.SL_STATUS_BT_CTRL_COMMAND_DISALLOWED:
self.log.info("PAST skipped by ESL already in Synchronized state.")
+ elif evt.lib_status == elw.ESL_LIB_STATUS_CONN_CLOSE_FAILED:
+ self.bonding_finished = True
+ if evt.sl_status == elw.SL_STATUS_TIMEOUT and evt.data == elw.ESL_LIB_CONNECTION_STATE_PAST_CLOSE_CONNECTION:
+ tag = self.tag_db.find(evt.node_id)
+ if tag is not None and tag.provisioned and not tag.advertising:
+ self.log.debug("Check if Tag at address %s got synchronized despite the connection closing timeout.", tag.ble_address)
+ self.ap_ping(tag.esl_id, tag.group_id) # Special edge case in which the synced flag may not be set after disconnection -> check if tag is synced
def esl_event_image_type(self, evt: esl_lib.EventImageType):
+ """ ESL event handler """
# Cache image type
- tag = self.get_tag(evt.connection_handle)
- tag.ots_image_type[evt.img_index] = evt.type_data[3]
- self.ap_imageupdate(evt.img_index, self.image_file, False, label=self.label, rotation=self.rotation)
+ tag = self.tag_db.find(evt.connection_handle)
+ self.ap_imageupdate(evt.img_index, tag.image_file, address = tag.ble_address, label=tag.label, rotation=tag.rotation)
def esl_event_bonding_data(self, evt: esl_lib.EventBondingData):
""" ESL event handler """
@@ -1364,9 +1240,14 @@ def esl_event_bonding_data(self, evt: esl_lib.EventBondingData):
# ESL event handler method extensions in CLI mode
def cli_esl_event_error(self, evt: esl_lib.EventError):
- if evt.lib_status == elw.ESL_LIB_STATUS_CONN_FAILED:
- if evt.sl_status == elw.SL_STATUS_ABORT and evt.node_id not in self.blocked_list: # handle advertisers bonded to different AP
- self.log.info("ESL at address %s refused connection attempts - probably bonded to other AP", evt.node_id)
+ """ ESL event handler in CLI mode """
+ if evt.lib_status in [elw.ESL_LIB_STATUS_CONN_FAILED, elw.ESL_LIB_STATUS_CONN_CLOSE_FAILED, elw.ESL_LIB_STATUS_CONN_TIMEOUT]:
+ if evt.lib_status == elw.ESL_LIB_STATUS_CONN_FAILED:
+ if evt.sl_status == elw.SL_STATUS_BT_CTRL_AUTHENTICATION_FAILURE: # handle advertisers bonded to different AP
+ self.log.info("ESL at address %s refused connection attempts - seemingly bonded to other AP", evt.node_id)
+ elif evt.lib_status == elw.ESL_LIB_STATUS_CONN_TIMEOUT:
+ self.log.error("Timeout occured on connection attempt to address %s", evt.node_id)
+ self.revert_auto_mode()
def cli_esl_event_system_boot(self, evt: esl_lib.EventSystemBoot):
""" ESL event handler in CLI mode """
@@ -1375,21 +1256,16 @@ def cli_esl_event_system_boot(self, evt: esl_lib.EventSystemBoot):
def cli_esl_event_tag_found(self, evt: esl_lib.EventTagFound):
""" ESL event handler in CLI mode """
- if evt.rssi > self.rssi_threshold:
- if evt.address not in self.ble_addresses:
- if not self.pawr_active:
- self.log.warning("ESL Tag cannot be synchronized because PAwR is not started.")
- self.log.info("Please start PAwR with command: 'sync start' before configuring.")
- self.ble_addresses.append(evt.address)
-
+ tag = self.tag_db.find(evt.address)
+ if tag is None:
+ return # Happens until the RSSI threshold is met
+ if not self.pawr_active and not tag.advertising and not tag.state == TagState.CONNECTING:
+ self.log.warning("ESL tag can't be synchronized because PAwR is not running.")
+ self.log.info("Don't forget to start PAwR with 'sync start' before completing configuration!")
def cli_esl_event_connection_closed(self, evt: esl_lib.EventConnectionClosed):
""" ESL event handler in CLI mode """
- if self.auto_override:
- self.cmd_mode = False
- self.auto_override = False
- self.log.warning("REVERT TO AUTO MODE!")
- self.set_mode_handlers()
+ self.revert_auto_mode()
# ----------------------------------------------------------------------------------------------
# ESL event handler method extensions in auto mode
@@ -1401,88 +1277,96 @@ def auto_esl_event_system_boot(self, evt: esl_lib.EventSystemBoot):
def auto_esl_event_tag_found(self, evt: esl_lib.EventTagFound):
""" ESL event handler in auto mode """
- if evt.rssi > self.rssi_threshold:
- if self.pawr_active:
- if evt.address not in self.blocked_list:
- if evt.address in self.ble_addresses and self.state == IDLE:
- self.connect(evt.address)
- elif evt.address not in self.ble_addresses:
- self.log.error("ESL Tag cannot be synchronized because PAwR is not started!")
- self.log.info("Please re-start auto mode with command: 'mode auto' to recover.")
- if evt.address not in self.ble_addresses:
- self.ble_addresses.append(evt.address)
- if self.state == IDLE:
+ tag = self.tag_db.find(evt.address)
+ if tag is not None and tag.state == TagState.IDLE:
+ if tag.advertising:
+ if self.pawr_active and not tag.blocked:
self.check_address_list()
+ elif not self.pawr_active:
+ self.log.error("ESL tag cannot be synchronized because PAwR is not started!")
+ self.log.info("Please re-start auto mode with command: 'mode auto' to recover.")
def auto_esl_event_error(self, evt: esl_lib.EventError):
""" ESL event handler in auto mode """
- tag = self.get_tag(evt.node_id)
- if evt.lib_status == elw.ESL_LIB_STATUS_GATT_TIMEOUT:
- self.check_address_list()
- elif evt.lib_status in [elw.ESL_LIB_STATUS_OTS_INIT_FAILED, elw.ESL_LIB_STATUS_OTS_META_READ_FAILED]:
- self.disconnect()
- elif evt.lib_status in [elw.ESL_LIB_STATUS_OTS_ERROR, elw.ESL_LIB_STATUS_OTS_TRANSFER_FAILED, elw.ESL_LIB_STATUS_OTS_GOTO_FAILED, elw.ESL_LIB_STATUS_OTS_UNEXPECTED_OFFSET, elw.ESL_LIB_STATUS_OTS_WRITE_RESP_FAILED]:
- try:
- self.upload_next_image(tag)
- except:
- self.check_address_list()
+ tag = self.tag_db.find(evt.node_id)
+ if evt.lib_status in [elw.ESL_LIB_STATUS_OTS_ERROR, elw.ESL_LIB_STATUS_OTS_TRANSFER_FAILED, elw.ESL_LIB_STATUS_OTS_GOTO_FAILED, elw.ESL_LIB_STATUS_OTS_UNEXPECTED_OFFSET, elw.ESL_LIB_STATUS_OTS_WRITE_RESP_FAILED]:
+ if evt.sl_status != elw.SL_STATUS_TIMEOUT:
+ try:
+ self.upload_next_image(tag)
+ return
+ except:
+ pass
+ if not self.max_conn_count_reached:
+ self.check_address_list()
elif evt.lib_status == elw.ESL_LIB_STATUS_OTS_GOTO_FAILED:
if evt.sl_status == elw.SL_STATUS_NOT_FOUND:
self.upload_next_image(tag)
+ elif evt.lib_status == elw.ESL_LIB_STATUS_BONDING_FAILED:
+ if tag is not None: # Tag may have been already deleted in the edge case where connection timeout of an unprovisined tag precedes bonding fail error
+ tag.block(evt.lib_status)
elif evt.lib_status in [elw.ESL_LIB_STATUS_CONN_FAILED, elw.ESL_LIB_STATUS_CONN_CLOSE_FAILED, elw.ESL_LIB_STATUS_CONN_TIMEOUT]:
- if evt.sl_status == elw.SL_STATUS_ABORT and evt.node_id not in self.blocked_list: # handle advertisers bonded to different AP
- self.blocked_list.append(evt.node_id)
- self.log.warning("ESL at address %s has been blocked due to too many failed connection attempts", evt.node_id)
self.auto_override = False
self.set_mode_handlers()
- self.reorder_device_list(evt.node_id)
- self.check_address_list()
+ if evt.sl_status not in [elw.SL_STATUS_NO_MORE_RESOURCE, elw.SL_STATUS_BT_CTRL_CONNECTION_LIMIT_EXCEEDED]:
+ self.check_address_list()
def auto_esl_event_connection_opened(self, evt: esl_lib.EventConnectionOpened):
""" ESL event handler in auto mode """
+ tag = self.tag_db.find(evt.address)
+ if tag is not None and not tag.associated:
+ self.auto_configured_tags_in_single_run += 1
+
if self.auto_override:
self.log.warning("AUTO MODE TEMPORARILY CHANGED TO MANUAL!")
self.cmd_mode = self.auto_override
self.set_mode_handlers()
+ elif tag is not None and tag.provisioned: # we remain in auto mode, so aviod stuck connected in special case below
+ if tag.max_image_index is not None and tag.has_image_transfer and IMAGE_MAX_AUTO_UPLOAD_COUNT and tag.auto_image_count < min((tag.max_image_index + 1), IMAGE_MAX_AUTO_UPLOAD_COUNT):
+ self.upload_auto_image((tag.auto_image_count % len(self.image_files)), tag)
+ else:
+ self.ap_update_complete(tag.esl_id, tag.group_id)
def auto_esl_event_tag_info(self, evt: esl_lib.EventTagInfo):
""" ESL event handler in auto mode """
if not self.auto_override:
- tag = self.get_tag(evt.connection_handle)
- values = self.configure(tag)
- if self.state == CONNECTED:
- self.write_values(tag, values) # auto-refreshing the existing tag config may already doing this
+ tag = self.tag_db.find(evt.connection_handle)
+ if not tag.associated: # auto-refreshing of an existing tag config may happened already within the tag event handling
+ values = self.configure(tag)
+ self.write_values(tag, values)
def auto_esl_event_control_point_response(self, evt: esl_lib.EventControlPointResponse):
""" ESL event handler in auto mode """
if evt.status == elw.SL_STATUS_OK:
- esl_id, group_id = self.esl_addr_from_bt(self.connection_dict[evt.connection_handle])
- if not (evt.data_sent[0] == TLV_OPCODE_UPDATE_COMPLETE and evt.data_sent[1] == esl_id):
- self.ap_update_complete(esl_id, group_id)
+ tag = self.tag_db.find(evt.connection_handle)
+ if evt.data_sent[1] == tag.esl_id and not evt.data_sent[0] == TLV_OPCODE_UPDATE_COMPLETE:
+ self.ap_update_complete(tag.esl_id, tag.group_id)
def auto_esl_event_configure_tag_response(self, evt: esl_lib.EventConfigureTagResponse):
""" ESL event handler in auto mode """
if evt.status == elw.SL_STATUS_OK:
- tag = self.get_tag(evt.connection_handle)
+ tag = self.tag_db.find(evt.connection_handle)
if tag.provisioned:
if tag.max_image_index is not None and tag.has_image_transfer and IMAGE_MAX_AUTO_UPLOAD_COUNT and tag.auto_image_count < min((tag.max_image_index + 1), IMAGE_MAX_AUTO_UPLOAD_COUNT):
- self.upload_auto_image(tag.auto_image_count)
+ self.upload_auto_image((tag.auto_image_count % len(self.image_files)), tag)
else:
- esl_id, group_id = self.esl_addr_from_bt(self.connection_dict[evt.connection_handle])
- self.ap_update_complete(esl_id, group_id)
+ self.ap_update_complete(tag.esl_id, tag.group_id)
def auto_esl_event_image_transfer_finished(self, evt: esl_lib.EventImageTransferFinished):
""" ESL event handler in auto mode """
- self.upload_next_image(self.get_tag(evt.connection_handle))
+ self.upload_next_image(self.tag_db.find(evt.connection_handle))
def auto_esl_event_connection_closed(self, evt: esl_lib.EventConnectionClosed):
""" ESL event handler in auto mode """
if self.auto_override:
self.cmd_mode = False
self.auto_override = False
- self.reorder_device_list(evt.address)
self.check_address_list()
+ def auto_esl_event_bonding_finished(self, evt: esl_lib.EventBondingData):
+ """ ESL event handler in auto mode """
+ if not self.max_conn_count_reached:
+ self.check_address_list()
+
# ----------------------------------------------------------------------------------------------
# ESL event handler method extensions in demo mode
@@ -1490,11 +1374,7 @@ def demo_esl_event_system_boot(self, evt: esl_lib.EventSystemBoot):
""" ESL event handler in demo mode """
# Enable advertising
self.lib.general_command(CMD_AP_CONTROL_ADV_ENABLE, b'\x01')
- self.scan_runs = False
self.start_scan()
- self.pawr_active = False
- self.pawr_handle = None
- self.pawr_restart = None
self.start_pawr_train()
def demo_esl_event_general(self, evt: esl_lib.EventGeneral):
@@ -1554,7 +1434,7 @@ def demo_ap_control_image_transfer(self, data: bytes):
self.notify_controller(CCMD_REQUEST_DATA, CONTROLLER_COMMAND_SUCCESS, REQUEST_IMAGE_DATA_HEADER, self.image_data_offset, REQUEST_IMAGE_DATA_RESERVED)
elif data[0] == CONTROLLER_REQUEST_LAST_DATA:
# This was the last data, starting image update
- self.ap_imageupdate(self.controller_image_index, self.image_from_controller, from_console=False)
+ self.ap_imageupdate(self.controller_image_index, self.image_from_controller)
else:
self.log.error("Invalid data chunk arrived during image_update")
self.notify_controller(CCMD_IMAGE_UPDATE, CONTROLLER_COMMAND_FAIL)
@@ -1581,16 +1461,15 @@ def demo_esl_event_connection_opened(self, evt: esl_lib.EventConnectionOpened):
def demo_esl_event_tag_found(self, evt: esl_lib.EventTagFound):
""" ESL event handler in demo mode """
- if self.pawr_active and evt.address in self.tags and self.state == IDLE:
- self.connect(evt.address)
+ tag = self.tag_db.find(evt.address)
+ if self.pawr_active and tag is not None and tag.esl_state == EslState.UNSYNCHRONIZED and tag.state == TagState.IDLE:
+ self.connect(tag)
self.demo_auto_reconfigure = True
def demo_esl_event_configure_tag_response(self, evt: esl_lib.EventConfigureTagResponse):
""" ESL demo event handler """
- self.update_state(CONNECTED)
if evt.status == elw.SL_STATUS_OK:
- tag = self.get_tag(evt.connection_handle)
- tag.gatt_values[evt.type] = tag.gatt_write_values[evt.type]
+ tag = self.tag_db.find(evt.connection_handle)
if tag.provisioned and self.demo_auto_reconfigure:
self.ap_update_complete(tag.esl_id, tag.group_id)
@@ -1612,27 +1491,66 @@ def demo_esl_event_error(self, evt: esl_lib.EventError):
if self.controller_command == CCMD_REQUEST_DATA:
self.notify_controller(CCMD_IMAGE_UPDATE, CONTROLLER_COMMAND_FAIL)
elif evt.lib_status in [elw.ESL_LIB_STATUS_OTS_INIT_FAILED, elw.ESL_LIB_STATUS_OTS_META_READ_FAILED]:
- self.disconnect()
+ tag = self.tag_db.find(evt.node_id)
+ self.disconnect(tag)
+
+ # ----------------------------------------------------------------------------------------------
+ # AP Class methods
+
+ def revert_auto_mode(self):
+ if self.auto_override and len(self.tag_db.list_state((TagState.CONNECTED, TagState.CONNECTING))) == 0:
+ self.cmd_mode = False
+ self.auto_override = False
+ self.log.warning("REVERT TO AUTO MODE!")
+ self.set_mode_handlers()
def check_address_list(self):
""" Check address list """
- next_addr = list(set(self.ble_addresses) - set(self.blocked_list))
- self.log.info("Checking for next tag in list")
- if next_addr:
- if self.state == IDLE:
- self.log.debug(f"Address list: {str(next_addr)}")
- try:
- self.connect(next_addr[0])
- self.active_address = next_addr[0]
- except:
- self.update_state(IDLE)
- pass
+ if self.pawr_active and self.bonding_finished and len(self.tag_db.list_state(TagState.CONNECTING)) < ESL_CMD_MAX_PENDING_CONNECTION_REQUEST_COUNT:
+ self.log.info("Checking for next advertising ESL")
+ # Advertising IDLE state tags those are not blocked
+ tag_list = [tag for tag in self.tag_db.list_state(TagState.IDLE) if not tag.blocked and tag.advertising]
+ if len(tag_list) > 0:
+ tag = tag_list[0]
+ if not self.max_conn_count_reached:
+ self.bonding_finished = False
+ self.connect(tag)
+ if self.auto_config_start_time is None:
+ self.auto_config_start_time = dt.now()
+ else:
+ self.log.info("Access point connection limit reached - auto provisioning suspended!")
else:
- self.log.warning("Access point should be idle - auto provisioning may stop!")
- else:
- self.log.info("Advertising list is empty")
- if not self.scan_runs and not self.cmd_mode:
- self.log.warning("Scaning is disabled in auto mode.")
+ if len(self.tag_db.list_state((TagState.CONNECTING, TagState.CONNECTED))) == 0:
+ if not self.scan_runs:
+ self.log.error("Scanning is disabled, auto commissioning stopped until scanning is enabled!")
+ else:
+ self.log.warning("No advertising ESL found within RSSI threshold of %d dBm, auto commissioning suspended until further detection!", self.rssi_threshold)
+ log("Auto mode summary:", _half_indent_log=True)
+ time = None
+ try:
+ time = dt.now() - self.auto_config_start_time # self.auto_config_start_time can be None in an edge case
+ time_per_tag = time.total_seconds() / self.auto_configured_tags_in_single_run
+ log(f"Last auto config session for {self.auto_configured_tags_in_single_run} " \
+ f"{'tags' if self.auto_configured_tags_in_single_run > 1 else 'tag'} took " \
+ f"a total time of {str(time)[:-3]}, which is {time_per_tag:.3f} seconds per ESL.")
+ except:
+ # since self.auto_configured_tags_in_single_run will always count brand new configurations, only: it can be zero, resulting in div by 0 exception
+ if time is not None: # if self.auto_config_start_time was not None, then time must be valid, at least
+ log(f"Last auto re-config session took a total time of {str(time.total_seconds())[:-3]}.") # in this case we don't know how many tags has been re-configured
+ self.auto_config_start_time = None
+ self.auto_configured_tags_in_single_run = 0
+ self.ap_list(["synchronized","unsynchronized","blocked"])
+ if not self.scan_runs and not self.cmd_mode:
+ self.log.warning("Scanning is disabled in auto mode - please consider enabling it by issuing 'scan start' command!")
+ return
+ # Gather some useful stats until there are tags advertising
+ tag_list = self.tag_db.all()
+ synced_count = len(self.tag_db.list_esl_state(EslState.SYNCHRONIZED))
+ connected_count = len(self.tag_db.list_state(TagState.CONNECTED))
+ connecting_count = len(self.tag_db.list_state(TagState.CONNECTING))
+ blocked_count = len([tag for tag in tag_list if tag.blocked])
+ total_count = len(tag_list)
+ self.log.info("Multiconnection stats - TOTAL: %d, CONNECTING: %d, CONNECTED: %d, SYNCED: %d, BLOCKED: %d", total_count, connecting_count, connected_count, synced_count, blocked_count)
def get_pawr_params(self):
""" Get periodic advertisement current parameter set """
@@ -1657,14 +1575,11 @@ def set_pawr_interval(self, adv_interval=None, persistive=False):
if persistive:
self.adv_interval_min = adv_interval[0]
self.adv_interval_max = adv_interval_max
- self.pa_timer_interval = adv_interval_max / 1000.0
- # Reinitilazize past timer, because pa interval has changed
- self.reinit_past_timer()
+ self.pa_timer_interval = adv_interval_max / 800.0 # don't forget: the natural units of adv_interval values are 1.25[ms]!
def pawr_configure(self, adv_interval_min, adv_interval_max, subevent_count, subevent_interval, response_slot_delay, response_slot_spacing, response_slot_count):
"""
Set PAwR configuration, validate if possible
-
"""
self.adv_interval_min = adv_interval_min
self.adv_interval_max = adv_interval_max
@@ -1713,13 +1628,17 @@ def start_pawr_train(self, adv_interval=None):
def stop_pawr_train(self):
""" Stop periodic advertisement command """
if self.pawr_active:
- self.lib.pawr_enable(self.pawr_handle, False)
+ try:
+ self.lib.pawr_enable(self.pawr_handle, False)
+ except Exception as e:
+ self.log.error(e)
else:
self.log.info("PAwR train is not running.")
def update_pending_commands_list(self, gid, cmds):
""" Update unresponded command list """
last_slotnum = {}
+ previous_pending = []
# Get ESL response slot numbers
for i in range(len(cmds)):
esl_id = cmds[i][1]
@@ -1727,8 +1646,9 @@ def update_pending_commands_list(self, gid, cmds):
last_slotnum[esl_id] = i
self.esl_pending_commands_lock.acquire()
- if gid not in self.esl_pending_commands:
- self.esl_pending_commands[gid] = []
+ if gid in self.esl_pending_commands:
+ previous_pending = self.esl_pending_commands[gid]
+ self.esl_pending_commands[gid] = []
for i in range(len(cmds)):
esl_id = cmds[i][1]
@@ -1736,7 +1656,8 @@ def update_pending_commands_list(self, gid, cmds):
if esl_id != BROADCAST_ADDRESS:
new_command = ESLCommand(cmds[i], gid, last_slotnum[esl_id])
if new_command.response_opcode is not None:
- self.esl_pending_commands[gid].insert(0,new_command)
+ self.esl_pending_commands[gid].append(new_command)
+ self.esl_pending_commands[gid].extend(previous_pending)
self.esl_pending_commands_lock.release()
def remove_outdated_commands(self, limit):
@@ -1750,10 +1671,9 @@ def remove_outdated_commands(self, limit):
if (now - item.timestamp) > limit:
cmd = item
self.log.warning("Unresponded command (0x%s) in subevent %s slot %d removed from queue.", cmd.params.hex(), key, cmd.slot_number)
- address = self.bt_addr_from_esl(cmd.esl_id, cmd.group_id)
pop_list.append(self.esl_pending_commands[key].index(item))
- if address in self.tags:
- tag = self.tags[address]
+ tag = self.tag_db.find((cmd.esl_id, cmd.group_id))
+ if tag is not None:
if tag.unresp_command_number > 0:
tag.unresp_command_number -= 1
@@ -1774,7 +1694,11 @@ def remove_esl_pending_command(self, cmd):
gid = cmd.group_id
self.esl_pending_commands_lock.acquire()
if gid in self.esl_pending_commands:
- self.esl_pending_commands[gid].remove(cmd)
+ try:
+ self.esl_pending_commands[gid].remove(cmd)
+ except ValueError:
+ # nothing to delete, pass silently
+ pass
# delete any emptied dictionary
if len(self.esl_pending_commands[gid]) == 0:
@@ -1846,8 +1770,10 @@ def set_tlv_len(self, tlv, len):
def start_scan(self, active=False, clear_lists=False):
""" Start scanning """
if clear_lists:
- self.ble_addresses.clear()
- self.blocked_list.clear()
+ for tag in self.tag_db.list_advertising():
+ tag.reset_advertising()
+ for tag in self.tag_db.list_blocked():
+ tag.unblock()
if not self.scan_runs:
self.lib.scan_configure(active_mode=active)
self.lib.scan_enable()
@@ -1859,126 +1785,78 @@ def stop_scan(self):
""" Stop scanning """
if self.scan_runs:
self.lib.scan_enable(False)
- self.ble_addresses.clear()
else:
self.log.info("Scanning is not running.")
- def init_tag(self, address, gattdb_handles=None):
- """ Tag initialization """
- if address not in self.tags:
- self.tags[address] = Tag(address)
- self.tags[address].state = ST_CONFIGURING
- self.log.info("Registering ESL Tag at BLE address: " + str(address))
- if gattdb_handles is not None:
- self.tags[address].gattdb_handles = gattdb_handles
- self.tags[address].pending_unassociate = False
- if self.tags[address].provisioned:
- self.log.info("Already known Tag at BLE address: " + str(address))
- self.tags[address].state = ST_UPDATING
- cmd = self.get_latest_command(self.tags[address])
- if cmd is not None:
- self.log.debug("Unresponded command removed from PAwR queue!")
- self.remove_esl_pending_command(cmd)
-
def configure(self, tag: Tag):
""" Configure tag
parameters:
- tag: ESL Tag to configure """
+ tag: ESL tag to configure """
if tag.esl_address is None:
esl_address = self.new_auto_address(tag.id)
+ self.log.info("New auto ESL Address: 0x%04x", esl_address)
else:
esl_address = tag.esl_address
+ self.log.info("Reuse ESL Address: 0x%04x", esl_address)
- self.absolute_now = self.get_absolute_time()
+ absolute_time = self.get_absolute_time()
values = {
elw.ESL_LIB_DATA_TYPE_GATT_ESL_ADDRESS: esl_address.to_bytes(2, "little"),
- elw.ESL_LIB_DATA_TYPE_GATT_RESPONSE_KEY: self.generate_key_material(),
+ elw.ESL_LIB_DATA_TYPE_GATT_RESPONSE_KEY: self.ead.generate_key_material(),
elw.ESL_LIB_DATA_TYPE_GATT_AP_SYNC_KEY: self.ap_key,
- elw.ESL_LIB_DATA_TYPE_GATT_CURRENT_TIME: self.absolute_now.to_bytes(4, "little")
+ elw.ESL_LIB_DATA_TYPE_GATT_CURRENT_TIME: absolute_time.to_bytes(4, "little")
}
return values
- def connect(self, address, subevent=None):
+ def connect(self, tag: Tag):
""" Establish connection with a tag normally or via PAwR"""
- if subevent is not None:
- pawr = esl_lib.PAWRSubevent(self.pawr_handle, subevent)
+ # group ID shouldn't be None if the tag is in sychronized state, but better to doublecheck
+ if tag.esl_state == EslState.SYNCHRONIZED and tag.group_id is not None:
+ pawr = esl_lib.PAWRSubevent(self.pawr_handle, tag.group_id)
else:
pawr = None
- self.update_state(CONNECTING)
- ltk = self.key_db.find_ltk(address)
+ ltk = self.key_db.find_ltk(tag.ble_address)
if ltk is not None:
key_type = elw.ESL_LIB_KEY_TYPE_LTK
+ self.log.info("Bonding LTK found for ESL at %s", tag.ble_address)
else:
key_type = elw.ESL_LIB_KEY_TYPE_NO_KEY
- gattdb_handles = None
- if address in self.tags:
- gattdb_handles = self.tags[address].gattdb_handles
- self.log.info(f"Request connecting to: {address}")
- self.lib.connect(address, pawr, key_type=key_type, key=ltk, gattdb=gattdb_handles)
+ self.log.info("Request connecting to: %s %s", tag.ble_address, "over PAwR" if pawr else "via connectable advertisement")
+ try:
+ tag.connect(pawr, key_type=key_type, key=ltk)
+ except Exception as e:
+ self.log.error(e)
- def disconnect(self, address=None):
+ def disconnect(self, tag: Tag):
""" Close connection with a tag """
- self.past_initiated = False
-
- if address is None:
- address = self.active_address
try:
- self.lib.close_connection(self.conn_handle_from_bt(address))
+ tag.close_connection()
except esl_lib.CommandFailedError as err:
- self.log.warning(f"Failed to disconnect from {address}: {err}")
+ self.log.warning("Failed to disconnect from %s: %s", tag.ble_address, err)
- def past(self, address=None):
+ def past(self, tag: Tag):
""" Do Periodic Advertisement Sync Transfer over connection """
- if self.past_initiated or self.active_address not in self.tags:
+ if tag.past_initiated:
return
elif not self.pawr_active:
- self.disconnect(address)
+ if self.cmd_mode:
+ self.log.warning("PAwR is not running, tag won't be synchronized!")
+ else:
+ self.log.error("PAwR is not running, auto provisioning will stop until PAwR train is started by 'sync start' command!")
+ tag.close_connection()
return
+ tag.initiate_past(self.pawr_handle, self.pa_timer_interval)
- # TODO: Since the PAST may take very long time by definition, it would be necesssary to open more connections in parallel while we're waiting for the Tags to get synced. Otherwise, the system scalability will suffer.
- if address is None:
- address = self.active_address
- self.lib.initiate_past(self.conn_handle_from_bt(address), self.pawr_handle)
- self.past_initiated = True
- if not self.past_timer.is_alive():
- self.reinit_past_timer()
- self.past_timer.start()
-
- def past_timeout(self):
- """ Called on PAST timeout """
- self.log.warning("PAST timeout, force close!")
- self.disconnect()
-
- def reinit_past_timer(self):
- """ Reinitialize PAST timer """
- running = self.past_timer.is_alive()
- if running:
- self.past_timer.cancel()
- self.past_timer = threading.Timer(self.pa_timer_interval * AUX_SYNC_IND_PDU_MAX_SKIP_COUNT, self.past_timeout)
- self.past_timer.daemon = True
- if running:
- self.past_timer.start()
-
- def write_values(self, tag: Tag, values: dict):
+ def write_values(self, tag: Tag, values: dict, att_response=True):
""" Write values """
- tag.gatt_write_values.update(values)
- self.lib.configure_tag(self.conn_handle_from_bt(tag.ble_address), values, att_response=not IOP_TEST)
- self.update_state(WRITING_VALUES)
+ tag.configure_tag(values, att_response)
- def upload_auto_image(self, image_index):
+ def upload_auto_image(self, image_index, tag:Tag):
""" Automatic image update helper """
- if self.selected_image < (len(self.image_files) - 1):
- self.selected_image += 1
- else:
- self.selected_image = 0
image_path = self.image_path + random.choice(random.sample(self.image_files, len(self.image_files)))
- self.ap_imageupdate(image_index, image_path, False)
-
- def update_image(self, image_index):
- """ Image update """
- self.lib.write_image(self.conn_handle_from_bt(self.active_address), image_index, self.raw_image)
+ self.ap_imageupdate(image_index, image_path, address=tag.ble_address)
def shutdown(self):
""" Initiate shutdown """
@@ -1987,152 +1865,146 @@ def shutdown(self):
except esl_lib.CommandFailedError:
self.log.warning("Clean shutdown failed")
+ def shutdown_timeout(self):
+ """ Shutdown on ESL AP NCP init timeout """
+ self.log.critical("ESL AP NCP initialization timeout, no response received! Shutdown.")
+ self.shutdown_cli()
+
def shutdown_cli(self):
""" Propagate shutdown towards the CLI """
if self.cli_queue is not None:
self.cli_queue.put("exit")
- def get_tag(self, node_id):
- try:
- if isinstance(node_id, esl_lib.Address):
- return self.tags[node_id]
- elif isinstance(node_id, int): # Connection handle is assumed
- return self.tags[self.connection_dict[node_id]]
- except KeyError:
- pass
- return None
-
- def conn_handle_from_bt(self, bt_addr: esl_lib.Address):
- """ Find connection handle from BT address """
- conn_dict_inverse = {addr: conn for conn, addr in self.connection_dict.items()}
- try:
- return conn_dict_inverse[bt_addr]
- except KeyError:
- return None
-
- def esl_addr_from_bt(self, bt_addr: esl_lib.Address):
- """ Return with ESL ID and group ID from BT address """
- if bt_addr in self.tags:
- tag = self.tags[bt_addr]
- if tag.provisioned:
- return tag.esl_id, tag.group_id
- return None, None
-
- def is_tag_connected(self, esl_id, group_id):
- """ Returns true when a connection is open with the tag """
- address = self.bt_addr_from_esl(esl_id, group_id)
- return address is not None and address == self.active_address
-
- def bt_addr_from_esl(self, esl_id, group_id):
- """ Return BT address according to ESL ID and group ID """
- esl_addr = esl_id | (group_id << 8)
- if esl_id != BROADCAST_ADDRESS:
- for k in self.tags:
- if self.tags[k].esl_address == esl_addr:
- return self.tags[k].ble_address
- return None
-
- def esl_id_exist(self, esl_id, group_id):
- """ Check if ESL ID is exist """
- for tag in self.tags.values():
- if tag.esl_id == esl_id and tag.group_id == group_id:
- return True
- return False
-
- def is_there_tag(self, state):
- """ Check if there is any tag in state described by 'state' argument """
- for tag in self.tags.values():
- if tag.state == state:
- return True
- return False
-
- def set_tags_state(self, state_to, state_from=None):
- """ Set the state of the ESL tags to 'state_to' """
- for addr in self.tags:
- if state_from is not None:
- if self.tags[addr].state == state_from:
- self.tags[addr].state = state_to
- if state_to == ST_UNSYNCHRONIZED:
- self.tags[addr].update_flags(BASIC_STATE_FLAG_SYNCHRONIZED, False)
+ def get_active_tag(self, bt_addr: str=None, state=TagState.CONNECTED):
+ """ CLI helper to get 1 connected tag if bt_addr is omitted """
+ tag = None
+ if bt_addr is not None:
+ if re.fullmatch(VALID_BD_ADDRESS_REGEX, str(bt_addr)) is not None:
+ tag = self.tag_db.find(bt_addr)
+ if tag is None:
+ self.log.error("Unknown tag address: %s", bt_addr)
+ else:
+ self.log.error("Invalid tag address: %s", bt_addr)
+ else:
+ tags = self.tag_db.list_state(state)
+ if len(tags) == 0:
+ self.log.error("No connected tag present!")
+ elif len(tags) == 1:
+ tag = tags[0]
else:
- self.tags[addr].state = state_to
+ address_list = ", ".join([str(t.ble_address) for t in tags])
+ self.log.warning("%u connected tags present, select one: %s!", len(tags), address_list)
+ return tag
- def remove_tag(self, esl_id, group_id):
+ def remove_tag(self, esl_id=None, group_id=None, tag: Tag= None):
""" Remove tag from lists """
- bt_addr = self.bt_addr_from_esl(esl_id, group_id)
- if bt_addr is not None or esl_id == BROADCAST_ADDRESS:
- bt_addr_rem = [] # Elements to remove
- if bt_addr in self.tags:
- bt_addr_rem.append(bt_addr)
- if bt_addr in self.ble_addresses:
- self.ble_addresses.remove(bt_addr)
-
- # Remove all tag belongs to the group because of broadcast
+ tags_to_remove = []
+ if tag is not None:
+ tags_to_remove.append(tag)
+ elif esl_id is not None:
+ node_id = esl_id, group_id
if esl_id == BROADCAST_ADDRESS:
- for k in self.tags:
- eid, gid = self.esl_addr_from_bt(k)
- if gid == group_id and not self.is_tag_connected(eid, gid):
- bt_addr_rem.append(k)
- if k in self.ble_addresses:
- self.ble_addresses.remove(k)
- for addr in bt_addr_rem:
- self.tags.pop(addr, None)
- self.key_db.delete_ltk(addr)
-
- def get_esl_id(self, addr, group_id):
- """ Return ESL ID from either BT address or ESL ID string """
+ tags_to_remove = self.tag_db.list_group(group_id)
+ else:
+ tag = self.tag_db.find(node_id)
+ tags_to_remove.append(tag)
+ if len(tags_to_remove) == 0:
+ self.log.error("Unable to remove tag: no matching tag found")
+ return
+ for tag in tags_to_remove:
+ if not tag.blocked: # keep tags with blocked status - manual connection can override blocking, later!
+ self.log.info("Tag removed at address %s", tag.ble_address)
+ self.tag_db.remove(tag)
+ else:
+ self.log.info("Delete bonding for blocked tag at address %s", tag.ble_address)
+ self.key_db.delete_ltk(tag.ble_address)
+ tag.unassociate()
+
+
+ def get_esl_address(self, addr, group_id):
+ """ Return ESL ID from either BT address or ESL ID string - can return with BROADCAST_ADDRESS value (0xFF)! """
+ tag = None
esl_id = None
- # Check address type
- if re.fullmatch(VALID_BD_ADDRESS_REGEX, str(addr)) is not None:
- esl_id, _ = self.esl_addr_from_bt(esl_lib.Address.from_str(str(addr)))
- else:
- if addr == "all":
- esl_id = BROADCAST_ADDRESS
- elif addr is not None:
+ if group_id is None:
+ group_id = 0 # defaulting group 0 if none is given
+ if addr == "all" or addr == BROADCAST_ADDRESS:
+ esl_id = BROADCAST_ADDRESS # convert user friendly keyword to broadcast address
+ if len(self.tag_db.list_group(group_id)):
+ self.log.warning("All tags in group %d will be addressed!", group_id)
+ else:
+ self.log.warning("Group %d is empty, the command will have no effect!", group_id)
+ elif re.fullmatch(VALID_BD_ADDRESS_REGEX, str(addr)) is not None:
+ tag = self.tag_db.find(addr)
+ elif addr is not None:
+ try:
esl_id = int(addr)
- # Check if esl_id is out of range
- if (esl_id < 0) or (esl_id > BROADCAST_ADDRESS):
- esl_id = None
- # Warn the user if ESL ID not found
- if esl_id == BROADCAST_ADDRESS:
- self.log.warning("All Tags in group %d will be addressed!", group_id)
- elif esl_id is not None and not self.esl_id_exist(esl_id, group_id):
- esl_id = None
- self.log.warning("Tag address unknown: %s in group %d", addr, group_id)
- if self.controller_command != None:
- self.notify_controller(self.controller_command, CONTROLLER_COMMAND_FAIL)
- return esl_id
+ except TypeError:
+ self.log.error("%d is not a valid address, request ignored!", str(addr))
+ else:
+ tag = self.tag_db.find((esl_id, group_id))
+
+ if tag is None:
+ if esl_id is not None:
+ if esl_id != BROADCAST_ADDRESS:
+ self.log.warning("Tag address unknown: %s in group %d", addr, group_id)
+ else:
+ self.log.warning("Tag address unknown: %s", addr)
+ if self.controller_command != None:
+ self.notify_controller(self.controller_command, CONTROLLER_COMMAND_FAIL)
+ else:
+ esl_id = tag.esl_id
+ group_id = tag.group_id
+ return esl_id, group_id
def route_command(self, esl_id, group_id, data, force_pawr=False):
""" Auto route commands between periodic advertisement (with responses) and ESL Control Point """
- data = bytes(data)
- group = group_id
- if self.active_address in self.tags:
- _, group = self.esl_addr_from_bt(self.active_address)
- if self.is_tag_connected(esl_id, group_id) and not force_pawr:
- address = self.bt_addr_from_esl(esl_id, group_id)
- self.send_cp_command(address, data)
- elif IOP_TEST and self.active_address in self.tags and group_id == group\
- and ((esl_id != BROADCAST_ADDRESS and not self.esl_id_exist(esl_id, group_id)) \
- or (data[0] == TLV_OPCODE_PING)): # force sending commands with unknown ESL ID or PING to ALL (test-only command!) to ESL CP if connected for IOP test case
- self.log.warning("Sending a deliberately misaddressed command to the current ESL Control Point")
- address = self.tags[self.active_address]
- self.send_cp_command(address, data)
- else:
- if force_pawr and self.active_address in self.tags and group_id == group:
- self.log.warning("Command routing overrided, send via PAwR while connected!")
+ if esl_id == BROADCAST_ADDRESS:
+ connected_in_group = [tag for tag in self.tag_db.list_state((TagState.CONNECTED,TagState.CONNECTING)) if (group_id is None or tag.group_id == group_id)]
+ control_point_data = data.copy()
+ # Handle the connected ESL tag(s) within the given group explicitly, if any
+ for tag in connected_in_group:
+ # broadcast addresses are otherwise routed over periodic advertisement, always!
+ if not IOP_TEST: # following line could break future IOP BI test cases that require writing 0xFF ESL ID to ESL CP!
+ control_point_data[1] = tag.esl_id # ESL Service Spec v1.0 3.9.2 Command behavior, paragraph 3: broadcast address would be rejected by INVALID_PARAMETER error.
+ self.send_cp_command(tag, control_point_data)
+ if control_point_data[0] == TLV_OPCODE_UNASSOCIATE:
+ self.log.warning("Unassociate connected ESL tag at %s results in immediate disconnection!", tag.ble_address)
+ elif control_point_data[0] == TLV_OPCODE_FACTORY_RST:
+ self.log.warning("Factory reset of ESL tag at %s results in immediate disconnection!", tag.ble_address)
+
+ tag = self.tag_db.find((esl_id, group_id))
+ if tag is not None:
+ # check for different dispatch path override cases
+ if force_pawr:
+ self.log.warning("Override command dispatch path: send via PAwR")
+ tag = None
+ elif tag.state != TagState.CONNECTED:
+ # tag exists but is currently not connected then send via PAwR by default
+ if data[0] == TLV_OPCODE_PING:
+ tag.update_request_timestamp()
+ tag = None
+ elif IOP_TEST:
+ # force sending commands with unknown ESL ID or PING to ALL (IOP test-only command!) to ESL CP
+ active_tag = self.get_active_tag()
+ if active_tag is not None and len(active_tag) == 1 and active_tag.group_id == group_id \
+ and (esl_id != BROADCAST_ADDRESS or (data[0] == TLV_OPCODE_PING)):
+ self.log.warning("Sending a deliberately misaddressed command to the current ESL Control Point")
+ tag = active_tag
+
+ if tag is None:
self.queue_pawr_command(group_id, data)
+ else:
+ self.send_cp_command(tag, data)
- def send_cp_command(self, address: esl_lib.Address, data):
+ def send_cp_command(self, tag: Tag, data: bytes):
""" Writes a command to the control point """
- if address in self.tags:
- id, gid = self.esl_addr_from_bt(address)
- if data[0] == TLV_OPCODE_UNASSOCIATE and data[1] == id:
- self.tags[address].pending_unassociate = True
- self.lib.write_control_point(self.conn_handle_from_bt(address), data, att_response=not IOP_TEST)
+ try:
+ tag.write_control_point(data, att_response=not IOP_TEST)
+ except InvalidTagStateError:
+ self.log.error("ESL ID %d in group %d is not connected, Control Point can't be written!", tag.esl_id, tag.group_id)
def led_control_command(self, esl_id, group_id, repeat_field, led_idx,
- absolute_value, gamut = 0, pattern = None):
+ absolute_value, gamut=0, pattern=None):
""" Execute LED control command """
tlv = TLV_OPCODE_LED_CONTROL
if absolute_value is not None:
@@ -2157,25 +2029,17 @@ def led_control_command(self, esl_id, group_id, repeat_field, led_idx,
def identify_sender_tag(self, response_slot, subevent):
""" Identify sender Tag """
- tag_addr = None
if subevent in self.esl_pending_commands:
for cmd in self.esl_pending_commands[subevent]:
# Response is in expected slot and subevent
if cmd.slot_number == response_slot and cmd.group_id == subevent:
- address = self.bt_addr_from_esl(cmd.esl_id, cmd.group_id)
- if address in self.tags:
- tag_addr = address
- id, gid = self.esl_addr_from_bt(address)
- if id is not None and gid is not None:
- self.log.info("Reply from ESL ID %d, in group %d.", id, gid)
- else:
- self.log.error("Implausible tag identification results - address: %s, ESL ID: %s, group: %s", tag_addr, str(id), str(gid))
- break
- return tag_addr
-
- def handle_encrypted_pawr_response(self, data, response_slot, subevent):
- """ Handle one or more encrypted PAwR responses """
-
+ tag = self.tag_db.find((cmd.esl_id, cmd.group_id))
+ if tag is not None:
+ self.log.info("Reply in slot %d from ESL ID %d in group %d.", response_slot, tag.esl_id, tag.group_id)
+ else:
+ self.log.warning("Unable to identify sender tag ID %d in group %d", cmd.esl_id, cmd.group_id)
+ return tag
+ return None
def handle_pawr_response(self, data, response_slot, subevent):
""" Handle one or more PAwR responses """
@@ -2189,7 +2053,7 @@ def handle_pawr_response(self, data, response_slot, subevent):
return
pop_list = []
- address = ""
+ tag = None
while data_index < len(data):
data_size = self.get_opcode_len(data[data_index])
last_data = data_index + data_size
@@ -2199,24 +2063,22 @@ def handle_pawr_response(self, data, response_slot, subevent):
for cmd in self.esl_pending_commands[subevent]:
# Response is in expected slot and subevent
if cmd.slot_number == response_slot and cmd.group_id == subevent:
- address = self.bt_addr_from_esl(cmd.esl_id, cmd.group_id)
+ tag = self.tag_db.find((cmd.esl_id, cmd.group_id))
# Response belongs to known address and not already processed
- if address in self.tags and self.esl_pending_commands[subevent].index(cmd) not in pop_list:
+ if tag is not None and self.esl_pending_commands[subevent].index(cmd) not in pop_list:
# Accept responses only that allowed by the spec.
if cmd.opcode_valid(response_data[0]):
- self.tags[address].update_state(response_data)
- self.tags[address].state = ST_SYNCHRONIZED
+ tag.handle_response(response_data)
# Get sensor info
- sensor_info = self.tags[address].sensor_info
- self.tags[address].update_response_timestamp(dt.now().timestamp())
- if response_data[0] == TLV_RESPONSE_BASIC_STATE:
- if self.tags[address].pending_unassociate:
- esl_id, group_id = self.esl_addr_from_bt(address)
- self.remove_tag(esl_id, group_id)
+ sensor_info = tag.sensor_info
+ tag.update_response_timestamp(dt.now().timestamp())
+ if response_data[0] == TLV_RESPONSE_BASIC_STATE and tag.pending_unassociate:
+ tag.block(elw.ESL_LIB_STATUS_UNASSOCITED) # blocking before remove_tag() call will preserve tag object in memory but still clears LTK!
+ self.remove_tag(tag=tag)
# Resend retry error responses
if response_data[0] == TLV_RESPONSE_ERROR:
if response_data[1] == ERROR_RESPONSE_RETRY or response_data[1] == ERROR_RESPONSE_CAPACITY_LIMIT:
- self.log.info("Resending command: (0x%s)", cmd.params.hex())
+ self.log.info("Resending command: (0x%s) to ESL %d in group %d", cmd.params.hex(), tag.esl_id, tag.group_id)
self.queue_pawr_command(cmd.group_id, cmd.params)
# pop_list is for already processed answers - will skip those in any next round of this the for cycle
pop_list.append(self.esl_pending_commands[subevent].index(cmd))
@@ -2224,16 +2086,16 @@ def handle_pawr_response(self, data, response_slot, subevent):
break
else:
self.log.error("Received response can't be found in pending commands! (%s)", data.hex())
- break;
+ break
if last_data <= len(data):
- parse_response_data(response_data, sensor_info)
+ self.log.debug("Parsing PAwR response: %s in slot %d", response_data.hex(), response_slot)
+ ResponseParser(response_data, sensor_info)
if self.controller_command is not None:
- if address != "":
- esl_id, group_id = self.esl_addr_from_bt(address)
- self.notify_controller(self.controller_command, CONTROLLER_COMMAND_SUCCESS, esl_id, group_id, response_data)
+ if tag is not None:
+ self.notify_controller(self.controller_command, CONTROLLER_COMMAND_SUCCESS, tag.esl_id, tag.group_id, response_data)
else:
- self.log.warning("Broken PAwR response chunk: " + data[data_index:].hex())
+ self.log.warning("Broken PAwR response chunk: %s in slot %d", data[data_index:].hex(), response_slot)
data_index += data_size
cmd_pos += 1
diff --git a/app/bluetooth/example_host/bt_host_esl_ap/ap_ead.py b/app/bluetooth/example_host/bt_host_esl_ap/ap_ead.py
index 7ec0272514b..ac1e113274b 100644
--- a/app/bluetooth/example_host/bt_host_esl_ap/ap_ead.py
+++ b/app/bluetooth/example_host/bt_host_esl_ap/ap_ead.py
@@ -37,23 +37,43 @@
class KeyMaterial():
""" Encrypted Advertising Data Key Material """
def __init__(self, keymat):
- self.key = keymat[0:16][::-1] # Reverse byteorder to big-endian for the key
- self.iv = keymat[16:24]
-
+ self.key = None
+ self.iv = None
+ try:
+ self.key = keymat[0:16][::-1] # Reverse byteorder to big-endian for the key
+ self.iv = keymat[16:24]
+ self.log.debug("Preparing Key Material byte order for use.")
+ except TypeError as e:
+ self.log.critical(e)
+ # Logger
+ @property
+ def log(self):
+ return getLogger("KEY")
+
+ @property
+ def valid(self):
+ return self.key is not None and self.iv is not None
class EAD():
""" Encrypted Advertising Data handling class """
- def __init__(self):
- self.log = getLogger()
+ # Logger
+ @property
+ def log(self):
+ return getLogger("EAD")
def encrypt(self, data, key_material, random=None):
""" Encrypt PA data and assemble the ESL payload """
+ if data is None or key_material is None or not key_material.valid:
+ return None
+
ead_ad_type = EAD_AD_TYPE.to_bytes(1, byteorder='little')
esl_ad_type = ESL_AD_TYPE.to_bytes(1, byteorder='little')
+
if random is not None:
randomizer = random
else:
randomizer = secrets.token_bytes(EAD_RANDOMIZER_SIZE)
+
add_data = (ENCRYPTED_DATA_B1_HEADER).to_bytes(1, byteorder='little')
nonce = randomizer + key_material.iv
@@ -64,10 +84,14 @@ def encrypt(self, data, key_material, random=None):
length = len(ead_ad_type) + len(randomizer) + len(ad_data)
ret = length.to_bytes(1, byteorder='little') + ead_ad_type + randomizer + ad_data
+ self.log.debug("EAD encryption completed.")
return ret
def decrypt(self, data, key_material):
""" Decrypt encrypted PA data """
+ if data is None or key_material is None or not key_material.valid:
+ return None
+
ad_data = b""
add_data = (ENCRYPTED_DATA_B1_HEADER).to_bytes(1, byteorder='little')
randomizer, enc_data = self.unpack(data)
@@ -77,8 +101,9 @@ def decrypt(self, data, key_material):
aes_ccm = AESCCM(key_material.key, 4)
try:
ad_data = aes_ccm.decrypt(nonce, enc_data, add_data)
+ self.log.debug("EAD decryption succeeded.")
except:
- self.log.error("AEAD decryption failed.")
+ self.log.error("EAD decryption failed.")
return ad_data
@@ -96,4 +121,11 @@ def unpack(self, data):
def generate_key(self, bitlen=128):
""" Generate AES key """
+ self.log.debug("Generating AES-128 Key.")
return AESCCM.generate_key(bit_length=bitlen)
+
+ def generate_key_material(self):
+ """ Generate AP key """
+ keymat = self.generate_key() + secrets.token_bytes(EAD_IV_SIZE)
+ self.log.debug("Generating ESL Key Material from AES-128 Key.")
+ return keymat
\ No newline at end of file
diff --git a/app/bluetooth/example_host/bt_host_esl_ap/ap_logger.py b/app/bluetooth/example_host/bt_host_esl_ap/ap_logger.py
index 65578054956..d9483795303 100644
--- a/app/bluetooth/example_host/bt_host_esl_ap/ap_logger.py
+++ b/app/bluetooth/example_host/bt_host_esl_ap/ap_logger.py
@@ -32,7 +32,8 @@
except ImportError:
pass
-LEVELS = {'DEBUG': logging.DEBUG,
+LEVELS = {'NOTSET': logging.NOTSET,
+ 'DEBUG': logging.DEBUG,
'INFO' : logging.INFO,
'WARNING' : logging.WARNING,
'ERROR': logging.ERROR,
@@ -49,10 +50,10 @@
_logger_list = {}
-def log(*args, **kwargs):
+def log(*args, _half_indent_log :bool=False, **kwargs):
''' Print with 1 tab + 1 whitespace indentation '''
args = [arg.replace('\n', '\n\t ') if isinstance(arg, str) else arg for arg in args]
- print('\t', *args, file=sys.stdout if stdout else sys.stderr, **kwargs)
+ print('\t' if not _half_indent_log else 3*' ', *args, file=sys.stdout if stdout else sys.stderr, **kwargs)
class StreamHandler(logging.StreamHandler):
def __init__(self):
@@ -68,12 +69,29 @@ def __init__(self):
self.setLevel(logging.NOTSET)
def getLogger(name="AP "):
- if name in _logger_list:
- return _logger_list[name]
logger = logging.Logger(name)
c_handler = StreamHandler()
- logger.propagate = False
+ logger.propagate = True
logger.addHandler(c_handler)
logger.setLevel(level)
_logger_list[name] = logger
- return logger
+ return _logger_list[name]
+
+def setLogLevel(new_level: LEVELS):
+ global level
+ global _logger_list
+ level = new_level
+ if level != logging.NOTSET:
+ basic_level = logging.CRITICAL
+ else:
+ basic_level = level
+ logging.basicConfig(force=True, level=basic_level, datefmt='%d/%b %H:%M:%S', format='\r%(asctime)s.%(msecs)03d: %(name)-3.3s - %(levelname)-8s - %(message)s')
+ logging.disable(logging.NOTSET)
+ for logger in _logger_list:
+ logging.Logger(logger).setLevel(level)
+
+def logLevelName():
+ return logging.getLevelName(level)
+
+def logLevel():
+ return level
\ No newline at end of file
diff --git a/app/bluetooth/example_host/bt_host_esl_ap/ap_response_parser.py b/app/bluetooth/example_host/bt_host_esl_ap/ap_response_parser.py
index acd92a7a54d..108e31dda8c 100644
--- a/app/bluetooth/example_host/bt_host_esl_ap/ap_response_parser.py
+++ b/app/bluetooth/example_host/bt_host_esl_ap/ap_response_parser.py
@@ -25,67 +25,79 @@
# 3. This notice may not be removed or altered from any source distribution.
from ap_constants import *
-from ap_logger import getLogger
-from ap_sensor import process_sensor_data_response
+from ap_logger import getLogger,log
+from ap_sensor import SensorResponseParser
-logging = getLogger()
+class ResponseParser():
+ def __init__(self, data, sensor_info):
+ """ Parser class for ESL response
+ input:
+ - data: ESL response data
+ - sensor_info: ESL tag sensor information
+ """
+ if data[0] == TLV_RESPONSE_ERROR:
+ self.process_error_response(data)
+ elif data[0] == TLV_RESPONSE_LED_STATE:
+ self.process_led_state_response(data)
+ elif data[0] == TLV_RESPONSE_BASIC_STATE:
+ self.process_basic_state_response(data)
+ elif data[0] == TLV_RESPONSE_DISPLAY_STATE:
+ self.process_display_state_response(data)
+ elif data[0] == TLV_RESPONSE_SILABS_SKIP:
+ # Silabs vendor specific example
+ self.process_silabs_skip_response(data)
+ elif (data[0] & 0x0F) == TLV_RESPONSE_READ_SENSOR:
+ # Mask upper 4 bit because sensor response can have vendor-specific values
+ SensorResponseParser(data, sensor_info)
+ else:
+ self.log.warning("Unspecified response received" + " (0x" + str(data.hex()) + ")")
-def parse_response_data(data, sensor_info):
- """ Parse ESL response and sensor data
- input:
- - data: ESL response data
- - sensor_info: ESL tag sensor information
- """
- if data[0] == TLV_RESPONSE_ERROR:
- process_error_response(data)
- elif data[0] == TLV_RESPONSE_LED_STATE:
- process_led_state_response(data)
- elif data[0] == TLV_RESPONSE_BASIC_STATE:
- process_basic_state_response(data)
- elif data[0] == TLV_RESPONSE_DISPLAY_STATE:
- process_display_state_response(data)
- elif data[0] == TLV_RESPONSE_SILABS_SKIP:
- # Silabs vendor specific example
- process_silabs_skip_response(data)
- elif (data[0] & 0x0F) == TLV_RESPONSE_READ_SENSOR:
- # Mask upper 4 bit because sensor response can have vendorÂ-specific values
- process_sensor_data_response(data, sensor_info)
- else:
- logging.warning("Unspecified response received" + " (0x" + str(data.hex()) + ")")
+ # Logger
+ @property
+ def log(self):
+ return getLogger("RSP")
-def process_error_response(data):
- """ Process error response """
- resp_param = data[1]
- if resp_param in ERROR_RESPONSE_STRINGS:
- logging.warning(RESPONSE_STRINGS[TLV_RESPONSE_ERROR] + ": " \
- + ERROR_RESPONSE_STRINGS[resp_param] + " (0x" + str(data.hex()) + ")")
- else:
- logging.warning("Unspecified or vendor-specific error code" + " (0x" + str(data.hex()) + ")")
+ def process_error_response(self, data):
+ """ Process error response """
+ resp_param = data[1]
+ if resp_param in ERROR_RESPONSE_STRINGS:
+ if resp_param == ERROR_RESPONSE_RETRY:
+ self.log.warning(RESPONSE_STRINGS[TLV_RESPONSE_ERROR] + ": " \
+ + ERROR_RESPONSE_STRINGS[resp_param] + " (0x" + str(data.hex()) + ")")
+ else:
+ self.log.error(RESPONSE_STRINGS[TLV_RESPONSE_ERROR] + ": " \
+ + ERROR_RESPONSE_STRINGS[resp_param] + " (0x" + str(data.hex()) + ")")
+ else:
+ self.log.warning("Unspecified or vendor-specific error code" + " (0x" + str(data.hex()) + ")")
-def process_led_state_response(data):
- """ Process LED state response """
- logging.info(RESPONSE_STRINGS[TLV_RESPONSE_LED_STATE] + ": LED_Index: " \
- + str(data[1]) + " (0x" + str(data.hex()) + ")")
+ def process_led_state_response(self, data):
+ """ Process LED state response """
+ self.log.info("Led State Response received")
+ log(RESPONSE_STRINGS[TLV_RESPONSE_LED_STATE] + ": LED_Index: " \
+ + str(data[1]) + " (0x" + str(data.hex()) + ")")
-def process_basic_state_response(data):
- """ Process basic state response """
- bs_bitmap = int.from_bytes(data[1:], byteorder="little")
- bs_string = ", ".join([value for key, value in BASIC_STATE_STRINGS.items() if bs_bitmap & key])
- if len(bs_string) == 0:
- bs_string = "No Basic State flag is set"
- logging.info(RESPONSE_STRINGS[TLV_RESPONSE_BASIC_STATE] + ": " + bs_string \
- + " (0x" + str(data.hex()) + ")")
+ def process_basic_state_response(self, data):
+ """ Process basic state response """
+ self.log.info("Basic State Response received")
+ bs_bitmap = int.from_bytes(data[1:], byteorder="little")
+ bs_string = ", ".join([value for key, value in BASIC_STATE_STRINGS.items() if bs_bitmap & key])
+ if len(bs_string) == 0:
+ bs_string = "No Basic State flag is set"
+ log(RESPONSE_STRINGS[TLV_RESPONSE_BASIC_STATE] + ": " + bs_string \
+ + " (0x" + str(data.hex()) + ")")
-def process_display_state_response(data):
- """ Process display state response """
- display_idx = data[1]
- image_idx = data[2]
- logging.info(RESPONSE_STRINGS[TLV_RESPONSE_DISPLAY_STATE] + ": Display_Index: " \
- + str(display_idx) + " Image_Index: " + str(image_idx) \
- + " (0x" + str(data.hex()) + ")")
+ def process_display_state_response(self, data):
+ """ Process display state response """
+ self.log.info("Display State Response received")
+ display_idx = data[1]
+ image_idx = data[2]
+ log(RESPONSE_STRINGS[TLV_RESPONSE_DISPLAY_STATE] + ": Display_Index: " \
+ + str(display_idx) + " Image_Index: " + str(image_idx) \
+ + " (0x" + str(data.hex()) + ")")
-def process_silabs_skip_response(data):
- """ Process display state response """
- skip_value = data[1]
- logging.info(RESPONSE_STRINGS[TLV_RESPONSE_SILABS_SKIP] + ": " \
- + str(skip_value))
+ def process_silabs_skip_response(self, data):
+ """ Process Silabs Skip (vendor opcode) response """
+ self.log.info("Silabs PAwR Skip Response received")
+ skip_value = data[1]
+ log(RESPONSE_STRINGS[TLV_RESPONSE_SILABS_SKIP] + ": " \
+ + str(skip_value))
diff --git a/app/bluetooth/example_host/bt_host_esl_ap/ap_sensor.py b/app/bluetooth/example_host/bt_host_esl_ap/ap_sensor.py
index 3589a1169a6..6f007265314 100644
--- a/app/bluetooth/example_host/bt_host_esl_ap/ap_sensor.py
+++ b/app/bluetooth/example_host/bt_host_esl_ap/ap_sensor.py
@@ -43,6 +43,39 @@
S_ID_SILABS_SENSOR_1 = 0xCAFE02FF
S_ID_SILABS_SENSOR_BUTTON = 0xC0DE02FF
+class SensorResponseParser():
+ def __init__(self, data: bytes, sensor_info):
+ """ Process and display ESL sensor data
+ input:
+ - data: Event data contains sensor information
+ - sensor_info: ESL tag sensor information
+ """
+ resp_code = data[0] & 0x0F
+ resp_length = data[0] & 0xF0
+
+ if resp_code == TLV_RESPONSE_READ_SENSOR:
+ self.log.info("Sensor data received: 0x" + data.hex())
+ if sensor_info is None:
+ self.log.info("Unknown sensor - raw response data: 0x" + data.hex())
+ elif data[1] < len(sensor_info):
+ sensor_type = sensor_info[data[1]]
+ if resp_length > 1:
+ if sensor_type in SENSOR_TYPES:
+ log("Sensor type: " + SENSOR_TYPES[sensor_type].desc)
+ value = SENSOR_TYPES[sensor_type].from_bytes(data[2:])
+ log(" " + str(value))
+ else:
+ log(f"Sensor type {sensor_type} not supported")
+ else:
+ self.log.info("No sensor data")
+ else:
+ self.log.info("Invalid sensor type")
+
+ # Logger
+ @property
+ def log(self):
+ return getLogger("SEN")
+
def sensor_type_voltage(data):
""" Interpret sensor value based on GATT Specification Supplement:
Type: Voltage | Chapter: 3.236
@@ -165,31 +198,3 @@ def sensor_type_silabs_button(data):
S_ID_SILABS_SENSOR_BUTTON: SensorType(sensor_type_silabs_button,
"Silabs button")
}
-
-def process_sensor_data_response(data: bytes, sensor_info):
- """ Process and display ESL sensor data
- input:
- - data: Event data contains sensor information
- - sensor_info: ESL tag sensor information
- """
- resp_code = data[0] & 0x0F
- resp_length = data[0] & 0xF0
- logging = getLogger()
-
- if resp_code == TLV_RESPONSE_READ_SENSOR:
- logging.info("Sensor data received: " + data.hex())
- if sensor_info is None:
- logging.info("Unknown sensor - raw response data: " + data.hex())
- elif data[1] < len(sensor_info):
- sensor_type = sensor_info[data[1]]
- if resp_length > 1:
- if sensor_type in SENSOR_TYPES:
- log("Sensor type: " + SENSOR_TYPES[sensor_type].desc)
- value = SENSOR_TYPES[sensor_type].from_bytes(data[2:])
- log(" " + str(value))
- else:
- log(f"Sensor type {sensor_type} not supported")
- else:
- logging.info("No sensor data")
- else:
- logging.info("Invalid sensor type")
diff --git a/app/bluetooth/example_host/bt_host_esl_ap/app.py b/app/bluetooth/example_host/bt_host_esl_ap/app.py
index 1992e267eff..2ae4b394e0a 100755
--- a/app/bluetooth/example_host/bt_host_esl_ap/app.py
+++ b/app/bluetooth/example_host/bt_host_esl_ap/app.py
@@ -72,26 +72,32 @@ def get_ap_config(conn):
return f"-connection serial -device {conn}"
def main():
+ if sys.version_info[0] < 3 or (sys.version_info[0] == 3 and sys.version_info[1] < 8):
+ raise Exception('Requires at least python 3.8.0')
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('conn', nargs='?', help='Serial or TCP connection parameter')
- parser.add_argument('--cmd', help='Command line mode', action='store_true')
- parser.add_argument('--demo', help='Demo mode', action='store_true')
- parser.add_argument('--stdout', help='Change output to stdout', action='store_true')
- parser.add_argument('-l', '--log', help='Log level', type=str.upper, choices=ap_logger.LEVELS.keys(), default='INFO')
+ parser.add_argument('-m', '--cmd', help='Start in command line (manual) mode instead of default ESL Profile (auto) mode', action='store_true')
+ parser.add_argument('-d', '--demo', help='Start in manual mode with EFRConnect demo mode enabled', action='store_true')
+ parser.add_argument('-r', '--stdout', help='Redirect logging output from default stderr to stdout', action='store_true')
+ parser.add_argument('-u', '--unsecure', help='Disable encryption for NCP communication', action='store_true')
+ parser.add_argument('-l', '--log', help='Logging level to start with - can be changed later with verbosity command', type=str.upper, choices=ap_logger.LEVELS.keys(), default='INFO')
args = parser.parse_args()
ap_logger.stdout = args.stdout
- ap_logger.level = ap_logger.LEVELS[args.log]
+ ap_logger.setLogLevel(ap_logger.LEVELS[args.log])
# Instantiate the ESL application
- access_point = AccessPoint(get_ap_config(args.conn), args.cmd, args.demo)
+ access_point = AccessPoint(get_ap_config(args.conn), args.unsecure, args.cmd, args.demo)
cli_processor = CliProcessor(access_point)
access_point.cli_queue = cli_processor.queue
try:
cli_processor.loop()
except KeyboardInterrupt:
pass
- access_point.shutdown()
+ try:
+ access_point.shutdown()
+ except KeyboardInterrupt:
+ pass # avoid crash in case of slow shutdown meets impatient user :)
# Script entry point.
if __name__ == "__main__":
diff --git a/app/bluetooth/example_host/bt_host_esl_ap/esl_command.py b/app/bluetooth/example_host/bt_host_esl_ap/esl_command.py
index aaf2544d617..21c5372be81 100644
--- a/app/bluetooth/example_host/bt_host_esl_ap/esl_command.py
+++ b/app/bluetooth/example_host/bt_host_esl_ap/esl_command.py
@@ -39,7 +39,11 @@ def __init__(self, params, group_id, slot_num):
self.params = params
self.response_opcode = []
self.calculate_expected_response()
- self.logger = getLogger()
+
+ # Logger
+ @property
+ def log(self):
+ return getLogger("CMD")
def calculate_expected_response(self):
""" Calculate possible response opcodes """
@@ -81,9 +85,10 @@ def opcode_valid(self, opcode):
elif (opcode & 0x0F) == TLV_OPCODE_VENDOR_SPECIFIC:
valid = True
elif opcode in self.response_opcode:
+ self.log.debug("Expected response received: 0x%02x", opcode)
valid = True
if not valid:
- self.logger.warning("Unexpected response received : 0x%02x!", opcode)
+ self.log.warning("Unexpected response received: %d - expected: %s!", opcode, self.response_opcode)
return valid
def __str__(self):
@@ -93,6 +98,6 @@ def __str__(self):
ret += f"esl_id: {self.esl_id}\n"
ret += f"group_id: {self.group_id}\n"
ret += f"slot_number: {self.slot_number}\n"
- ret += f"timestamp: {self.timestamp}\n"
- ret += f"params: {self.params}"
+ ret += f"timestamp: {dt.fromtimestamp(self.timestamp).strftime('%d/%b %H:%M:%S.%f')[:-3]}\n"
+ ret += f"params: {self.params.hex()}"
return ret
diff --git a/app/bluetooth/example_host/bt_host_esl_ap/esl_lib.py b/app/bluetooth/example_host/bt_host_esl_ap/esl_lib.py
index 5cd3ef3cc77..d2fa655c24c 100644
--- a/app/bluetooth/example_host/bt_host_esl_ap/esl_lib.py
+++ b/app/bluetooth/example_host/bt_host_esl_ap/esl_lib.py
@@ -33,6 +33,7 @@
import threading
import ap_logger
import esl_lib_wrapper as elw
+from ap_constants import ADDRESS_TYPE_PUBLIC_ADDRESS
SCAN_PHY_1M = 1
SCAN_PHY_CODED = 4
@@ -94,6 +95,7 @@ def event_factory(evt_code: elw.esl_lib_evt_type_t, evt_data: elw.esl_lib_evt_da
EventConnectionClosed,
EventConnectionOpened,
EventBondingData,
+ EventBondingFinished,
EventPawrStatus,
EventPawrResponse,
EventPawrDataRequest,
@@ -114,8 +116,7 @@ def get_node_id(node_id: elw.esl_lib_node_id_t):
if node_id.type == elw.ESL_LIB_NODE_ID_TYPE_ADDRESS:
return Address.from_ctype(node_id.id.address)
if node_id.type == elw.ESL_LIB_NODE_ID_TYPE_CONNECTION:
- # Store pointer as an integer
- return int(node_id.id.connection_handle)
+ return ConnectionHandle(node_id.id.connection_handle)
if node_id.type == elw.ESL_LIB_NODE_ID_TYPE_PAWR:
return PAWRSubevent.from_ctype(node_id.id.pawr)
return None
@@ -128,39 +129,44 @@ def __init__(self, address: bytes, address_type=None):
self.addr = address
if address_type is None:
# use public device address type per default
- self.addr_type = 0
+ self.address_type = ADDRESS_TYPE_PUBLIC_ADDRESS
else:
- self.addr_type = int(address_type)
+ self.address_type = int(address_type)
def __repr__(self) -> str:
- return ':'.join([f'{b:02X}' for b in iter(reversed(self.addr))])
+ return ':'.join([f'{b:02X}' for b in iter(reversed(self.addr))]) + f', type {self.address_type}'
def __eq__(self, other):
if other is None:
return False
if isinstance(other, Address):
- return (self.addr == other.addr) and (self.addr_type == other.addr_type)
+ return (self.addr == other.addr) and (self.address_type == other.address_type)
if len(other) >= 12:
val_b = bytes.fromhex(other.replace(':', ''))[::-1]
return val_b == self.addr
return other == self.addr
def __hash__(self):
- return hash((self.addr, self.addr_type))
+ return hash((self.addr, self.address_type))
@classmethod
def from_str(cls, address: str, address_type=None):
'''Create new address instance from string object'''
- return cls(bytes.fromhex(address.replace(':', ''))[::-1], address_type)
+ return cls(bytes.fromhex(address.replace(':', ''))[::-1], ADDRESS_TYPE_PUBLIC_ADDRESS if address_type is None else int(address_type))
@classmethod
def from_ctype(cls, address: elw.esl_lib_address_t):
'''Create new address instance from ctype object'''
- return cls(bytes(address.addr), int(address.addr_type))
+ return cls(bytes(address.addr), int(address.address_type))
def to_ctype(self) -> elw.esl_lib_address_t:
'''Convert to ctype object'''
- return elw.esl_lib_address_t((ctypes.c_ubyte * 6).from_buffer_copy(self.addr), self.addr_type)
+ return elw.esl_lib_address_t(elw.struct_esl_lib_address_s._fields_[0][1].from_buffer_copy(self.addr), self.address_type)
+
+class ConnectionHandle(int):
+ '''Connection handle representation'''
+ def __repr__(self):
+ return hex(self)
class PAWRSubevent():
'''Wrapper for esl_lib_pawr_subevent_t'''
@@ -169,7 +175,7 @@ def __init__(self, handle: int, subevent: int):
self.subevent = subevent
def __repr__(self) -> str:
- return f'PAWR {self.handle} subevent {self.subevent}'
+ return f'PAWR 0x{self.handle:08x} subevent {self.subevent}'
@classmethod
def from_ctype(cls, pawr: elw.esl_lib_pawr_subevent_t):
@@ -226,7 +232,7 @@ class EventTagInfo():
evt_code = EventType(elw.ESL_LIB_EVT_TAG_INFO)
def __init__(self, evt_data: elw.esl_lib_evt_data_t):
- self.connection_handle = evt_data.evt_tag_info.connection_handle
+ self.connection_handle = ConnectionHandle(evt_data.evt_tag_info.connection_handle)
tlv_data = long_array_to_bytes(evt_data.evt_tag_info.tlv_data)
self.tlv_data = {}
tlv_position = 0
@@ -239,108 +245,119 @@ def __init__(self, evt_data: elw.esl_lib_evt_data_t):
def __repr__(self) -> str:
tlv_data_str = ', '.join([f'{get_enum("ESL_LIB_DATA_TYPE_", key)}: {value.hex()}' for key, value in self.tlv_data.items()])
- return f'{self.evt_code}, {self.connection_handle:#x}, {tlv_data_str}'
+ return f'{self.evt_code}, {self.connection_handle}, {tlv_data_str}'
class EventConfigureTagResponse():
'''Wrapper for esl_lib_evt_configure_tag_response_t'''
evt_code = EventType(elw.ESL_LIB_EVT_CONFIGURE_TAG_RESPONSE)
def __init__(self, evt_data: elw.esl_lib_evt_data_t):
- self.connection_handle = evt_data.evt_configure_tag_response.connection_handle
+ self.connection_handle = ConnectionHandle(evt_data.evt_configure_tag_response.connection_handle)
self.type = evt_data.evt_configure_tag_response.type
self.status = evt_data.evt_configure_tag_response.status
def __repr__(self) -> str:
type_str = get_enum('ESL_LIB_DATA_TYPE_', self.type)
status_str = get_enum('SL_STATUS_', self.status)
- return f'{self.evt_code}, {self.connection_handle:#x}, {type_str}, {status_str}'
+ return f'{self.evt_code}, {self.connection_handle}, {type_str}, {status_str}'
class EventControlPointResponse():
'''Wrapper for esl_lib_evt_control_point_response_t'''
evt_code = EventType(elw.ESL_LIB_EVT_CONTROL_POINT_RESPONSE)
def __init__(self, evt_data: elw.esl_lib_evt_data_t):
- self.connection_handle = evt_data.evt_control_point_response.connection_handle
+ self.connection_handle = ConnectionHandle(evt_data.evt_control_point_response.connection_handle)
self.status = evt_data.evt_control_point_response.status
self.data_sent = array_to_bytes(evt_data.evt_control_point_response.data_sent)
def __repr__(self) -> str:
status_str = get_enum('SL_STATUS_', self.status)
- return f'{self.evt_code}, {self.connection_handle:#x}, {status_str}, {self.data_sent.hex()}'
+ return f'{self.evt_code}, {self.connection_handle}, {status_str}, {self.data_sent.hex()}'
class EventControlPointNotification():
'''Wrapper for esl_lib_evt_control_point_notification_t'''
evt_code = EventType(elw.ESL_LIB_EVT_CONTROL_POINT_NOTIFICATION)
def __init__(self, evt_data: elw.esl_lib_evt_data_t):
- self.connection_handle = evt_data.evt_control_point_notification.connection_handle
+ self.connection_handle = ConnectionHandle(evt_data.evt_control_point_notification.connection_handle)
self.data = array_to_bytes(evt_data.evt_control_point_notification.data)
def __repr__(self) -> str:
- return f'{self.evt_code}, {self.connection_handle:#x}, {self.data.hex()}'
+ return f'{self.evt_code}, {self.connection_handle}, {self.data.hex()}'
class EventConnectionOpened():
'''Wrapper for esl_lib_evt_connection_opened_t'''
evt_code = EventType(elw.ESL_LIB_EVT_CONNECTION_OPENED)
def __init__(self, evt_data: elw.esl_lib_evt_data_t):
- self.connection_handle = evt_data.evt_connection_opened.connection_handle
+ self.connection_handle = ConnectionHandle(evt_data.evt_connection_opened.connection_handle)
self.address = Address.from_ctype(evt_data.evt_connection_opened.address)
self.gattdb_handles = elw.esl_lib_gattdb_handles_t.from_buffer_copy(evt_data.evt_connection_opened.gattdb_handles)
def __repr__(self) -> str:
gattdb_str = f'[{self.gattdb_handles.services.esl}, {self.gattdb_handles.services.ots}, {self.gattdb_handles.services.dis}]'
- return f'{self.evt_code}, {self.connection_handle:#x}, {self.address}, {gattdb_str}'
+ return f'{self.evt_code}, {self.connection_handle}, {self.address}, {gattdb_str}'
class EventConnectionClosed():
'''Wrapper for esl_lib_evt_connection_closed_t'''
evt_code = EventType(elw.ESL_LIB_EVT_CONNECTION_CLOSED)
def __init__(self, evt_data: elw.esl_lib_evt_data_t):
- self.connection_handle = evt_data.evt_connection_closed.connection_handle
+ self.connection_handle = ConnectionHandle(evt_data.evt_connection_closed.connection_handle)
self.address = Address.from_ctype(evt_data.evt_connection_closed.address)
self.reason = evt_data.evt_connection_closed.reason
def __repr__(self) -> str:
reason_str = get_enum('SL_STATUS_', self.reason)
- return f'{self.evt_code}, {self.connection_handle:#x}, {self.address}, {reason_str}'
+ return f'{self.evt_code}, {self.connection_handle}, {self.address}, {reason_str}'
class EventBondingData():
'''Wrapper for esl_lib_evt_bonding_data_t'''
evt_code = EventType(elw.ESL_LIB_EVT_BONDING_DATA)
def __init__(self, evt_data: elw.esl_lib_evt_data_t):
- self.connection_handle = evt_data.evt_bonding_data.connection_handle
+ self.connection_handle = ConnectionHandle(evt_data.evt_bonding_data.connection_handle)
self.address = Address.from_ctype(evt_data.evt_bonding_data.address)
self.ltk = bytes(evt_data.evt_bonding_data.ltk)
def __repr__(self) -> str:
- return f'{self.evt_code}, {self.connection_handle:#x}, {self.address}, {self.ltk.hex()}'
+ return f'{self.evt_code}, {self.connection_handle}, {self.address}, {self.ltk.hex()}'
+
+class EventBondingFinished():
+ '''Wrapper for esl_lib_evt_bonding_finished_t'''
+ evt_code = EventType(elw.ESL_LIB_EVT_BONDING_FINISHED)
+
+ def __init__(self, evt_data: elw.esl_lib_evt_data_t):
+ self.connection_handle = ConnectionHandle(evt_data.evt_bonding_finished.connection_handle)
+ self.address = Address.from_ctype(evt_data.evt_bonding_finished.address)
+
+ def __repr__(self) -> str:
+ return f'{self.evt_code}, {self.connection_handle}, {self.address}'
class EventImageTransferFinished():
'''Wrapper for esl_lib_evt_image_transfer_finished_t'''
evt_code = EventType(elw.ESL_LIB_EVT_IMAGE_TRANSFER_FINISHED)
def __init__(self, evt_data: elw.esl_lib_evt_data_t):
- self.connection_handle = evt_data.evt_image_transfer_finished.connection_handle
+ self.connection_handle = ConnectionHandle(evt_data.evt_image_transfer_finished.connection_handle)
self.img_index = evt_data.evt_image_transfer_finished.img_index
self.status = evt_data.evt_image_transfer_finished.status
def __repr__(self) -> str:
status_str = get_enum('SL_STATUS_', self.status)
- return f'{self.evt_code}, {self.connection_handle:#x}, {self.img_index}, {status_str}'
+ return f'{self.evt_code}, {self.connection_handle}, {self.img_index}, {status_str}'
class EventImageType():
'''Wrapper for esl_lib_evt_image_type_t'''
evt_code = EventType(elw.ESL_LIB_EVT_IMAGE_TYPE)
def __init__(self, evt_data: elw.esl_lib_evt_data_t):
- self.connection_handle = evt_data.evt_image_type.connection_handle
+ self.connection_handle = ConnectionHandle(evt_data.evt_image_type.connection_handle)
self.img_index = evt_data.evt_image_type.img_index
self.type_data = long_array_to_bytes(evt_data.evt_image_type.type_data)
def __repr__(self) -> str:
- return f'{self.evt_code}, {self.connection_handle:#x}, {self.img_index}, {self.type_data.hex()}'
+ return f'{self.evt_code}, {self.connection_handle}, {self.img_index}, {self.type_data.hex()}'
class EventPawrStatus():
'''Wrapper for esl_lib_evt_pawr_status_t'''
@@ -394,7 +411,7 @@ def __repr__(self) -> str:
lib_status_str = get_enum('ESL_LIB_STATUS_', self.lib_status)
sl_status_str = get_enum('SL_STATUS_', self.sl_status)
try:
- if isinstance(self.node_id, int):
+ if isinstance(self.node_id, ConnectionHandle) or isinstance(self.node_id, (Address)):
# Connection handle node ID type
data_str = get_enum('ESL_LIB_CONNECTION_STATE_', self.data)
elif isinstance(self.node_id, PAWRSubevent):
@@ -439,27 +456,33 @@ def _process_run(self, conn, stdout, level):
'''Main method of the ESL lib process'''
ap_logger.stdout = stdout
ap_logger.level = level
- logger = ap_logger.getLogger('LIB')
- logger.debug('started pid: %u', os.getpid())
+ self.log.debug('started pid: %u', os.getpid())
threading.Thread(target=self._deserialize_command, daemon=True, args=(conn,)).start()
# Instantiate callback function pointer
on_event_func = elw.esl_lib_on_event_t(self._on_event)
def log(level:int, module:str, log:str, file:str, line:int, function:str):
"""Logging callback for ESL lib instance"""
+ filter_events = []
LOG_LEVEL_DICT = {
- elw.ESL_LIB_LOG_LEVEL_DEBUG: logger.debug,
- elw.ESL_LIB_LOG_LEVEL_INFO: logger.info,
- elw.ESL_LIB_LOG_LEVEL_WARNING: logger.warning,
- elw.ESL_LIB_LOG_LEVEL_ERROR: logger.error,
- elw.ESL_LIB_LOG_LEVEL_CRITICAL: logger.critical,
+ elw.ESL_LIB_LOG_LEVEL_DEBUG: self.log.debug,
+ elw.ESL_LIB_LOG_LEVEL_INFO: self.log.info,
+ elw.ESL_LIB_LOG_LEVEL_WARNING: self.log.warning,
+ elw.ESL_LIB_LOG_LEVEL_ERROR: self.log.error,
+ elw.ESL_LIB_LOG_LEVEL_CRITICAL: self.log.critical,
}
- filter_events = [b"PAwR response, data status = 255", b"Tag found"]
+ if ap_logger.logLevel() > ap_logger.LEVELS["NOTSET"]: # Note: The lowest log level becomes extra verbose, completely flooding the CLI!
+ filter_events = [b"PAwR response, data status = 255", b"Tag found", b"characteristic", b"tag info"]
if not any(flt in log for flt in filter_events):
- LOG_LEVEL_DICT[level](f'[{module}] {log.rstrip()} @ {file}:{line} in {function}')
+ LOG_LEVEL_DICT[level]("[%s] %s in %s() @ %d:%s ", module, log.rstrip(), function, line, file)
+
log_func = elw.esl_lib_log_callback_t(log)
elw.esl_lib_start(self.config, on_event_func, log_func)
- logger.debug('terminated pid: %u', os.getpid())
+ self.log.debug('terminated pid: %u', os.getpid())
+
+ @property
+ def log(self):
+ return ap_logger.getLogger('LIB')
def _deserialize_command(self, conn: Connection):
'''Deserialize command and serialize result'''
@@ -645,14 +668,14 @@ def _pawr_enable(self, pawr_handle, enable: bool):
status = elw.esl_lib_pawr_enable(pawr_handle, enable)
return (status, )
- def pawr_set_data(self, pawr_handle, subevent: int, payload: bytes):
+ def pawr_set_data(self, pawr_handle, subevent: int, response_slot_max: int, payload: bytes):
'''Public wrapper for esl_lib_pawr_set_data'''
- self._serialize_command('_pawr_set_data', (pawr_handle, subevent, payload))
+ self._serialize_command('_pawr_set_data', (pawr_handle, subevent, response_slot_max, payload))
- def _pawr_set_data(self, pawr_handle, subevent: int, payload: bytes):
+ def _pawr_set_data(self, pawr_handle, subevent: int, response_slot_max: int, payload: bytes):
'''Internal wrapper for esl_lib_pawr_set_data'''
c_payload = bytes_to_array(payload)
- status = elw.esl_lib_pawr_set_data(pawr_handle, subevent, array_p(c_payload))
+ status = elw.esl_lib_pawr_set_data(pawr_handle, subevent, response_slot_max, array_p(c_payload))
return (status, )
def pawr_configure(self,
diff --git a/app/bluetooth/example_host/bt_host_esl_ap/esl_tag.py b/app/bluetooth/example_host/bt_host_esl_ap/esl_tag.py
index 83d638af9c1..bab8e2e246e 100644
--- a/app/bluetooth/example_host/bt_host_esl_ap/esl_tag.py
+++ b/app/bluetooth/example_host/bt_host_esl_ap/esl_tag.py
@@ -24,39 +24,110 @@
# misrepresented as being the original software.
# 3. This notice may not be removed or altered from any source distribution.
+import threading
from collections import namedtuple
from datetime import datetime as dt
+from image_converter import XbmConverter
from ap_constants import *
from ap_config import IOP_TEST
-from ap_logger import getLogger
+from ap_logger import getLogger, log, LEVELS, logLevel
from ap_sensor import SENSOR_INFO_LENGTH_SHORT, SENSOR_INFO_LENGTH_LONG, SENSOR_TYPES
import esl_lib_wrapper as elw
+import esl_lib
PNP_VENDOR_ID_SOURCE_SIG = 1
DISPLAY_INFO_STRUCT_SIZE = 5
+class InvalidTagStateError(Exception):
+ '''Invalid ESL Tag State error'''
+
+class ImageUpdateFailed(Exception):
+ '''Image update failed'''
+
+class ImageTypeRequired(Exception):
+ '''Image update failed'''
+
+class TagState(int):
+ """ Tag state from the point of view of the AP """
+ IDLE = 0
+ CONNECTING = 1
+ CONNECTED = 2
+
+ def __str__(self) -> str:
+ state_to_str = {
+ self.IDLE: "Idle",
+ self.CONNECTING: "Connecting",
+ self.CONNECTED: "Connected",
+ }
+ try:
+ return state_to_str[self]
+ except KeyError:
+ return f"Unknown internal state ({self})"
+
+class EslState(int):
+ """ Tag state according to the ESL Profile specification """
+ UNASSOCIATED = 0
+ CONFIGURING = 1
+ SYNCHRONIZED = 2
+ UPDATING = 3
+ UNSYNCHRONIZED = 4
+
+ def __str__(self) -> str:
+ state_to_str = {
+ self.UNASSOCIATED: "Unassociated",
+ self.CONFIGURING: "Configuring",
+ self.SYNCHRONIZED: "Synchronized",
+ self.UPDATING: "Updating",
+ self.UNSYNCHRONIZED: "Unsynchronized"
+ }
+ try:
+ return state_to_str[self]
+ except KeyError:
+ return f"Unknown ESL state ({self})"
+
class Tag():
""" ESL Tag """
_counter = 0
- def __init__(self, address, dummy=False):
+ def __init__(self, lib:esl_lib.Lib, address: esl_lib.Address, dummy=False):
self.id = type(self)._counter
if not dummy:
type(self)._counter +=1
+ self.lib = lib
+ self._state = TagState(TagState.IDLE)
+ self._state_timestamp = dt.now()
+ self._current_time_last_set = dt.now().timestamp()
# ESL specific attributes
self.ble_address = address
self.ots_image_type = {}
self.pending_unassociate = False
- self.state = ST_UNASSOCIATED
+ self._advertising_timer = threading.Timer(ADVERTISING_TIMEOUT, self.__advertising_timeout)
self.last_req_timestamp = dt.now().timestamp()
self.last_resp_timestamp = dt.now().timestamp()
self.unresp_command_number = 0
self.basic_state_flags = 0
- self.logger = getLogger()
self.gatt_values = {}
self.gatt_write_values = {}
self.gattdb_handles = None
self.auto_image_count = 0
+ self.connection_handle = None
+ self._blocked = elw.ESL_LIB_STATUS_NO_ERROR
+ # PAST timer, Note: interval is re-initialized properly after PA interval is set below
+ self._past_timer = threading.Timer(10, self.__past_timeout)
+ self._past_timer.daemon = True
+ self._past_initiated = False
+ self._advertising = False
+ self.raw_image = None
+ self.image_file = None
+ self.rotation = None
+ self.label = None
+ self.xbm_converter = XbmConverter()
+ self.busy = False
+
+ # Logger
+ @property
+ def log(self):
+ return getLogger("TAG")
def get_value_as_bytes(self, key) -> bytes:
try:
@@ -70,15 +141,30 @@ def get_value_as_int(self, key):
except KeyError:
return None
+ @property
+ def advertising(self):
+ """ Tell if the tag is advertising at the moment """
+ return self._advertising and self.state != TagState.CONNECTING
+
@property
def provisioned(self):
""" Tell if the tag is provisioned """
- return self.esl_address is not None and self.response_key is not None and self.ap_sync_key is not None and self.time is not None
+ return self.associated and self.response_key is not None and self.ap_sync_key is not None and self.time is not None
+
+ @property
+ def associated(self):
+ """ Tell if the tag is a new instance """
+ return self.esl_address is not None
@property
- def skip_get_info(self):
- """ Tell if getting tag info can be skipped during connecting """
- return elw.ESL_LIB_DATA_TYPE_GATT_CONTROL_POINT in self.gatt_values
+ def synchronized(self):
+ """ Tell if the tag is associated """
+ return self.basic_state_flags & BASIC_STATE_FLAG_SYNCHRONIZED
+
+ @property
+ def blocked(self):
+ """ Tell if the tag is blocked """
+ return self._blocked
@property
def esl_address(self):
@@ -142,7 +228,7 @@ def display_info(self):
except KeyError:
return []
if (len(value) % DISPLAY_INFO_STRUCT_SIZE != 0):
- self.logger.error("Invalid display information")
+ self.log.error("Invalid display information")
return []
field_index = 0
display_info = []
@@ -205,9 +291,59 @@ def pnp_product_version(self):
def serial_number(self):
return self.get_value_as_bytes(elw.ESL_LIB_DATA_TYPE_GATT_SERIAL_NUMBER)
- def set_valid(self):
- """ Set object as a real/valid ESL (as opposed to a virtual ones used for simulation, or those unassociated yet) """
- self.gatt_values[elw.ESL_LIB_DATA_TYPE_GATT_CONTROL_POINT] = True # ESL Control Point is a mandatory characteristic
+ @property
+ def esl_state(self):
+ """ ESL state getter """
+ if self.state in [TagState.IDLE, TagState.CONNECTING]:
+ if not self.associated:
+ return EslState(EslState.UNASSOCIATED)
+ else:
+ if not self.synchronized:
+ return EslState(EslState.UNSYNCHRONIZED)
+ else:
+ return EslState(EslState.SYNCHRONIZED)
+ else:
+ if not self.provisioned:
+ return EslState(EslState.CONFIGURING)
+ else:
+ return EslState(EslState.UPDATING)
+
+ @property
+ def state(self):
+ """ Tag state getter """
+ return self._state
+
+ @state.setter
+ def state(self, value: TagState):
+ """ Connection state setter - for class internal use, only!"""
+ if self._state != value:
+ now = dt.now()
+ new_state = TagState(value)
+ self.log.debug("[%s] Tag state transition: %s -> %s, time spent in state: %s",
+ self.ble_address, self._state, new_state, now - self._state_timestamp)
+ self._state_timestamp = now
+ self._state = new_state
+
+
+ @property
+ def connection_handle(self):
+ """ Connection handle getter """
+ return self._connection_handle
+
+ @property
+ def past_initiated(self):
+ """ PAST procedure progress monitor """
+ return self._past_initiated
+
+ @connection_handle.setter
+ def connection_handle(self, value):
+ """ Connection handle setter - for class internal use, only!"""
+ # Setting the connection handle implicitly changes the connection status accordingly.
+ if value is None:
+ self.state = TagState.IDLE
+ else:
+ self.state = TagState.CONNECTED
+ self._connection_handle = value
def reset(self):
""" Reset object states """
@@ -217,16 +353,54 @@ def reset(self):
elw.ESL_LIB_DATA_TYPE_GATT_ESL_ADDRESS]
self.gattdb_handles = None
self.pending_unassociate = False
- self.update_timestamps()
- self.state = ST_UNSYNCHRONIZED
- self.update_flags(BASIC_STATE_FLAG_SYNCHRONIZED,False)
+ self.update_response_timestamp()
+ self.__update_flags(BASIC_STATE_FLAG_SYNCHRONIZED, False)
self.gatt_write_values = {key: value for key, value in self.gatt_values.items() if key in keys}
- # keep ESL Address if exists, delete the rest - also make the ESL object invalid for possible future re-discovery
- self.gatt_values = {key: value for key, value in self.gatt_values.items() if key == elw.ESL_LIB_DATA_TYPE_GATT_ESL_ADDRESS}
+ # Keep ESL Address if exists and the previous config has been finished succesfully, delete the rest - also make the ESL object invalid for possible future re-discovery
+ self.gatt_values = {key: value for key, value in self.gatt_values.items() if key == elw.ESL_LIB_DATA_TYPE_GATT_ESL_ADDRESS and len(self.gatt_write_values) == len(keys)}
+ if len(self.gatt_values) == 0:
+ # Reset the image counter also in this corner case
+ self.auto_image_count = 0
+ # Reset timers
+ if self._advertising_timer.is_alive():
+ self._advertising_timer.cancel()
+ if self._past_timer.is_alive():
+ self._past_timer.cancel()
+ # Reset busy state
+ self.busy = False
+ self.connection_handle = None
+ self._past_initiated = False
+ self._advertising = False
+
+ def block(self, lib_status = elw.ESL_LIB_STATUS_UNSPECIFIED_ERROR):
+ """ Set blocked state if not set already"""
+ if self._blocked == elw.ESL_LIB_STATUS_NO_ERROR:
+ self.update_request_timestamp()
+ self._blocked = lib_status
+
+ def unblock(self):
+ """ Release from blocked state """
+ self._blocked = elw.ESL_LIB_STATUS_NO_ERROR
+
+ def reset_advertising(self):
+ """ Enable re-discovery of an already known (reported) tag """
+ if self._advertising_timer.is_alive():
+ self._advertising_timer.cancel()
+ self._advertising = False
+
+ def unassociate(self):
+ """ Unassociate tag object """
+ self.gatt_values = {}
+ self.reset()
+ self._associated = False
- def update_state(self, data, state=ST_SYNCHRONIZED):
- """ Update tag state """
- self.state = state
+ def unsynchronize(self):
+ """ Clear the BASIC_STATE_FLAG_SYNCHRONIZED flag internally to consider a tag unsynced """
+ # while clearing the bit it is still possible that the tag is actually synced so doing this allows it to recover silently
+ self.basic_state_flags = self.basic_state_flags & ~BASIC_STATE_FLAG_SYNCHRONIZED
+
+ def handle_response(self, data):
+ """ Handle TLV response """
# Error
if data[0] == TLV_RESPONSE_ERROR:
pass
@@ -236,7 +410,7 @@ def update_state(self, data, state=ST_SYNCHRONIZED):
# Basic state
elif data[0] == TLV_RESPONSE_BASIC_STATE:
bs_bitmap = int.from_bytes(data[1:], byteorder="little")
- self.update_flags(bs_bitmap)
+ self.__update_flags(bs_bitmap, None)
# Display state
elif data[0] == TLV_RESPONSE_DISPLAY_STATE:
pass
@@ -270,21 +444,26 @@ def timestamps_diff(self, timestamp):
""" Get timestamp difference """
return timestamp - self.last_req_timestamp, timestamp - self.last_resp_timestamp
- def update_flags(self, flags, new_state=True):
- """ Update basic state flags """
+ def __update_flags(self, flags, new_state=True):
if flags is not None:
- if new_state:
+ if new_state == True:
self.basic_state_flags = self.basic_state_flags | flags
- else:
+ elif new_state == False:
self.basic_state_flags = self.basic_state_flags & ~flags
-
- if self.basic_state_flags & BASIC_STATE_FLAG_SYNCHRONIZED:
- self.state = ST_SYNCHRONIZED
else:
- self.last_req_timestamp = dt.now().timestamp()
+ self.basic_state_flags = flags # raw data update from PAwR / ESL CP response
+
+ if not self.synchronized and not self.blocked:
+ self.update_request_timestamp()
if flags & BASIC_STATE_FLAG_SERVICE_NEEDED:
- self.logger.warning("ESL ID %d in group %d needs attention, Service Needed flag is active!", self.esl_id, self.group_id)
+ self.log.warning("ESL ID %d in group %d needs attention, Service Needed flag is active!", self.esl_id, self.group_id)
+
+ def find_type_matching_display_index(self, display_type):
+ for x in self.display_info:
+ if x.type == display_type:
+ return self.display_info.index(x)
+ return None
def get_info(self):
""" Get tag information """
@@ -366,10 +545,347 @@ def get_info(self):
bs_string = ", ".join([value for key, value in BASIC_STATE_STRINGS.items() if self.basic_state_flags & key])
if len(bs_string) == 0:
bs_string = "No Basic State flag is set"
- info += f"{bs_string} ({self.basic_state_flags:#06x})"
+ info += f"{bs_string} ({self.basic_state_flags:#06x}) received at {dt.fromtimestamp(self.last_resp_timestamp, tz=None).strftime('%d/%b %H:%M:%S.%f')[:-3]}"
return info
def __str__(self):
if self.esl_address is None:
return f"BLE Address: {self.ble_address}, Unassociated"
return f"BLE Address: {self.ble_address}, ESL ID {self.esl_id} in group {self.group_id} ({self.esl_address:#06x})"
+
+ def handle_event(self, evt):
+ """ Handle event """
+ if isinstance(evt, esl_lib.EventConnectionOpened):
+ if evt.address == self.ble_address:
+ self.connection_handle = evt.connection_handle
+ self.gattdb_handles = evt.gattdb_handles
+ self.pending_unassociate = False
+ if not self.provisioned:
+ self.log.info("Reading tag information from address %s", self.ble_address)
+ self.get_tag_info()
+ else:
+ self.log.info("Tag info already available, skipping discovery for %s", self.ble_address)
+
+ if self._advertising_timer.is_alive():
+ self._advertising_timer.cancel()
+ self._advertising = False
+ if self.esl_address is None:
+ self.log.info("Registering ESL Tag at BLE address: %s", self.ble_address)
+ if self.provisioned:
+ self.log.info("Already known Tag at BLE address: %s", self.ble_address)
+ elif isinstance(evt, esl_lib.EventBondingFinished):
+ if self.blocked:
+ self.unblock() # clear the blocked state if it has been connected and bonded - this happened certainly manually
+ self.block(elw.ESL_LIB_STATUS_CONN_CONFIG_FAILED) # change the reason until at least ESL Address is set
+ elif isinstance(evt, esl_lib.EventConnectionClosed):
+ if evt.connection_handle == self.connection_handle:
+ self._past_timer.cancel()
+ self.connection_handle = None
+ self._past_initiated = False
+ if evt.reason == elw.SL_STATUS_BT_CTRL_REMOTE_USER_TERMINATED:
+ if self.provisioned and not self.pending_unassociate:
+ self.__update_flags(BASIC_STATE_FLAG_SYNCHRONIZED)
+ else:
+ self.__update_flags(BASIC_STATE_FLAG_SYNCHRONIZED, False)
+ self.unresp_command_number = 0
+ self.update_timestamps()
+ elif evt.reason == elw.SL_STATUS_BT_CTRL_CONNECTION_TERMINATED_BY_LOCAL_HOST:
+ self.__update_flags(BASIC_STATE_FLAG_SYNCHRONIZED, False)
+ self.log.info("Connection to %s closed with reason %s",self.ble_address, esl_lib.get_enum("SL_STATUS_",evt.reason))
+ elif isinstance(evt, esl_lib.EventTagInfo):
+ if evt.connection_handle == self.connection_handle:
+ self.gatt_values.update(evt.tlv_data)
+ if elw.ESL_LIB_DATA_TYPE_GATT_PNP_ID in evt.tlv_data:
+ if self.pnp_vendor_id is None:
+ self.log.error("PnP characteristic not found - vendor opcodes support disabled")
+ elif self.pnp_vendor_id == SIG_VENDOR_ID_SILABS:
+ self.log.info("Silabs device found - vendor opcodes are not defined")
+ else:
+ self.log.info("PnP characteristic '0x%02x' found for %s", self.pnp_vendor_id, self.ble_address)
+ if elw.ESL_LIB_DATA_TYPE_GATT_SERIAL_NUMBER in evt.tlv_data:
+ self.log.info("Serial Number String '%s' present on %s", str(self.serial_number), self.ble_address)
+ self.busy = False
+ values = self.gatt_write_values
+ if len(values) and self.associated:
+ if elw.ESL_LIB_DATA_TYPE_GATT_CURRENT_TIME in self.gatt_write_values:
+ # compensate the absolute time value if it's present
+ absolute_time = int.from_bytes(self.gatt_write_values[elw.ESL_LIB_DATA_TYPE_GATT_CURRENT_TIME], 'little')
+ current_timestamp = dt.now().timestamp()
+ time_diff_ms = int(1000 * (current_timestamp - self._current_time_last_set))
+ absolute_time += time_diff_ms
+ self.gatt_write_values[elw.ESL_LIB_DATA_TYPE_GATT_CURRENT_TIME] = absolute_time.to_bytes(4, 'little')
+ self.configure_tag(values)
+ elif isinstance(evt, esl_lib.EventConfigureTagResponse):
+ if evt.connection_handle == self.connection_handle:
+ self.gatt_values[evt.type] = self.gatt_write_values[evt.type]
+ if evt.type == elw.ESL_LIB_DATA_TYPE_GATT_ESL_ADDRESS:
+ if evt.status == elw.SL_STATUS_OK:
+ self._associated = True
+ self.unblock() # clear previous blocked state if the Tag becomes associated
+ else:
+ self._associated = False
+ if self.provisioned:
+ self.log.info("ESL Tag fully provisioned at address %s", self.ble_address)
+ self.busy = False
+ elif isinstance(evt, esl_lib.EventImageType):
+ if evt.connection_handle == self.connection_handle:
+ self.ots_image_type[evt.img_index] = evt.type_data[3]
+ self.busy = False
+ elif isinstance(evt, esl_lib.EventImageTransferFinished):
+ if evt.connection_handle == self.connection_handle:
+ if evt.status == elw.SL_STATUS_OK and evt.img_index > self.auto_image_count:
+ self.auto_image_count = evt.img_index
+ self.log.info("Image %d sent to device at address %s with result 0x%x", evt.img_index, self.ble_address, evt.status)
+ self.busy = False
+ elif isinstance(evt, esl_lib.EventControlPointResponse):
+ if evt.connection_handle == self.connection_handle:
+ self.log.info("Command: %s written successfully for %s", evt.data_sent.hex(), self.ble_address)
+ self.busy = False
+ if evt.data_sent[0] == TLV_OPCODE_FACTORY_RST:
+ self.reset()
+ self.pending_unassociate = True # need to revert pending_unassociate until AP core processes the same event!
+ elif isinstance(evt, esl_lib.EventControlPointNotification):
+ if evt.connection_handle == self.connection_handle:
+ self.handle_response(evt.data)
+ if evt.data[0] == TLV_RESPONSE_BASIC_STATE:
+ self.basic_state_flags = int.from_bytes(evt.data[1:2], 'little')
+ elif isinstance(evt, esl_lib.EventTagFound):
+ if evt.address == self.ble_address:
+ if self.state == TagState.IDLE:
+ if not self._advertising: # note the internal check, not the property! (To print following message only once)
+ self.log.info("ESL service found at BLE address: %s with RSSI: %d dBm", self.ble_address, evt.rssi)
+ self._advertising = True # setting this has to precede self.esl_state == EslState.SYNCHRONIZED check!
+ if self.advertising:
+ if self.esl_state == EslState.SYNCHRONIZED:
+ self.log.warning("ESL at address %s lost sync!", self.ble_address)
+ self.reset() # reset will clear _advertising state, too
+ self._advertising = True # set _advertising back - since it is indeed advertising
+ if self._advertising_timer.is_alive():
+ self._advertising_timer.cancel()
+ self._advertising_timer = threading.Timer(ADVERTISING_TIMEOUT, self.__advertising_timeout)
+ self._advertising_timer.daemon = True
+ self._advertising_timer.start()
+ elif isinstance(evt, esl_lib.EventError):
+ self._advertising = False
+ if evt.lib_status == elw.ESL_LIB_STATUS_BONDING_FAILED:
+ self.state = TagState.IDLE
+ elif evt.lib_status == elw.ESL_LIB_STATUS_CONN_SUBSCRIBE_FAILED:
+ self.block(evt.lib_status)
+ self.log.error("ESL at address %s blocked due to ESL Control Point subscription failure!", self.ble_address)
+ elif evt.lib_status == elw.ESL_LIB_STATUS_CONN_FAILED:
+ if evt.sl_status is not elw.SL_STATUS_ALREADY_EXISTS:
+ self.connection_handle = None
+ if evt.data in [elw.ESL_LIB_CONNECTION_STATE_SERVICE_DISCOVERY, elw.ESL_LIB_CONNECTION_STATE_DIS_DISCOVERY, elw.ESL_LIB_CONNECTION_STATE_ESL_DISCOVERY]:
+ self.log.error("ESL at address %s blocked due to ESL service discovery failure!", self.ble_address)
+ self.block(evt.lib_status)
+ self.state = TagState.IDLE
+ elif evt.lib_status == elw.ESL_LIB_STATUS_CONN_CLOSE_FAILED:
+ self.connection_handle = None
+ self.busy = False
+ if evt.sl_status == elw.SL_STATUS_TIMEOUT and evt.data == elw.ESL_LIB_CONNECTION_STATE_PAST_CLOSE_CONNECTION and self._past_timer.is_alive():
+ self._past_timer.cancel()
+ elif evt.lib_status == elw.ESL_LIB_STATUS_CONN_TIMEOUT:
+ self.connection_handle = None
+ if evt.data == elw.ESL_LIB_CONNECTION_STATE_PAST_CLOSE_CONNECTION:
+ self.log.warning("ESL at address %s failed to sync!", self.ble_address)
+ self.reset()
+ elif evt.lib_status == elw.ESL_LIB_STATUS_OTS_GOTO_FAILED:
+ if evt.sl_status == elw.SL_STATUS_NOT_FOUND:
+ self.log.error("No object found with the requested Object ID for address %s", self.ble_address)
+ self.busy = False
+ elif evt.lib_status == elw.ESL_LIB_STATUS_PAST_INIT_FAILED:
+ self.busy = False
+ self._past_initiated = False
+ if self._past_timer.is_alive():
+ self._past_timer.cancel()
+ if evt.sl_status in [elw.SL_STATUS_BT_CTRL_COMMAND_DISALLOWED, elw.SL_STATUS_INVALID_PARAMETER]:
+ self.log.info("PAST skipped for address %s by ESL already in Synchronized state.", self.ble_address)
+ else:
+ self.log.error("PAST was unsuccesssful, force closing connection to tag at address %s", self.ble_address)
+ self.close_connection()
+ elif evt.lib_status == elw.ESL_LIB_STATUS_OTS_INIT_FAILED:
+ self.auto_image_count = 0
+
+ def __advertising_timeout(self):
+ self.log.warning("Advertisements from address %s are no longer received!", self.ble_address)
+ self._advertising = False
+
+ def __past_timeout(self):
+ """ Called on PAST timeout """
+ self.log.warning("PAST timeout for address %s, force close!", self.ble_address)
+ try:
+ self.close_connection()
+ except Exception as e:
+ self.log.error(e)
+
+ def connect(self,
+ pawr=None,
+ identity: esl_lib.Address=None,
+ key_type: int=elw.ESL_LIB_KEY_TYPE_NO_KEY,
+ key: bytes=None):
+ """ Connect to the tag """
+ if self.state != TagState.IDLE:
+ raise InvalidTagStateError(f"Invalid ESL object state: {self._state} at address {self.ble_address}")
+ self._connection_handle = None # silent but forced reset of handle
+ self.state = TagState.CONNECTING
+ if pawr is None:
+ self._advertising = True # necessary step for any connect requests to undetected advertisers!
+ self.lib.connect(address=self.ble_address,
+ pawr=pawr,
+ identity=identity,
+ key_type=key_type,
+ key=key,
+ gattdb=self.gattdb_handles)
+
+ def close_connection(self):
+ """ Disconnect from the tag """
+ if self.state != TagState.CONNECTED:
+ raise InvalidTagStateError(f"Invalid ESL object state: {self._state} at address {self.ble_address}")
+ self._past_initiated = False
+ try:
+ self.lib.close_connection(self.connection_handle)
+ self.busy = True
+ except esl_lib.CommandFailedError as e:
+ self.log.error(e)
+
+ def get_tag_info(self):
+ """ Get tag info """
+ if self.state != TagState.CONNECTED:
+ raise InvalidTagStateError(f"Invalid ESL object state: {self._state} at address {self.ble_address}")
+ try:
+ self.lib.get_tag_info(self.connection_handle)
+ self.busy = True
+ except esl_lib.CommandFailedError as e:
+ self.log.error(e)
+
+ def configure_tag(self, tlv_data: dict, att_response: bool=True):
+ """ Configure tag using TLVs """
+ if self.state != TagState.CONNECTED:
+ raise InvalidTagStateError(f"Invalid ESL object state: {self._state} at address {self.ble_address}")
+ # Clear anything previously configured but ESL Address before to be (re)written
+ for key in tlv_data:
+ if key != elw.ESL_LIB_DATA_TYPE_GATT_ESL_ADDRESS:
+ if key == elw.ESL_LIB_DATA_TYPE_GATT_CURRENT_TIME:
+ self._current_time_last_set = dt.now().timestamp()
+ try:
+ self.gatt_values.pop(key)
+ except KeyError:
+ pass
+
+ self.gatt_write_values.update(tlv_data)
+ try:
+ self.lib.configure_tag(self.connection_handle, tlv_data, att_response)
+ self.busy = True
+ except esl_lib.CommandFailedError as e:
+ self.log.error(e)
+
+ def write_control_point(self, data: bytes, att_response: bool=True):
+ """ Write ESL Control Point """
+ if self.state != TagState.CONNECTED:
+ raise InvalidTagStateError(f"Invalid ESL object state: {self._state} at address {self.ble_address}")
+ factory_reset = data[0] == TLV_OPCODE_FACTORY_RST
+ if (data[0] == TLV_OPCODE_UNASSOCIATE or factory_reset) and data[1] == self.esl_id:
+ self.pending_unassociate = True
+ try:
+ self.lib.write_control_point(self.connection_handle, data, (att_response or factory_reset))
+ self.busy = True
+ except esl_lib.CommandFailedError as e:
+ self.log.error(e)
+
+ def write_image(self, img_index, img_data: bytes):
+ """ Write image to the tag """
+ if self.state != TagState.CONNECTED:
+ raise InvalidTagStateError(f"Invalid ESL object state: {self._state} at address {self.ble_address}")
+ try:
+ self.lib.write_image(self.connection_handle, img_index, img_data)
+ self.busy = True
+ except esl_lib.CommandFailedError as e:
+ self.log.error(e)
+
+ def get_image_type(self, img_index):
+ """ Get image type """
+ if self.state != TagState.CONNECTED:
+ raise InvalidTagStateError(f"Invalid ESL object state: {self._state} at address {self.ble_address}")
+ try:
+ self.lib.get_image_type(self.connection_handle, img_index)
+ self.busy = True
+ except esl_lib.CommandFailedError as e:
+ self.log.error(e)
+
+ def image_update(self, image_index, file, raw=False, display_ind=None, label=None, rotation=None, cropfit=False):
+ """ Update image """
+ if self.state != TagState.CONNECTED:
+ raise InvalidTagStateError(f"Invalid ESL object state: {self._state} at address {self.ble_address}")
+
+ if raw:
+ self.log.info("Raw image file opened: %s", file)
+ self.raw_image = open(file, "rb").read()
+ #nothing to do with raw files except upload!
+ else:
+ # Open and convert image file, otherwise
+ ots_object_type = None
+ if display_ind is None:
+ self.image_file = file
+ self.rotation = rotation
+ self.label = label
+ try:
+ ots_object_type = self.ots_image_type[image_index]
+ except KeyError:
+ ots_object_type = None
+ if ots_object_type == "unknown":
+ self.log.error("Unable to read ots object type")
+ elif ots_object_type == None:
+ self.get_image_type(image_index)
+ raise ImageTypeRequired("Image type required for address %s, image: %d", self.ble_address, image_index)
+ else:
+ display_ind = self.find_type_matching_display_index(ots_object_type)
+ if display_ind is None:
+ raise ImageUpdateFailed(f"Unable to find a valid display index for address {self.ble_address} and ots_object_type: " + hex(ots_object_type))
+ else:
+ if self.display_count is None or display_ind >= self.display_count:
+ raise ImageUpdateFailed(f"Invalid display index for address {self.ble_address}: " + str(display_ind))
+ ots_object_type = self.display_info[display_ind].type
+
+ disp_size = (self.display_info[display_ind].width, self.display_info[display_ind].height)
+ disp_type = self.display_info[display_ind].type
+
+ if ots_object_type and disp_type == ots_object_type:
+ self.log.info("Display type matches object type for address %s", self.ble_address)
+ if type(file) == str:
+ self.xbm_converter.open(file)
+ if self.xbm_converter.image is not None:
+ self.log.info("Image file opened: %s", file)
+ elif type(file) == bytes:
+ self.xbm_converter.open_frombytes(file)
+ if ots_object_type == ESL_WSTK_DISPLAY_TYPE:
+ self.raw_image = self.xbm_converter.convert(display_size=disp_size, bw=True, label=label, rotation=rotation, cropfit=cropfit) # bw=true if object type FF, bw=False if FE
+ elif ots_object_type == ESL_EPAPER_DISPLAY_TYPE:
+ self.raw_image = self.xbm_converter.convert(display_size=disp_size, bw=False, label=label, rotation=rotation, cropfit=cropfit)
+ else:
+ self.raw_image = b""
+ raise ImageUpdateFailed(f"Unknown OTS object type, automatic conversion can't be done. Please upload raw image data to address {self.ble_address}!")
+ else:
+ raise ImageUpdateFailed(f"Cannot upload file: display type is not the same as object type for address {self.ble_address}!")
+ # Send file if raw input or converted result seems OK
+ if len(self.raw_image) != 0:
+ self.write_image(image_index, self.raw_image)
+ else:
+ raise ImageUpdateFailed(f"Cannot upload file: image conversion failed for address {self.ble_address}")
+
+ def initiate_past(self, pawr_handle, pa_interval):
+ """ Initiate PAST """
+ if self.state != TagState.CONNECTED:
+ raise InvalidTagStateError(f"Invalid ESL object state: {self._state} at address {self.ble_address}")
+ if self._past_initiated:
+ return
+ try:
+ self.lib.initiate_past(self.connection_handle, pawr_handle)
+ self._past_initiated = True
+ if self._past_timer.is_alive():
+ self._past_timer.cancel()
+ self._past_timer = threading.Timer(pa_interval * AUX_SYNC_IND_PDU_MAX_SKIP_COUNT, self.__past_timeout)
+ self._past_timer.daemon = True
+ self._past_timer.start()
+ self.busy = True
+ except esl_lib.CommandFailedError as e:
+ self.log.error(e)
diff --git a/app/bluetooth/example_host/bt_host_esl_ap/esl_tag_db.py b/app/bluetooth/example_host/bt_host_esl_ap/esl_tag_db.py
new file mode 100644
index 00000000000..e4a7daa29c3
--- /dev/null
+++ b/app/bluetooth/example_host/bt_host_esl_ap/esl_tag_db.py
@@ -0,0 +1,89 @@
+"""
+ESL Tag Database.
+"""
+
+# Copyright 2023 Silicon Laboratories Inc. www.silabs.com
+#
+# SPDX-License-Identifier: Zlib
+#
+# The licensor of this software is Silicon Laboratories Inc.
+#
+# This software is provided 'as-is', without any express or implied
+# warranty. In no event will the authors be held liable for any damages
+# arising from the use of this software.
+#
+# Permission is granted to anyone to use this software for any purpose,
+# including commercial applications, and to alter it and redistribute it
+# freely, subject to the following restrictions:
+#
+# 1. The origin of this software must not be misrepresented; you must not
+# claim that you wrote the original software. If you use this software
+# in a product, an acknowledgment in the product documentation would be
+# appreciated but is not required.
+# 2. Altered source versions must be plainly marked as such, and must not be
+# misrepresented as being the original software.
+# 3. This notice may not be removed or altered from any source distribution.
+
+import esl_lib
+import esl_tag
+from ap_constants import BROADCAST_ADDRESS
+
+class TagDB:
+ """ ESL tag database """
+ def __init__(self):
+ self.tags: list[esl_tag.Tag] = []
+
+ def add(self, lib:esl_lib.Lib, address: esl_lib.Address, dummy=False):
+ tag = self.find(address)
+ if tag is None:
+ tag = esl_tag.Tag(lib, address, dummy=dummy)
+ self.tags.append(tag)
+ return tag
+
+ def remove(self, tag:esl_tag.Tag=None, address: esl_lib.Address=None):
+ if address is not None:
+ tag = self.find(address)
+ if tag is not None:
+ self.tags.remove(tag)
+
+ def find(self, node_id):
+ value = node_id
+ if isinstance(node_id, (esl_lib.Address, str)):
+ attr = 'ble_address'
+ elif isinstance(node_id, esl_lib.ConnectionHandle):
+ attr = 'connection_handle'
+ elif isinstance(node_id, (tuple, list)):
+ esl_id, group_id = node_id
+ if esl_id == BROADCAST_ADDRESS:
+ # Invalid ESL ID
+ return None
+ value = esl_id | (group_id << 8)
+ attr = 'esl_address'
+ else:
+ return None # Unknown node ID type
+ for tag in self.tags:
+ if getattr(tag, attr) == value:
+ return tag
+ return None # Tag not found
+
+ def all(self):
+ return self.tags
+
+ def list_group(self, group_id):
+ return [tag for tag in self.tags if tag.group_id == group_id ]
+
+ def list_state(self, state):
+ if not isinstance(state, (list, tuple)):
+ state = [state]
+ return [tag for tag in self.tags if tag.state in state]
+
+ def list_esl_state(self, esl_state):
+ if not isinstance(esl_state, (list, tuple)):
+ esl_state = [esl_state]
+ return [tag for tag in self.tags if tag.esl_state in esl_state]
+
+ def list_advertising(self):
+ return [tag for tag in self.tags if tag.advertising]
+
+ def list_blocked(self):
+ return [tag for tag in self.tags if tag.blocked]
diff --git a/app/bluetooth/example_host/bt_host_esl_ap/image_converter.py b/app/bluetooth/example_host/bt_host_esl_ap/image_converter.py
index e953e1e9810..8fae1ac82ed 100644
--- a/app/bluetooth/example_host/bt_host_esl_ap/image_converter.py
+++ b/app/bluetooth/example_host/bt_host_esl_ap/image_converter.py
@@ -55,16 +55,20 @@ class XbmConverter():
def __init__(self, image=None):
self.image = image
- self.logger = getLogger()
self.DISPLAY_WSTK_PALETTE = (XbmConverter.xbm_white + XbmConverter.xbm_black)
self.DISPLAY_EPD_PALETTE = (XbmConverter.xbm_white + XbmConverter.xbm_black + XbmConverter.xbm_red)
+ # Logger
+ @property
+ def log(self):
+ return getLogger("IMG")
+
def open(self, img_path):
""" Open image file """
try:
self.image = Image.open(img_path, "r").convert(mode='RGB')
except:
- self.logger.error("Cannot open image file: %s!", img_path)
+ self.log.error("Cannot open image file: %s!", img_path)
self.image = None
def open_frombytes(self, img_bytes):
@@ -72,7 +76,7 @@ def open_frombytes(self, img_bytes):
try:
self.image = Image.open(io.BytesIO(img_bytes)).convert(mode='RGB')
except:
- self.logger.error("Cannot open image file: %s!", img_bytes)
+ self.log.error("Cannot open image file: %s!", img_bytes)
self.image = None
def save_to_xbm(self, out):
@@ -80,7 +84,7 @@ def save_to_xbm(self, out):
try:
self.image.convert('1').save(out, format='xbm')
except AttributeError:
- self.logger.error("File conversion failed!")
+ self.log.error("File conversion failed!")
def reverse_bits(self, x):
""" Reversing MSB->LSB bit order of a bytes class object """
diff --git a/app/bluetooth/example_host/bt_host_esl_ap/makefile b/app/bluetooth/example_host/bt_host_esl_ap/makefile
index 592352a683c..74722cea999 100644
--- a/app/bluetooth/example_host/bt_host_esl_ap/makefile
+++ b/app/bluetooth/example_host/bt_host_esl_ap/makefile
@@ -1,13 +1,18 @@
# Build library dependencies and Python wrappers for the ESL AP script
-.PHONY: all lib wrapper clean cleanlib cleancopy
+.PHONY: all lib debug release wrapper clean clean_sql cleanlib cleanlib_sql cleancopy
-all: lib wrapper
+all: release
+debug: ARGS += debug
+debug: lib wrapper
+release: ARGS += release
+release: lib wrapper
clean: cleanlib cleancopy
+cleansql: cleanlib_sql cleancopy
lib:
@$(MAKE) -C ../../common_host/esl_lib -j $(ARGS)
- @$(MAKE) -C ../../common_host/esl_key_lib -j
+ @$(MAKE) -C ../../common_host/esl_key_lib -j $(ARGS)
@cp -Rv ../../common_host/esl_lib/lib .
@cp -Rv ../../common_host/esl_key_lib/lib .
@@ -18,9 +23,13 @@ wrapper:
@cp -v ../../common_host/esl_key_lib/esl_key_lib_wrapper.py .
cleanlib:
+ @$(MAKE) -C ../../common_host/esl_lib clean
+ @$(MAKE) -C ../../common_host/esl_key_lib clean_keep_sql
+
+cleanlib_sql:
@$(MAKE) -C ../../common_host/esl_lib clean
@$(MAKE) -C ../../common_host/esl_key_lib clean
cleancopy:
rm -f *_wrapper.py
- rm -rf lib
+ rm -rf lib
\ No newline at end of file
diff --git a/app/bluetooth/example_host/bt_host_esl_ap/readme/readme.md b/app/bluetooth/example_host/bt_host_esl_ap/readme/readme.md
index 5b64169178c..bc6597d8002 100644
--- a/app/bluetooth/example_host/bt_host_esl_ap/readme/readme.md
+++ b/app/bluetooth/example_host/bt_host_esl_ap/readme/readme.md
@@ -9,6 +9,7 @@ This Python example implements the functionality of an Access Point as specified
Table of content:
- [ESL Access Point](#esl-access-point)
+ - [Features](#features)
- [Limitations, known issues](#limitations-known-issues)
- [Project structure](#project-structure)
- [Getting started](#getting-started)
@@ -35,20 +36,27 @@ Table of content:
- [read\_sensor](#read_sensor)
- [vendor\_opcode](#vendor_opcode)
- [Access Point control commands](#access-point-control-commands)
- - [demo](#demo)
- [help](#help)
- [mode](#mode)
- - [scan](#scan)
- [set\_rssi\_threshold](#set_rssi_threshold)
+ - [scan](#scan)
- [list](#list)
- [sync](#sync)
+ - [demo](#demo)
- [script](#script)
+ - [verbosity](#verbosity)
- [exit](#exit)
+## Features
+- Full support of ESL Profile and Service specification v1.0
+- Built-in auto conversion for Silabs ESL example devices with image storage and display for any size.
+- Multiple connections in parallel up to the limits of the Bluetooth stack on the attached ESL Network Co-Processor embedded target.
+- Encrypted communication between the AP script and the embedded target, which can be optionally disabled or completely removed. For more information on building prerequisites of the secure components for the NCP, see chapter 4.2 of the SiLabs application note [AN-1259](https://www.silabs.com/documents/public/application-notes/an1259-bt-ncp-mode-sdk-v3x.pdf).
+- Simple chaining in CLI using `;` (semicolon) as separator between consequtive commands
+- Simple [scripting](#script) capability
## Limitations, known issues
---
-- Only one BLE connection is handled at a time. This may affect the system scalability performance.
- In some cases, especially when there are many BLE devices advertising nearby while the AP is scanning for longer periods, the AP script may become unresponsive. In such a case, it may help to limit the period of scanning or to reduce the number of nearby advertising devices. If neither of these are possible, you may want to increase the throughput of the NCP VCOM according to [this article](https://community.silabs.com/s/article/wstk-virtual-com-port-baudrate-setting?language=en_US). After changing the WSTK VCOM speed as described, do not forget to update the VCOM Baud rate configuration of the ESL AP NCP example also accordingly, then re-build and re-flash the target with the new firmware.
- On Windows, there is also a known issue when running the AP where the debugging trace and command line input can interfere with each other on some terminals if python pyreadline3 is installed, so it is strongly recommended to uninstall it using the command `pip uninstall pyreadline3` before running the AP. To find out if it is installed or not, the command `pip freeze` can be used.
- MSYS2 MinGW bash is not recommended for use with ESL Access Point Python example application due to various compatibility issues between the native Windows Python environment and that of MSYS2.
@@ -92,7 +100,7 @@ _Note: Shall any unsolicited error occur during the automated process, the autom
## Getting started
---
-The NCP Host side application requires Python v3. Run `pip install -r requirements.txt` to install all other requirements for the application. Make sure to run this command before running `make`.
+The NCP Host side application requires Python 3. Run `pip install -r requirements.txt` to install all other requirements for the application. Make sure to run this command before running `make`.
On the target side an EFR device is needed, programmed with the *Bluetooth - NCP ESL Access Point* sample application along with an appropriate bootloader project called *Bootloader - NCP BGAPI UART DFU*.
@@ -167,57 +175,73 @@ Parameters:
#### config
Configure the writable mandatory GATT characteristics of the ESL tag.
-Usage: `config [-h] [--full] [--esl_id ] [--group_id ] [--sync_key] [--response_key] [--time] [--absolute ] [device]`
+Usage: `config [-h] [--full] [--esl_id ] [--group_id ] [--sync_key] [--response_key] [--time | --absolute ] [device]`
Positional argument:
-- `device`: Bluetooth address of the target device (e.g. `AA:BB:CC:DD:EE:22`) in case insensitive format
+- `device`: Bluetooth address of the target device (e.g. `AA:BB:CC:DD:EE:22`) in case insensitive format or `all`.
Parameters:
-- `[--full]`: Configure everything in one step.
+- `[--full]`: Configure everything in one step. ESL ID and group can be specified to override default values - see notes.
- `[--esl_id, -i ]`: New ESL ID of the connected tag.
- `[--group_id, -g ]`: New ESL group ID (optional, default is group 0).
- `[--sync_key, -sk]`: Set current Access Point Sync Key Material.
- `[--response_key, -rk]`: Generate then set new Response Key Material.
- `[--time, -t]`: Set current Absolute Time of the ESL Access Point.
-- `[--absolute, -a ]`: Set custom Absolute Time epoch value - use with care! Mutually exclusive with the `--time` parameter.
+- `[--absolute, -a ]`: Set custom Absolute Time epoch value - use with care! _Mutually exclusive with the `--time` parameter._
_Notes:_
- _Either the option `--full` or at least one of the optional parameters shall be given._
+- _The 'all' keyword can be used to configure a number of connected ESLs, but the ESL ID can't be specified in turn, as this would make the command ambiguous._
+- _However, the same ESL group ID can be specified for multiple connected devices - but use this with care, as this command doesn't check against existing ESL configurations, so the network MAY END UP BROKEN!_
Examples:
- `config --full --absolute 0`
- Will configure everything plus overrides the ESL Absolute Time epoch value for the given tag (e.g. for testing purposes)
+
+ Will configure everything plus overrides the ESL Absolute Time epoch value for the given tag (e.g. for testing purposes)
- `config -i 2 -g 3`
+
(Re-)configure only ESL ID and group ID - please note that the other ESL Characteristics e.g. Key Materials and Absolute Time will remain unchanged this way, including their unconfigured states if that's the case.
- `config -i 1 AA:BB:CC:DD:EE:22`
+
(Re-)configure only ESL ID while group ID remains unchanged (0 by default if not given before). Bluetooth address shall be given if there are more active connection opened.
#### connect
- Connect to an ESL device with the specified address.
+ Connect to one or more ESL devices.
Usage: `connect [-h] [--group_id ] [--addr_type, -t] address`
+positional arguments:
+- `address` Bluetooth address (e.g. `AA:BB:CC:DD:EE:22`) in case insensitive format or ESL ID of the tag or `all`.
+
Parameters:
-- `address`: Bluetooth address (e.g., `AA:BB:CC:DD:EE:22`) in case insensitive format or ESL ID of the Tag.
- `[--group_id, -g ]`: ESL group ID (optional, default is group 0).
- `[--addr_type, -t]`: ESL address type (optional), possible values:
- - `public`: Public device address (default).
- - `static`: Static device address.
- - `rand_res`: Resolvable private random address.
- - `rand_nonres`: Non-resolvable private random address.
+ - `public`: Public device address (default assumption).
+ - `static`: Random static device address.
_Notes:_
- _`` and `` can be used instead of `` if ESL is already configured._
-- _`` will be taken into account only if the given `` is unknown - otherwise the proper type reported by the remote device will be used_
-- _If the group ID is not given after the ESL ID then the default value group zero is used. This applies to many commands expecting the group ID as optional parameter._
+- _`` will be taken into account only if the given `` is unknown - otherwise the proper type reported by the remote device will be used._
+- _If the `` is not given after the ESL ID then the default value group zero is used. This applies to many commands expecting the group ID as optional parameter._
+- _The `all` keyword can be used with a special meaning with `connect` command: it will try to connect to all advertiser ESLs (within the 'group_id' if it is given or to any advertisers if it isn't) up to the the maximum number of simultaneous connections supported by the current build of the ESL library and the attached Network Co-Processor embedded controller._
+- _If the group is specified along with the keyword `all`, then only devices in the group will be connected. That is, specifying the group ID will not work with ESLs that are not yet configured._
+- _An explicit address type is ignored for an already configured ESL that is addressed by ESL ID. The correct type is already known in this case and will be used instead._
-Example:
+Examples:
- `connect bc:33:ac:fa:57:d0`
+ Try connect to the given address - even if it's advertisement is not detected e.g. due disabled scanning. Will fail with timeout if the given address is out of radio range.
+- `connect`
+
+ Checks nearby advertisers and connects to one if there's only one. Scan needs to be enabled for this to work.
+- `connect all`
+
+ Checks nearby advertisers and connects to all up to the supported number of parallel connections. Scan needs to be enabled for this to work.
+
#### delete\_timed
Delete a delayed command of an ESL Tag peripheral with the selected index.
-Usage: delete_timed [-h] [--group_id ] {led,display} esl_id index
+Usage: `delete_timed [-h] [--group_id ] {led,display} esl_id index`
Parameters:
- `{led,display}`: Delete timed led or display_image command.
@@ -226,21 +250,33 @@ Parameters:
- `[--group_id, -g ]`: ESL group ID (optional, default is group 0).
#### disconnect
- Initiate the Periodic Advertisement Sync Transfer process then
- disconnect from an ESL device with the specified address.
+ Initiate the Periodic Advertisement Sync Transfer process if PAwR train is
+ available then disconnect from an ESL device with the specified address.
+
+Usage: `disconnect [-h] [--group_id ] []`
-Usage: `disconnect [-h] [--address ] [--group_id ]`
+Positional argument:
+- ``: Bluetooth address (e.g. `AA:BB:CC:DD:EE:22`) in case insensitive format or ESL ID of the tag or `all`.
Parameters:
-- `[--address ]`: Bluetooth address (e.g., `AA:BB:CC:DD:EE:22`) in case insensitive format or ESL ID of the Tag.
- `[--group_id, -g ]`: ESL group ID (optional, default is group 0).
-_Note:_
-- _Should no address be given, then the default active connection will be closed if any._
+_Notes:_
+- _If no address is specified, the default active connection is closed - if only one exists._
+- _To close more existing connections at once, you can use the `disconnect all` command._
+- _If the group ID is specified with the keyword `all`, then only the devices in the group will be disconnected._
Examples:
-- `disconnect bc:33:ac:fa:57:d0`
-- or simply `disconnect`
+- `disconnect bc:33:ac:fa:57:d0`
+
+ Disconnect from the addressed device.
+- `disconnect`
+
+ Disconnect from the only existing connection - gives error response if there's none or more than one.
+- `disconnect all -g0`
+
+ Disconnect from all connected ESLs that are in group 0.
+
#### display\_image
Display desired image on target ESL.
@@ -263,36 +299,45 @@ _Note:_
- _Timed display commands with a delay shorter than the actual periodic advertisement interval may be rejected on receive by Implausible Absolute Time (0x0C) ESL error response._
Example:
-- `display_image 17 1 0 delay=5000`
+- `display_image 17 1 0 --delay=5000`
![](images/03_imageupdate.png)
#### image\_update
- Update single image on the connected Tag.
+ Update single image on one or more connected Tags.
-Usage: `image_update [-h] [--raw] [--display_index ] [--label ] [--cw] [--ccw] [--flip] [--cropfit] image_index imagefile_path`
+Usage: `image_update [-h] [--address ] [--group_id ] [--label ] [--cropfit] [--raw | --display_index ] [--cw | --ccw | --flip] image_index imagefile_path`
-Parameters:
-- `image_index`: Image index to update.
-- `imagefile_path`: Relative path of the image file.
-- `[--address , -a]`: Bluetooth address of the target device or ESL ID if there are more ESLs connected
+Positional arguments:
+- `image_index`: Image storage index of the ESL tag to be updated.
+- `imagefile_path`: Relative or full path to the selected image file. Use quotation marks if the path contains spaces.
+- `[address]`: Bluetooth address of the target device or ESL ID / `all` if there are more ESLs connected.
+
+Optional arguments:
- `[--group_id , -g ]`: ESL group ID (optional, default is group 0)
+- `[--label, -l ]`: Caption to be written over the image. Use quotation marks if it includes spaces or line breaks.
+- `[--cropfit, -c]`: Fit the image to the display proportions by cropping.
- `[--raw, -r]`: Upload raw image file without any conversion.
-- `[--display_index, -d ]`: Try auto-conversion image for this display.
-- `[--label, -l ]`: Text label overlay to be written on the image.
+- `[--display_index, -d ]`: Try auto-conversion image for this display. Mutuall exclusive with `--raw` argument.
- `[--cw, -rr]`: Clockwise (right) rotation.
- `[--ccw, -rl]`: Counter clockwise (left) rotation.
- `[--flip, -f]`: Turn the image upside down
_Note: cw, ccw and flip are mutually exclusive_
-- `[--cropfit, -c]`: Fit the image to the display proportions by cropping.
_Notes:_
- _ESL Tag must be connected to the AP before running this command._
- _The ESL won't display any change after the image upload is complete unless a `display image` command is also sent with the same image index - or a `refresh display` command to a display already showing the same image that has changed. Please refer to the `display_image` and `refresh_display` commands' examples._
+- _To use space or backslash in the filename or other special characters, such as line break escape sequences in the text caption, please enclose these strings in quotes._
+- _The modifiers like rotation, fitting and and labeling are mutually exclusive with raw data input._
+- _If the group is specified along with the keyword `all`, then only connected devices in the group will be affected._
+
+Examples:
+- `image_update 0 ./image/banana.png --label="Line 1\nLine 2"`
-Example:
-- `image_update 1 image/croissant.png`
+ Send an image to index 0 on the single connected ESL with two lines of label. Note that address is a positional argument yet it can be omitted if there's only one connected device present at the moment.
+- `image_update 1 "/user/home/path with space/img.jpg"` all
+ Use the 'all' keyword as special address to send the same image to slot 1 on all connected ESLs.
#### led
Turn on / off or flash an LED utilizing the LED control command.
@@ -318,11 +363,13 @@ Parameters:
- `[--date, -dt YYYY-MM-DD]`: Execution date of the command in ISO-8601 format (optional to time, only).
- `[--delay, -dy ]`: Delay in milliseconds (optional).
-Example: `led flash 17 index=1 pattern=101100111000 time=16:18:00`
+Example: `led flash 17 --index=1 --pattern=101100111000 --time=16:18:00`
![](images/04_ledon.png)
_Notes:_
+- _Arguments controlling flashing parameters are ignored for 'on' and 'off' commands._
+- _Color and brightness control parameters are useless for 'off' command._
- _Timed LED commands with a delay shorter than the actual periodic advertisement interval may be rejected on receive by Implausible Absolute Time (0x0C) ESL error response. Please refer the ESL specification on timed commands._
- _If the delay is given in the human readable form (using `--time`) then the LED will either turn on on the same day at the specified time or the next day - the latter if the given time has passed already on your local computer's clock!_
- _In the SoC ESL Tag example the LED at index 0 is used for special purposes, that is it can't be controlled directly as opposed to LED 1 on the WSTK. Rather, LED 0 is used as optical feedback only for various internal states of the ESL Tag. Nevertheless, the special function for LED 0 can be still switched on and off via the `led` command._
@@ -334,19 +381,24 @@ _Notes:_
Usage: `refresh_display [-h] [--group_id ] esl_id display_index`
Parameters:
-- `esl_id`: ESL ID of the Tag. _Note: `all` also can be used as a broadcast address (0xff)._
-- `display_id`: Display index.
-- `[--group_id, -g ]`: ESL group ID (optional, default is group 0).
+- `esl_id`: ESL ID of the Tag. _Note: `all` also can be used as a broadcast address (0xff)._
+- `display_id`: Display index.
+- `[--group_id, -g ]`: ESL group ID (optional, default is group 0).
#### update\_complete
- Send update complete command.
+ Send Update Complete ESL opcode.
Usage: `update_complete [-h] [--group_id ] [address]`
+Positional argument:
+- `[address]`: Bluetooth address (e.g. `AA:BB:CC:DD:EE:22`) in case insensitive format or ESL ID of the tag or `all`.
+
Parameters:
-- `[address]`: Bluetooth address (e.g. 'AA:BB:CC:DD:EE:22') in case insensitive format or ESL ID of the tag.
-- `[--group_id, -g ]`: ESL group ID (optional, default is group 0).
+- `[--group_id, -g ]`: ESL group ID (optional, default is group 0).
+
+_Notes:_
+- _The `update_complete` command works only in IOP test mode!_
+- _If the group is specified along with the keyword `all`, then only connected devices in the group will be affected._
-_Note: Warning! The `update_complete` command works only in IOP test mode!_
#### unassociate
Unassociate Tag from AP.
@@ -357,7 +409,7 @@ Parameters:
_Note: `all` also can be used as a broadcast address (0xff)._
- `[--group_id, -g ]`: ESL group ID (optional, default is group 0).
-Example: `unassociate 17 2`
+Example: `unassociate 17 -g 2`
#### factory\_reset
Reset ESL to a state when it was not associated with the AP.
@@ -400,26 +452,22 @@ Parameters:
- `[--data, -d ]`: ASCII hexadecimal data stream up to 16 bytes overall - an appropriate TLV to the given length will be built automatically.
- `[--group_id, -g ]`: ESL group ID (optional, default is group 0).
-Example:
+Examples:
- `vendor_opcode 0 -g 1`
- there will be no extra payload, the resulting ESL TLV is 0F00 for group 1
+
+ There will be no extra payload, the resulting ESL TLV is 0F00 for group 1
- `vendor_opcode 3 --data 0x0004`
+
2 bytes payload, the resulting ESL TLV is 2F030004 for default group 0
- `vendor_opcode 1 --data 12233`
+
3 bytes payload, the resulting ESL TLV is 3F01012233
- `vendor_opcode 5 -d 0012233`
+
4 bytes payload, the resulting ESL TLV is 4F0500012233
### Access Point control commands
---
-#### demo
- Start or stop advertising Dynamic GATT.
-
-Usage: `demo [-h] {on,off}`
-
-Parameters:
-- `{on,off}`: Turn AP advertising on or off for ESL Demo in EFR Connect mobile app.
-
#### help
Help utility.
@@ -427,9 +475,11 @@ Usage: `help `
Examples:
- `help`
-Display available commands:
+
+ Display available commands
- `help list`
-Display help message of a specific (in this case `list`) command:
+
+ Display help message of a specific (in this case `list`) command
![](images/terminal_help.png)
@@ -437,27 +487,20 @@ Display help message of a specific (in this case `list`) command:
#### mode
Changes ESL Access Point operation mode.
-Usage: mode [-h] [{auto,manual}]
+Usage: `mode [-h] [{auto,manual}]`
Parameters:
- `{auto,manual}`: Switch to automatic or manual mode.
-Example:
+_Note: To check current mode you can issue the command without argument._
+
+Examples:
- `mode manual`
+
Change mode to manual mode.
- `mode`
- Ask current mode.
-
-#### scan
- Start or stop scanning for advertising ESL devices.
-
-Usage: `scan [-h] [--active, -a] {start,stop}`
-
-Parameters:
-- `{start, stop}`: Start/stop scanning for advertising ESL devices.
-- `[--active]`: Start active scan instead of default passive.
-_Note: Scanning starts automatically when AP script is started in auto mode to provide continuous Tag discovery._
+ Ask current mode.
#### set\_rssi\_threshold
Set RSSI filter threshold value. Below this value the device will be ignored during scanning.
@@ -469,6 +512,19 @@ Parameters:
_Note: Negative values are accepted, only!_
+#### scan
+ Start or stop scanning for advertising ESL devices.
+
+Usage: `scan [-h] [--active, -a] {start,stop}`
+
+Parameters:
+- `{start, stop}`: Start/stop scanning for advertising ESL devices.
+- `[--active]`: Start active scan instead of default passive.
+
+_Notes:_
+- _Passive tpye scanning starts automatically when AP script is started in auto mode to provide continuous Tag discovery._
+- _You can obtain the current status of the scanning by omitting the choice._
+
#### list
List ESL Tag information.
@@ -511,16 +567,29 @@ _Notes:_
- _After changing the PAwR sync configuration by `sync config` the sync train needs to be restarted by issuing a simple `sync start` command. The new config will take place until exiting the script._
- _Issuing `sync config` without any further parameter will display the current sync train configuration._
- _Using the optional `-ms` argument with the 'config' subcommand allows you to specify timing parameters in milliseconds instead of their natural units, but this may introduce rounding errors. Please also note that with this option the fractional milliseconds can't be specified precisely._
+- _You can ask for the current status of the PAwR train by omitting the choice._
Examples:
- `sync start`
+
Start sync with current PAwR parameters.
- `sync config -min 1500 -max 2500 -sc 3 -si 250 -rd 170 -rs 3 -rc 24`
+
Configure PAwR train with given parameters - please note that the new config will be active after sync is re-started.
- `sync config`
+
Get current config and doesn't change any sync status. That is, the PAwR train will continue running if it was already enabled.
- `sync start [-min 2000] -max 2100`
- Start sync with current PAwR parameters but override interval temporarily to value between 2.0 and 2.1 sec. Please note that this short form is for convenience only to change the interval quickly, but its effect on current configuration is not permanent and the value is always interpreted in milliseconds.
+
+ Start sync with current PAwR parameters, but temporarily override the interval to a value between 2.0 and 2.1 seconds. Please note that this short form is only for convenience to quickly change the interval, but its effect on the current configuration is not permanent and the value is always interpreted in milliseconds - so it may also introduce rounding errors.
+
+#### demo
+ Control the built-in advertising feature of the ESL NCP AP target for the ESL demo in the EFR Connect mobile application.
+
+Usage: `demo [-h] {on,off}`
+
+Parameters:
+- `{on,off}`: Turn AP advertising on or off for ESL Demo in EFR Connect mobile app.
#### script
Record or execute commands from an input file.
@@ -540,12 +609,31 @@ _Notes:_
- _Recorded script files may run other scripts also, but never use it recursively! That is, avoid running the script from within itself or the AP script will crash. However, it is strongly advised to keep the scripting level low as possible. Use with care!_
Examples:
- - `script record myscript.esl`
- Start recording to local file `myscript.esl`.
- - `script record stop`
- Stop current recording (issue after steps to be recorded were executed manually).
- - `script run myscript.esl`
- Repeat steps (commands) that were previously recorded to local file `myscript.esl`.
+- `script record myscript.esl`
+
+ Start recording to local file `myscript.esl`.
+- `script record stop`
+
+ Stop current recording (issue after steps to be recorded were executed manually).
+- `script run myscript.esl`
+
+ Repeat steps (commands) that were previously recorded to local file `myscript.esl`.
+- `script record test.script ; image_update 0 "image/banana.png" -l "hello;\n world!" ; ping 0 ; config -f -i1 ; script wait 2 ; display_image 1 0 0 ; script record stop`
+
+ Advanced example to demonstrate complex scripting with command chaining at the same time. Please note that the command separator `;` needs spaces before and after for the chaining to work properly. The recored script will contain one command per line as it was recorded without command chaining.
+
+#### verbosity
+
+ Set Access Point logging verbosity level at runtime
+
+Usage: `verbosity [-h] [{NOTSET,DEBUG,INFO,WARNING,ERROR,CRITICAL}]`
+
+Parameters:
+- `{NOTSET,DEBUG,INFO,WARNING,ERROR,CRITICAL}` Level to apply
+
+_Notes:_
+- _To check current verbosity level you can issue the command without argument._
+- _NOTSET can be used to display debugging messages not only for AP code, but also for all python modules that may utilze logging._
#### exit
Terminate AP application.
diff --git a/app/bluetooth/example_host/bt_host_esl_ap/requirements.txt b/app/bluetooth/example_host/bt_host_esl_ap/requirements.txt
index 267368cfe41..d293f8f92dd 100644
--- a/app/bluetooth/example_host/bt_host_esl_ap/requirements.txt
+++ b/app/bluetooth/example_host/bt_host_esl_ap/requirements.txt
@@ -5,5 +5,5 @@ colorlog
intelhex
bincopy
qrcode
-ctypesgen
+ctypesgen>=1.1.0
pyserial
diff --git a/app/bluetooth/script/ctypesgen_wrapper_fix.py b/app/bluetooth/script/ctypesgen_wrapper_fix.py
index fd312d9c0f8..91d107bf184 100755
--- a/app/bluetooth/script/ctypesgen_wrapper_fix.py
+++ b/app/bluetooth/script/ctypesgen_wrapper_fix.py
@@ -3,9 +3,10 @@
import os
import sys
import argparse
+import re
-SECTION_1_ORIG = 'path = os.path.abspath(path)'
-SECTION_1_REPL = 'path = os.path.abspath(os.path.join(os.path.dirname(__file__), path))'
+SECTION_1_ORIG = r'(\w+) \= os\.path\.abspath\((\1)\)'
+SECTION_1_REPL = ' = os.path.abspath(os.path.join(os.path.dirname(__file__), '
SECTION_2_ORIG = 'add_library_search_dirs([])'
SECTION_2_REPL = 'add_library_search_dirs(["./lib"])'
@@ -47,10 +48,15 @@ def main():
except FileNotFoundError:
print("Python wrapper file not found: {:s}".format(pf))
sys.exit(1)
- cm = c.replace(SECTION_1_ORIG, SECTION_1_REPL, 1)
- if cm == c:
+ ret = re.search(SECTION_1_ORIG, c)
+ if ret == None:
print("'{:s}' not found".format(SECTION_1_ORIG))
sys.exit(2)
+ variable = ret.group(0).split()[0]
+ cm = re.sub(SECTION_1_ORIG, variable + SECTION_1_REPL + variable + "))", c)
+ if cm == c:
+ print("'{:s}' could not change".format(SECTION_1_ORIG))
+ sys.exit(2)
c = cm
cm = c.replace(SECTION_2_ORIG, SECTION_2_REPL, 1)
if cm == c:
diff --git a/app/btmesh/btmesh.properties b/app/btmesh/btmesh.properties
index 33fdb074181..d3b20420129 100644
--- a/app/btmesh/btmesh.properties
+++ b/app/btmesh/btmesh.properties
@@ -2,8 +2,8 @@
id=com.silabs.stack.btMesh
label=Bluetooth Mesh SDK
description=Bluetooth Mesh Software Development Kit
-version=5.0.1.0
-prop.subLabel=Bluetooth\\ Mesh\\ 5.0.1
+version=5.0.2.0
+prop.subLabel=Bluetooth\\ Mesh\\ 5.0.2
# Default compatibility of the BT Mesh SDK (This is needed for the documentation only)
prop.boardCompatibility=.*
diff --git a/app/btmesh/btmesh_internal_demos.xml b/app/btmesh/btmesh_internal_demos.xml
index 77cf9c8c6f6..8fae64bb554 100644
--- a/app/btmesh/btmesh_internal_demos.xml
+++ b/app/btmesh/btmesh_internal_demos.xml
@@ -6,11 +6,11 @@
-
+
-
+
Friend example for IOP test. This node acts as a friend for the low power node and caches messages sent to it when the low power node is sleeping.
@@ -18,11 +18,11 @@
-
+
-
+
Friend example for IOP test. This node acts as a friend for the low power node and caches messages sent to it when the low power node is sleeping.
@@ -30,11 +30,11 @@
-
+
-
+
Friend example for IOP test. This node acts as a friend for the low power node and caches messages sent to it when the low power node is sleeping.
@@ -42,11 +42,11 @@
-
+
-
+
Friend example for IOP test. This node acts as a friend for the low power node and caches messages sent to it when the low power node is sleeping.
@@ -54,11 +54,11 @@
-
+
-
+
Low power node example for IOP test. This node acts as a typical low power device and sleeps most of the time. It needs a friend node to cache messages and forward them when polled.
@@ -66,11 +66,11 @@
-
+
-
+
Low power node example for IOP test. This node acts as a typical low power device and sleeps most of the time. It needs a friend node to cache messages and forward them when polled.
@@ -78,11 +78,11 @@
-
+
-
+
Low power node example for IOP test. This node acts as a typical low power device and sleeps most of the time. It needs a friend node to cache messages and forward them when polled.
@@ -90,11 +90,11 @@
-
+
-
+
Low power node example for IOP test. This node acts as a typical low power device and sleeps most of the time. It needs a friend node to cache messages and forward them when polled.
@@ -102,11 +102,11 @@
-
+
-
+
Low power node example for IOP test. This node acts as a typical low power device and sleeps most of the time. It needs a friend node to cache messages and forward them when polled.
@@ -114,11 +114,11 @@
-
+
-
+
Low power node example for IOP test. This node acts as a typical low power device and sleeps most of the time. It needs a friend node to cache messages and forward them when polled.
@@ -126,11 +126,11 @@
-
+
-
+
Proxy example for IOP test. This node forwards/relays messages between GATT and advertising bearers in the network.
@@ -138,11 +138,11 @@
-
+
-
+
Proxy example for IOP test. This node forwards/relays messages between GATT and advertising bearers in the network.
@@ -150,11 +150,11 @@
-
+
-
+
Proxy example for IOP test. This node forwards/relays messages between GATT and advertising bearers in the network.
@@ -162,11 +162,11 @@
-
+
-
+
Proxy example for IOP test. This node forwards/relays messages between GATT and advertising bearers in the network.
@@ -174,11 +174,11 @@
-
+
-
+
Proxy example for IOP test. This node forwards/relays messages between GATT and advertising bearers in the network.
@@ -186,11 +186,11 @@
-
+
-
+
Relay example for IOP test. This node acts as a relay, i.e. if a node is out of range for another node, it relays messages between the two, provided the relay node is in range for both.
@@ -198,11 +198,11 @@
-
+
-
+
Relay example for IOP test. This node acts as a relay, i.e. if a node is out of range for another node, it relays messages between the two, provided the relay node is in range for both.
@@ -210,11 +210,11 @@
-
+
-
+
Relay example for IOP test. This node acts as a relay, i.e. if a node is out of range for another node, it relays messages between the two, provided the relay node is in range for both.
@@ -222,11 +222,11 @@
-
+
-
+
Relay example for IOP test. This node acts as a relay, i.e. if a node is out of range for another node, it relays messages between the two, provided the relay node is in range for both.
@@ -234,11 +234,11 @@
-
+
-
+
Relay example for IOP test. This node acts as a relay, i.e. if a node is out of range for another node, it relays messages between the two, provided the relay node is in range for both.
@@ -246,10 +246,10 @@
-
+
-
+
diff --git a/app/btmesh/btmesh_production_demos.xml b/app/btmesh/btmesh_production_demos.xml
index e80343c16c1..988c3cd7739 100644
--- a/app/btmesh/btmesh_production_demos.xml
+++ b/app/btmesh/btmesh_production_demos.xml
@@ -6,11 +6,11 @@
-
+
-
+
An NCP Target C application that makes it possible for the NCP Host Controller to access the Bluetooth Mesh stack via UART. It provides access to the host layer via BGAPI and not to the link layer via HCI. Contains models from BT mesh specification 1.1.
@@ -18,11 +18,11 @@
-
+
-
+
An NCP Target C application that makes it possible for the NCP Host Controller to access the Bluetooth Mesh stack via UART. It provides access to the host layer via BGAPI and not to the link layer via HCI. Contains models from BT mesh specification 1.1.
@@ -30,11 +30,11 @@
-
+
-
+
An NCP Target C application that makes it possible for the NCP Host Controller to access the Bluetooth Mesh stack via UART. It provides access to the host layer via BGAPI and not to the link layer via HCI. Contains models from BT mesh specification 1.1.
@@ -42,11 +42,11 @@
-
+
-
+
An NCP Target C application that makes it possible for the NCP Host Controller to access the Bluetooth Mesh stack via UART. It provides access to the host layer via BGAPI and not to the link layer via HCI. Contains models from BT mesh specification 1.1.
@@ -54,11 +54,11 @@
-
+
-
+
An NCP Target C application that makes it possible for the NCP Host Controller to access the Bluetooth Mesh stack via UART. It provides access to the host layer via BGAPI and not to the link layer via HCI. Contains models from BT mesh specification 1.1.
@@ -66,11 +66,11 @@
-
+
-
+
An NCP Target C application that makes it possible for the NCP Host Controller to access the Bluetooth Mesh stack via UART. It provides access to the host layer via BGAPI and not to the link layer via HCI. Contains models from BT mesh specification 1.1.
@@ -78,11 +78,11 @@
-
+
-
+
An NCP Target C application that makes it possible for the NCP Host Controller to access the Bluetooth Mesh stack via UART. It provides access to the host layer via BGAPI and not to the link layer via HCI. Contains models from BT mesh specification 1.1.
@@ -90,11 +90,11 @@
-
+
-
+
An NCP Target C application that makes it possible for the NCP Host Controller to access the Bluetooth Mesh stack via UART. It provides access to the host layer via BGAPI and not to the link layer via HCI. Contains models from BT mesh specification 1.1.
@@ -102,11 +102,11 @@
-
+
-
+
An NCP Target C application that makes it possible for the NCP Host Controller to access the Bluetooth Mesh stack via UART. It provides access to the host layer via BGAPI and not to the link layer via HCI. Contains models from BT mesh specification 1.1.
@@ -114,11 +114,11 @@
-
+
-
+
An NCP Target C application that makes it possible for the NCP Host Controller to access the Bluetooth Mesh stack via UART. It provides access to the host layer via BGAPI and not to the link layer via HCI. Contains models from BT mesh specification 1.1.
@@ -126,11 +126,11 @@
-
+
-
+
An NCP Target C application that makes it possible for the NCP Host Controller to access the Bluetooth Mesh stack via UART. It provides access to the host layer via BGAPI and not to the link layer via HCI. Contains models from BT mesh specification 1.1.
@@ -138,11 +138,11 @@
-
+
-
+
An NCP Target C application that makes it possible for the NCP Host Controller to access the Bluetooth Mesh stack via UART. It provides access to the host layer via BGAPI and not to the link layer via HCI. Contains models from BT mesh specification 1.1.
@@ -150,11 +150,11 @@
-
+
-
+
An NCP Target C application that makes it possible for the NCP Host Controller to access the Bluetooth Mesh stack via UART. It provides access to the host layer via BGAPI and not to the link layer via HCI. Contains models from BT mesh specification 1.1.
@@ -162,11 +162,11 @@
-
+
-
+
An NCP Target C application that makes it possible for the NCP Host Controller to access the Bluetooth Mesh stack via UART. It provides access to the host layer via BGAPI and not to the link layer via HCI. Contains models from BT mesh specification 1.1.
@@ -174,11 +174,11 @@
-
+
-
+
An NCP Target C application that makes it possible for the NCP Host Controller to access the Bluetooth Mesh stack via UART. It provides access to the host layer via BGAPI and not to the link layer via HCI. Contains models from BT mesh specification 1.1.
@@ -186,11 +186,11 @@
-
+
-
+
An NCP Target C application that makes it possible for the NCP Host Controller to access the Bluetooth Mesh stack via UART. It provides access to the host layer via BGAPI and not to the link layer via HCI. Contains models from BT mesh specification 1.1.
@@ -198,11 +198,11 @@
-
+
-
+
An NCP Target C application that makes it possible for the NCP Host Controller to access the Bluetooth Mesh stack via UART. It provides access to the host layer via BGAPI and not to the link layer via HCI. Contains models from BT mesh specification 1.1.
@@ -210,11 +210,11 @@
-
+
-
+
An NCP Target C application that makes it possible for the NCP Host Controller to access the Bluetooth Mesh stack via UART. It provides access to the host layer via BGAPI and not to the link layer via HCI. Contains models from BT mesh specification 1.1.
@@ -222,11 +222,11 @@
-
+
-
+
An NCP Target C application that makes it possible for the NCP Host Controller to access the Bluetooth Mesh stack via UART. It provides access to the host layer via BGAPI and not to the link layer via HCI.
@@ -234,11 +234,11 @@
-
+
-
+
Demonstrates the bare minimum needed for an NCP Target C application. This example is recommended for EFR32xG22, which has limited RAM and flash, and therefore some of the stack classes are disabled by default.
@@ -246,11 +246,11 @@
-
+
-
+
Demonstrates the bare minimum needed for an NCP Target C application. This example is recommended for EFR32xG22, which has limited RAM and flash, and therefore some of the stack classes are disabled by default.
@@ -258,11 +258,11 @@
-
+
-
+
Demonstrates the bare minimum needed for an NCP Target C application. This example is recommended for EFR32xG22, which has limited RAM and flash, and therefore some of the stack classes are disabled by default.
@@ -270,11 +270,11 @@
-
+
-
+
Demonstrates the bare minimum needed for an NCP Target C application. This example is recommended for EFR32xG22, which has limited RAM and flash, and therefore some of the stack classes are disabled by default.
@@ -282,11 +282,11 @@
-
+
-
+
Demonstrates the bare minimum needed for an NCP Target C application. This example is recommended for EFR32xG22, which has limited RAM and flash, and therefore some of the stack classes are disabled by default.
@@ -294,11 +294,11 @@
-
+
-
+
Demonstrates the bare minimum needed for an NCP Target C application. This example is recommended for EFR32xG22, which has limited RAM and flash, and therefore some of the stack classes are disabled by default.
@@ -306,11 +306,11 @@
-
+
-
+
An NCP Target C application that makes it possible for the NCP Host Controller to access the Bluetooth Mesh stack via UART. It provides access to the host layer via BGAPI and not to the link layer via HCI.
@@ -318,11 +318,11 @@
-
+
-
+
An NCP Target C application that makes it possible for the NCP Host Controller to access the Bluetooth Mesh stack via UART. It provides access to the host layer via BGAPI and not to the link layer via HCI.
@@ -330,11 +330,11 @@
-
+
-
+
Demonstrates the bare minimum needed for an NCP Target C application. This example is recommended for EFR32xG22, which has limited RAM and flash, and therefore some of the stack classes are disabled by default.
@@ -342,11 +342,11 @@
-
+
-
+
Demonstrates the bare minimum needed for an NCP Target C application. This example is recommended for EFR32xG22, which has limited RAM and flash, and therefore some of the stack classes are disabled by default.
@@ -354,11 +354,11 @@
-
+
-
+
Demonstrates the bare minimum needed for an NCP Target C application. This example is recommended for EFR32xG22, which has limited RAM and flash, and therefore some of the stack classes are disabled by default.
@@ -366,11 +366,11 @@
-
+
-
+
Certificate generating firmware example. Software is generating the device EC key pair, the signing request for the device certificate, and other related data. The generated data can be read out by the Central Authority.
@@ -378,11 +378,11 @@
-
+
-
+
Certificate generating firmware example. Software is generating the device EC key pair, the signing request for the device certificate, and other related data. The generated data can be read out by the Central Authority.
@@ -390,11 +390,11 @@
-
+
-
+
Certificate generating firmware example. Software is generating the device EC key pair, the signing request for the device certificate, and other related data. The generated data can be read out by the Central Authority.
@@ -402,11 +402,11 @@
-
+
-
+
Certificate generating firmware example. Software is generating the device EC key pair, the signing request for the device certificate, and other related data. The generated data can be read out by the Central Authority.
@@ -414,11 +414,11 @@
-
+
-
+
Certificate generating firmware example. Software is generating the device EC key pair, the signing request for the device certificate, and other related data. The generated data can be read out by the Central Authority.
@@ -426,11 +426,11 @@
-
+
-
+
Certificate generating firmware example. Software is generating the device EC key pair, the signing request for the device certificate, and other related data. The generated data can be read out by the Central Authority.
@@ -438,11 +438,11 @@
-
+
-
+
Certificate generating firmware example. Software is generating the device EC key pair, the signing request for the device certificate, and other related data. The generated data can be read out by the Central Authority.
@@ -450,11 +450,11 @@
-
+
-
+
Certificate generating firmware example. Software is generating the device EC key pair, the signing request for the device certificate, and other related data. The generated data can be read out by the Central Authority.
@@ -462,11 +462,11 @@
-
+
-
+
Certificate generating firmware example. Software is generating the device EC key pair, the signing request for the device certificate, and other related data. The generated data can be read out by the Central Authority.
@@ -474,11 +474,11 @@
-
+
-
+
Certificate generating firmware example. Software is generating the device EC key pair, the signing request for the device certificate, and other related data. The generated data can be read out by the Central Authority.
@@ -486,11 +486,11 @@
-
+
-
+
Certificate generating firmware example. Software is generating the device EC key pair, the signing request for the device certificate, and other related data. The generated data can be read out by the Central Authority.
@@ -498,11 +498,11 @@
-
+
-
+
Certificate generating firmware example. Software is generating the device EC key pair, the signing request for the device certificate, and other related data. The generated data can be read out by the Central Authority.
@@ -510,11 +510,11 @@
-
+
-
+
Certificate generating firmware example. Software is generating the device EC key pair, the signing request for the device certificate, and other related data. The generated data can be read out by the Central Authority.
@@ -522,227 +522,227 @@
-
+
-
+
- Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Updating nodes and monitoring the progress of the firmware update.
+ Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Target nodes and monitoring the progress of the firmware update.
-
+
-
+
- Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Updating nodes and monitoring the progress of the firmware update.
+ Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Target nodes and monitoring the progress of the firmware update.
-
+
-
+
- Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Updating nodes and monitoring the progress of the firmware update.
+ Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Target nodes and monitoring the progress of the firmware update.
-
+
-
+
- Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Updating nodes and monitoring the progress of the firmware update.
+ Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Target nodes and monitoring the progress of the firmware update.
-
+
-
+
- Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Updating nodes and monitoring the progress of the firmware update.
+ Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Target nodes and monitoring the progress of the firmware update.
-
+
-
+
- Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Updating nodes and monitoring the progress of the firmware update.
+ Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Target nodes and monitoring the progress of the firmware update.
-
+
-
+
- Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Updating nodes and monitoring the progress of the firmware update.
+ Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Target nodes and monitoring the progress of the firmware update.
-
+
-
+
- Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Updating nodes and monitoring the progress of the firmware update.
+ Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Target nodes and monitoring the progress of the firmware update.
-
+
-
+
- Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Updating nodes and monitoring the progress of the firmware update.
+ Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Target nodes and monitoring the progress of the firmware update.
-
+
-
+
- Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Updating nodes and monitoring the progress of the firmware update.
+ Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Target nodes and monitoring the progress of the firmware update.
-
+
-
+
- Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Updating nodes and monitoring the progress of the firmware update.
+ Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Target nodes and monitoring the progress of the firmware update.
-
+
-
+
- Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Updating nodes and monitoring the progress of the firmware update.
+ Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Target nodes and monitoring the progress of the firmware update.
-
+
-
+
- Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Updating nodes and monitoring the progress of the firmware update.
+ Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Target nodes and monitoring the progress of the firmware update.
-
+
-
+
- Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Updating nodes and monitoring the progress of the firmware update.
+ Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Target nodes and monitoring the progress of the firmware update.
-
+
-
+
- Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Updating nodes and monitoring the progress of the firmware update.
+ Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Target nodes and monitoring the progress of the firmware update.
-
+
-
+
- Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Updating nodes and monitoring the progress of the firmware update.
+ Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Target nodes and monitoring the progress of the firmware update.
-
+
-
+
- Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Updating nodes and monitoring the progress of the firmware update.
+ Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Target nodes and monitoring the progress of the firmware update.
-
+
-
+
- Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Updating nodes and monitoring the progress of the firmware update.
+ Demonstrates the Firmware Distributor role based on the BT Mesh Model specification. Distributor is responsible for delivering new firmware images to the Target nodes and monitoring the progress of the firmware update.
-
+
-
+
An out-of-the-box software demo where the LEDs of the mainboard can be switched on and off, and their lighting intensity, hue, and saturation can be set. The example also tries to establish friendship as a Friend node.
@@ -750,11 +750,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the mainboard can be switched on and off, and their lighting intensity, hue, and saturation can be set. The example also tries to establish friendship as a Friend node.
@@ -762,11 +762,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the mainboard can be switched on and off, and their lighting intensity, hue, and saturation can be set. The example also tries to establish friendship as a Friend node.
@@ -774,11 +774,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the mainboard can be switched on and off, and their lighting intensity, hue, and saturation can be set. The example also tries to establish friendship as a Friend node.
@@ -786,11 +786,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the mainboard can be switched on and off, and their lighting intensity, hue, and saturation can be set. The example also tries to establish friendship as a Friend node.
@@ -798,11 +798,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the Thunderboard Sense 2 can be switched on and off, and their lighting intensity, hue, and saturation can be set. The example also tries to establish friendship as a Friend node.
@@ -810,11 +810,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the mainboard can be switched on and off, and their lighting intensity, hue, and saturation can be set. The example also tries to establish friendship as a Friend node.
@@ -822,11 +822,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the mainboard can be switched on and off, and their lighting intensity, hue, and saturation can be set. The example also tries to establish friendship as a Friend node.
@@ -834,11 +834,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the mainboard can be switched on and off, and their lighting intensity, hue, and saturation can be set. The example also tries to establish friendship as a Friend node.
@@ -846,11 +846,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the mainboard can be switched on and off, and their lighting intensity, hue, and saturation can be set. The example also tries to establish friendship as a Friend node.
@@ -858,11 +858,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the mainboard can be switched on and off, and their lighting intensity, hue, and saturation can be set. The example also tries to establish friendship as a Friend node.
@@ -870,11 +870,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the mainboard can be switched on and off, and their lighting intensity, hue, and saturation can be set. The example also tries to establish friendship as a Friend node.
@@ -882,11 +882,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the mainboard can be switched on and off, and their lighting intensity, hue, and saturation can be set. The example also tries to establish friendship as a Friend node.
@@ -894,11 +894,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the mainboard can be switched on and off, and their lighting intensity, hue, and saturation can be set. The example also tries to establish friendship as a Friend node.
@@ -906,11 +906,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the mainboard can be switched on and off, and their lighting intensity, hue, and saturation can be set. The example also tries to establish friendship as a Friend node.
@@ -918,11 +918,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the mainboard can be switched on and off, and their lighting intensity, hue, and saturation can be set. The example also tries to establish friendship as a Friend node.
@@ -930,11 +930,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the mainboard can be switched on and off, and their lighting intensity, hue, and saturation can be set. The example also tries to establish friendship as a Friend node.
@@ -942,11 +942,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the mainboard can be switched on and off, and their lighting intensity, hue, and saturation can be set. The example also tries to establish friendship as a Friend node.
@@ -954,11 +954,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the mainboard can be switched on and off, and their lighting intensity, hue, and saturation can be set. The example also tries to establish friendship as a Friend node.
@@ -966,11 +966,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the mainboard can be switched on and off, and their lighting intensity, hue, and saturation can be set. The example also tries to establish friendship as a Friend node.
@@ -978,11 +978,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the mainboard can be switched on and off, and their lighting intensity, hue, and saturation can be set. The example also tries to establish friendship as a Friend node.
@@ -990,11 +990,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the mainboard can be switched on and off, and their lighting intensity, hue, and saturation can be set. The example also tries to establish friendship as a Friend node.
@@ -1002,11 +1002,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the mainboard can be switched on and off, and their lighting intensity, color temperature, and delta UV can be set.
@@ -1014,11 +1014,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the mainboard can be switched on and off, and their lighting intensity, color temperature, and delta UV can be set.
@@ -1026,11 +1026,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the mainboard can be switched on and off, and their lighting intensity, color temperature, and delta UV can be set.
@@ -1038,11 +1038,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the WSTK can be switched on and off, and their lighting intensity, color temperature, and delta UV can be set.
@@ -1050,11 +1050,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the mainboard can be switched on and off, and their lighting intensity, color temperature, and delta UV can be set.
@@ -1062,11 +1062,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the Thunderboard Sense 2 board can be switched on and off, and their lighting intensity, color temperature, and delta UV can be set.
@@ -1074,11 +1074,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the WSTK can be switched on and off, and their lighting intensity, color temperature, and delta UV can be set.
@@ -1086,11 +1086,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the WSTK can be switched on and off, and their lighting intensity, color temperature, and delta UV can be set.
@@ -1098,11 +1098,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the WSTK can be switched on and off, and their lighting intensity, color temperature, and delta UV can be set.
@@ -1110,11 +1110,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the WSTK can be switched on and off, and their lighting intensity, color temperature, and delta UV can be set.
@@ -1122,11 +1122,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the WSTK can be switched on and off, and their lighting intensity, color temperature, and delta UV can be set.
@@ -1134,11 +1134,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the WSTK can be switched on and off, and their lighting intensity, color temperature, and delta UV can be set.
@@ -1146,11 +1146,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the WSTK can be switched on and off, and their lighting intensity, color temperature, and delta UV can be set.
@@ -1158,11 +1158,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the WSTK can be switched on and off, and their lighting intensity, color temperature, and delta UV can be set.
@@ -1170,11 +1170,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the WSTK can be switched on and off, and their lighting intensity, color temperature, and delta UV can be set.
@@ -1182,11 +1182,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the WSTK can be switched on and off, and their lighting intensity, color temperature, and delta UV can be set.
@@ -1194,11 +1194,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the WSTK can be switched on and off, and their lighting intensity, color temperature, and delta UV can be set.
@@ -1206,11 +1206,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the WSTK can be switched on and off, and their lighting intensity, color temperature, and delta UV can be set.
@@ -1218,11 +1218,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the WSTK can be switched on and off, and their lighting intensity, color temperature, and delta UV can be set.
@@ -1230,11 +1230,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the WSTK can be switched on and off, and their lighting intensity, color temperature, and delta UV can be set.
@@ -1242,11 +1242,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the WSTK can be switched on and off, and their lighting intensity, color temperature, and delta UV can be set.
@@ -1254,11 +1254,11 @@
-
+
-
+
An out-of-the-box software demo where the LEDs of the WSTK can be switched on and off, and their lighting intensity, color temperature, and delta UV can be set.
@@ -1266,11 +1266,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1278,11 +1278,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1290,11 +1290,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1302,11 +1302,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1314,11 +1314,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1326,11 +1326,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1338,11 +1338,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1350,11 +1350,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1362,11 +1362,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1374,11 +1374,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1386,11 +1386,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1398,11 +1398,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1410,11 +1410,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1422,11 +1422,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1434,11 +1434,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1446,11 +1446,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1458,11 +1458,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1470,11 +1470,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1482,11 +1482,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1494,11 +1494,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1506,11 +1506,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1518,11 +1518,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1530,11 +1530,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1542,11 +1542,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1554,11 +1554,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1566,11 +1566,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1578,11 +1578,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1590,11 +1590,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1602,11 +1602,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1614,11 +1614,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1626,11 +1626,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Client Model. It collects and displays sensor measurement data from remote device(s) (e.g., btmesh_soc_sensor_server).
@@ -1638,11 +1638,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature, people count, and illuminance, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1650,11 +1650,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature, people count, and illuminance, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1662,11 +1662,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature and people count, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1674,11 +1674,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature and people count, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1686,11 +1686,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature and people count, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1698,11 +1698,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature and people count, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1710,11 +1710,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature, people count, and illuminance, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1722,11 +1722,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature and people count, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1734,11 +1734,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature and people count, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1746,11 +1746,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature and people count, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1758,11 +1758,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature and people count, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1770,11 +1770,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature and people count, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1782,11 +1782,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature and people count, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1794,11 +1794,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature, people count, and illuminance, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1806,11 +1806,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature, people count, and illuminance, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1818,11 +1818,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature and people count, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1830,11 +1830,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature and people count, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1842,11 +1842,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature and people count, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1854,11 +1854,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature and people count, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1866,11 +1866,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature and people count, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1878,11 +1878,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature and people count, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1890,11 +1890,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature and people count, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1902,11 +1902,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature and people count, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1914,11 +1914,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature and people count, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1926,11 +1926,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature and people count, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1938,11 +1938,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature and people count, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1950,11 +1950,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature and people count, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1962,11 +1962,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature and people count, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1974,11 +1974,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature and people count, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1986,11 +1986,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature and people count, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -1998,11 +1998,11 @@
-
+
-
+
This example demonstrates the Bluetooth Mesh Sensor Server Model and Sensor Setup Server Model. It measures temperature and people count, and sends the measurement data to a remote device (e.g., btmesh_soc_sensor_client).
@@ -2010,11 +2010,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2022,11 +2022,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2034,11 +2034,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2046,11 +2046,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2058,11 +2058,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2070,11 +2070,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2082,11 +2082,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2094,11 +2094,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2106,11 +2106,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2118,11 +2118,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2130,11 +2130,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2142,11 +2142,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2154,11 +2154,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2166,11 +2166,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2178,11 +2178,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2190,11 +2190,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2202,11 +2202,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2214,11 +2214,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2226,11 +2226,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2238,11 +2238,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2250,11 +2250,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2262,11 +2262,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2274,11 +2274,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2286,11 +2286,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2298,11 +2298,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2310,11 +2310,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2322,11 +2322,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2334,11 +2334,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2346,11 +2346,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2358,11 +2358,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2370,11 +2370,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. It is optimized for low current consumption with disabled CLI, logging, and LCD.Push Button presses can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2382,11 +2382,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2394,11 +2394,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2406,11 +2406,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2418,11 +2418,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2430,11 +2430,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2442,11 +2442,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2454,11 +2454,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2466,11 +2466,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2478,11 +2478,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2490,11 +2490,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2502,11 +2502,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2514,11 +2514,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2526,11 +2526,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2538,11 +2538,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2550,11 +2550,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2562,11 +2562,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2574,11 +2574,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2586,11 +2586,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2598,11 +2598,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2610,11 +2610,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2622,11 +2622,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2634,11 +2634,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2646,11 +2646,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2658,11 +2658,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2670,11 +2670,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2682,11 +2682,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2694,11 +2694,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2706,11 +2706,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2718,11 +2718,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2730,11 +2730,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2742,11 +2742,11 @@
-
+
-
+
An out-of-the-box Software Demo where the device acts as a switch. Push Button presses or CLI commands can control the state, lightness, and color temperature of the LEDs and scenes on a remote device.
@@ -2754,10 +2754,10 @@
-
+
-
+
diff --git a/app/btmesh/btmesh_production_templates.xml b/app/btmesh/btmesh_production_templates.xml
index 66dd5f6fb17..9b869931cd5 100644
--- a/app/btmesh/btmesh_production_templates.xml
+++ b/app/btmesh/btmesh_production_templates.xml
@@ -8,7 +8,7 @@
-
+
@@ -23,7 +23,7 @@
-
+
@@ -38,7 +38,7 @@
-
+
@@ -53,14 +53,14 @@
-
+
-
+
@@ -68,14 +68,14 @@
-
+
-
+
@@ -83,7 +83,7 @@
-
+
@@ -98,7 +98,7 @@
-
+
@@ -113,7 +113,7 @@
-
+
@@ -128,7 +128,7 @@
-
+
@@ -143,7 +143,7 @@
-
+
@@ -158,7 +158,7 @@
-
+
@@ -173,7 +173,7 @@
-
+
@@ -188,7 +188,7 @@
-
+
@@ -203,7 +203,7 @@
-
+
@@ -218,7 +218,7 @@
-
+
@@ -233,7 +233,7 @@
-
+
@@ -248,7 +248,7 @@
-
+
@@ -263,7 +263,7 @@
-
+
@@ -278,7 +278,7 @@
-
+
@@ -293,7 +293,7 @@
-
+
@@ -308,7 +308,7 @@
-
+
@@ -323,7 +323,7 @@
-
+
@@ -338,7 +338,7 @@
-
+
@@ -353,7 +353,7 @@
-
+
@@ -368,7 +368,7 @@
-
+
@@ -383,7 +383,7 @@
-
+
@@ -398,7 +398,7 @@
-
+
@@ -413,7 +413,7 @@
-
+
@@ -428,7 +428,7 @@
-
+
@@ -443,7 +443,7 @@
-
+
@@ -458,7 +458,7 @@
-
+
@@ -473,7 +473,7 @@
-
+
@@ -488,7 +488,7 @@
-
+
@@ -503,7 +503,7 @@
-
+
diff --git a/app/btmesh/common/btmesh_blob_transfer_client/btmesh_blob_transfer_client.dcd b/app/btmesh/common/btmesh_blob_transfer_client/btmesh_blob_transfer_client.dcd
index 3e3728598a9..0eb1eeab881 100644
--- a/app/btmesh/common/btmesh_blob_transfer_client/btmesh_blob_transfer_client.dcd
+++ b/app/btmesh/common/btmesh_blob_transfer_client/btmesh_blob_transfer_client.dcd
@@ -1,6 +1,7 @@
[
{
- "name": "Main",
+ "name": "Distributor",
+ "group": "Main",
"location": "0x0000",
"sig_models" : [
{"mid":"0x1401", "name":"BLOB Transfer Client"}
diff --git a/app/btmesh/common/btmesh_blob_transfer_client/sl_btmesh_blob_transfer_client.c b/app/btmesh/common/btmesh_blob_transfer_client/sl_btmesh_blob_transfer_client.c
index 770d62d0928..6df0d578f4e 100644
--- a/app/btmesh/common/btmesh_blob_transfer_client/sl_btmesh_blob_transfer_client.c
+++ b/app/btmesh/common/btmesh_blob_transfer_client/sl_btmesh_blob_transfer_client.c
@@ -651,7 +651,7 @@ sl_btmesh_blob_transfer_client_calculate_block_size_log(uint32_t blob_size,
// so the implementation chooses the block_size_log_max to send the BLOB
// at least to a subset of servers.
// In general this should not happen in case of firmware image transfer
- // because the same kind of updating nodes are updated with the same
+ // because the same kind of target nodes are updated with the same
// firmware so their capabilities should match.
for (block_size_log = block_size_log_max;
block_size_log_min <= block_size_log;
@@ -933,7 +933,7 @@ static void sl_btmesh_blob_transfer_client_element_init(uint16_t elem_index)
static void sl_btmesh_blob_transfer_client_init(void)
{
- sl_btmesh_blob_transfer_client_element_init(BTMESH_BLOB_TRANSFER_CLIENT_MAIN);
+ sl_btmesh_blob_transfer_client_element_init(BTMESH_BLOB_TRANSFER_CLIENT_GROUP_MAIN_ELEM_INDEX);
}
// Process the return value of MBT procedure BT Mesh stack API calls in retry state
@@ -1315,7 +1315,7 @@ static void handle_query_information_complete(blob_transfer_client_t *const self
case sl_btmesh_mbt_client_mbt_transfer_mode_both:
// Push mode shall be the default if both transfer modes are supported
case sl_btmesh_mbt_client_mbt_transfer_mode_none:
- // Subset of updating nodes can participate in the BLOB transfer because
+ // Subset of target nodes can participate in the BLOB transfer because
// neither transfer mode is supported by all nodes (defaults to push)
// Note: it is not necessary to check the supported_transfer_modes in
// the event because it must have "both" value otherwise the
diff --git a/app/btmesh/common/btmesh_blob_transfer_server/btmesh_blob_transfer_server.dcd b/app/btmesh/common/btmesh_blob_transfer_server/btmesh_blob_transfer_server_inst.dcd
similarity index 86%
rename from app/btmesh/common/btmesh_blob_transfer_server/btmesh_blob_transfer_server.dcd
rename to app/btmesh/common/btmesh_blob_transfer_server/btmesh_blob_transfer_server_inst.dcd
index aeb9e6d32f9..d638ffc0ce7 100644
--- a/app/btmesh/common/btmesh_blob_transfer_server/btmesh_blob_transfer_server.dcd
+++ b/app/btmesh/common/btmesh_blob_transfer_server/btmesh_blob_transfer_server_inst.dcd
@@ -1,6 +1,7 @@
[
{
"name": "Main",
+ "group": "Main",
"location": "0x0000",
"sig_models" : [
{"mid":"0x1400", "name":"BLOB Transfer Server"}
diff --git a/app/btmesh/common/btmesh_blob_transfer_server/btmesh_blob_transfer_server_validation.lua b/app/btmesh/common/btmesh_blob_transfer_server/btmesh_blob_transfer_server_validation.lua
index 7357d49cada..2cf3067ba62 100644
--- a/app/btmesh/common/btmesh_blob_transfer_server/btmesh_blob_transfer_server_validation.lua
+++ b/app/btmesh/common/btmesh_blob_transfer_server/btmesh_blob_transfer_server_validation.lua
@@ -4,7 +4,7 @@ local LOG_LVL_WARNING = 2
local LOG_LVL_ERROR = 1
local LOG_PREFIX = "btmesh_blob_transfer_server_validation: "
local VALIDATION_PREFIX = "BLOB Transfer Server: "
-local log_level = LOG_LVL_INFO
+local log_level = LOG_LVL_WARNING
-- Status code definitions
local STATUS = {
@@ -47,15 +47,24 @@ local function report_warning(problem, target, description, quickfix)
quickfix)
end
+local function conf_name_inst(unresolved_name, inst)
+ return string.gsub(unresolved_name, "INSTANCE", string.upper(inst))
+end
+
-- Iterate over the configuration name and descriptor pairs from config_schema
-- and checks if the configuration exists (for required config names) and
-- converts it to the descriptor value type.
-- If the configuration name does not exist or type conversion is not successful
-- then error is reported in the log and on the user interface of Simplicity
-- Studio. The preprocessed config is written to preprocessed_config parameter.
-local function preprocess_config(preprocessed_config, config_schema)
+local function preprocess_config(inst, preprocessed_config, config_schema)
local status = STATUS.OK
- for name, descriptor in pairs(config_schema) do
+ for unresolved_name, descriptor in pairs(config_schema) do
+ -- The INSTANCE shall be substituted in BLOB Transfer Server component
+ -- instance configuration names only however the INSTANCE keyword is
+ -- present in these names only. So the substitution can be performed
+ -- for each configuration name.
+ local name = conf_name_inst(unresolved_name, inst)
if slc.config(name) == nil then
if descriptor.required then
local problem = "Configuration missing"
@@ -116,29 +125,36 @@ local function chunk_size_to_message_count(chunk_size)
end
end
-local min_block_size_log = "SL_BTMESH_BLOB_TRANSFER_SERVER_MIN_BLOCK_SIZE_LOG_CFG_VAL"
-local max_block_size_log = "SL_BTMESH_BLOB_TRANSFER_SERVER_MAX_BLOCK_SIZE_LOG_CFG_VAL"
-local max_chunks_per_block = "SL_BTMESH_BLOB_TRANSFER_SERVER_MAX_CHUNKS_PER_BLOCK_CFG_VAL"
-local max_chunk_size = "SL_BTMESH_BLOB_TRANSFER_SERVER_MAX_CHUNK_SIZE_CFG_VAL"
-local push_mode = "SL_BTMESH_BLOB_TRANSFER_SERVER_PUSH_MODE_CFG_VAL"
-local pull_mode = "SL_BTMESH_BLOB_TRANSFER_SERVER_PULL_MODE_CFG_VAL"
-local pull_chunk_request_cnt = "SL_BTMESH_BLOB_TRANSFER_SERVER_PULL_CHUNK_REQUEST_CNT_CFG_VAL"
-local lpn_mode = "SL_BTMESH_BLOB_TRANSFER_SERVER_LPN_MODE_CFG_VAL"
+local min_block_size_log = "SL_BTMESH_BLOB_TRANSFER_SERVER_INSTANCE_MIN_BLOCK_SIZE_LOG_CFG_VAL"
+local max_block_size_log = "SL_BTMESH_BLOB_TRANSFER_SERVER_INSTANCE_MAX_BLOCK_SIZE_LOG_CFG_VAL"
+local max_chunks_per_block = "SL_BTMESH_BLOB_TRANSFER_SERVER_INSTANCE_MAX_CHUNKS_PER_BLOCK_CFG_VAL"
+local max_chunk_size = "SL_BTMESH_BLOB_TRANSFER_SERVER_INSTANCE_MAX_CHUNK_SIZE_CFG_VAL"
+local push_mode = "SL_BTMESH_BLOB_TRANSFER_SERVER_INSTANCE_PUSH_MODE_CFG_VAL"
+local pull_mode = "SL_BTMESH_BLOB_TRANSFER_SERVER_INSTANCE_PULL_MODE_CFG_VAL"
+local pull_chunk_request_cnt = "SL_BTMESH_BLOB_TRANSFER_SERVER_INSTANCE_PULL_CHUNK_REQUEST_CNT_CFG_VAL"
+local lpn_mode = "SL_BTMESH_BLOB_TRANSFER_SERVER_INSTANCE_LPN_MODE_CFG_VAL"
local lpn_min_queue_length = "SL_BTMESH_LPN_MIN_QUEUE_LENGTH_CFG_VAL"
local config_schema = {
- [min_block_size_log] = {value_type = "number", required = true},
- [max_block_size_log] = {value_type = "number", required = true},
- [max_chunks_per_block] = {value_type = "number", required = true},
- [max_chunk_size] = {value_type = "number", required = true},
- [push_mode] = {value_type = "boolean", required = true},
- [pull_mode] = {value_type = "boolean", required = true},
- [pull_chunk_request_cnt] = {value_type = "number", required = true},
- [lpn_mode] = {value_type = "boolean", required = true},
- [lpn_min_queue_length] = {value_type = "number", required = false}
+ [min_block_size_log] = {value_type = "number", required = true},
+ [max_block_size_log] = {value_type = "number", required = true},
+ [max_chunks_per_block] = {value_type = "number", required = true},
+ [max_chunk_size] = {value_type = "number", required = true},
+ [push_mode] = {value_type = "boolean", required = true},
+ [pull_mode] = {value_type = "boolean", required = true},
+ [pull_chunk_request_cnt] = {value_type = "number", required = true},
+ [lpn_mode] = {value_type = "boolean", required = true},
+ [lpn_min_queue_length] = {value_type = "number", required = false}
}
-local config = {}
+local btmesh_blob_transfer_server_component = slc.component("btmesh_blob_transfer_server")
+local btmesh_blob_transfer_server_instances = btmesh_blob_transfer_server_component.instances
+
+-- The instances are in a lua set (table with string key and boolean value)
+for inst, exists in pairs(btmesh_blob_transfer_server_instances) do
+ log_info("BLOB Transfer Server instance: " .. inst)
+end
+
local lpn_component_selected = slc.is_selected("btmesh_lpn")
if lpn_component_selected then
@@ -146,85 +162,97 @@ if lpn_component_selected then
config_schema[lpn_min_queue_length].required = true
end
-local status = preprocess_config(config, config_schema)
-
-if status == STATUS.OK then
- -- It is guaranteed that all required config parameters exist and these are
- -- converted to the proper type
-
- for name, value in pairs(config) do
- -- Log the converted configuration parameters
- log_info(name .. ": " .. tostring(value) .. " (type: " .. type(value) .. ")")
- end
-
- if config[max_block_size_log] < config[min_block_size_log] then
- local problem = "Block size configuration invalid"
- local description =
- string.format("Min Block Size Log (%i) shall not be greater than " ..
- "Max Block Size Log (%i)",
- config[min_block_size_log],
- config[max_block_size_log])
- report_error(problem,
- validation.target_for_defines({min_block_size_log,
- max_block_size_log}),
- description,
- nil)
- end
+for inst, exists in pairs(btmesh_blob_transfer_server_instances) do
+ local config = {}
+ local status = preprocess_config(inst, config, config_schema)
- if (config[max_chunks_per_block] * config[max_chunk_size]) < (2 ^ config[max_block_size_log]) then
- local problem = "Block and chunk configuration inconsistent"
- local description =
- string.format("Max Block Size (%i) shall not be greater than product of " ..
- "max chunk size (%i) and max number of chunks per block (%i)",
- 2 ^ config[max_block_size_log],
- config[max_chunk_size],
- config[max_chunks_per_block])
- report_error(problem,
- validation.target_for_defines({max_block_size_log,
- max_chunk_size,
- max_chunks_per_block}),
- description,
- nil)
- end
+ if status == STATUS.OK then
+ -- It is guaranteed that all required config parameters exist and these are
+ -- converted to the proper type
+ for name, value in pairs(config) do
+ -- Log the converted configuration parameters
+ log_info(name .. ": " .. tostring(value) .. " (type: " .. type(value) .. ")")
+ end
- if config[lpn_mode] ~= lpn_component_selected then
- local problem = "LPN mode and LPN component consistency issue"
- local description = "The LPN mode configuration option shall be turned on " ..
- "if and only if the Low Power Node component is present"
- report_warning(problem,
- validation.target_for_defines({lpn_mode}),
- description,
- nil)
- elseif config[lpn_mode] then
- -- LPN mode is turned on and the LPN component exists in the project
+ local min_block_size_log_inst = conf_name_inst(min_block_size_log, inst)
+ local max_block_size_log_inst = conf_name_inst(max_block_size_log, inst)
+ local max_chunks_per_block_inst = conf_name_inst(max_chunks_per_block, inst)
+ local max_chunk_size_inst = conf_name_inst(max_chunk_size, inst)
+ local push_mode_inst = conf_name_inst(push_mode, inst)
+ local pull_mode_inst = conf_name_inst(pull_mode, inst)
+ local pull_chunk_request_cnt_inst = conf_name_inst(pull_chunk_request_cnt, inst)
+ local lpn_mode_inst = conf_name_inst(lpn_mode, inst)
- if not config[pull_mode] then
- local problem = "Transfer mode inconsistency"
- local description = "Pull transfer mode is recommended for low power nodes"
- report_warning(problem,
- validation.target_for_defines({push_mode, pull_mode}),
- description,
- nil)
+ if config[max_block_size_log_inst] < config[min_block_size_log_inst] then
+ local problem = "Block size configuration invalid"
+ local description =
+ string.format("Min Block Size Log (%i) shall not be greater than " ..
+ "Max Block Size Log (%i)",
+ config[min_block_size_log_inst],
+ config[max_block_size_log_inst])
+ report_error(problem,
+ validation.target_for_defines({min_block_size_log_inst,
+ max_block_size_log_inst}),
+ description,
+ nil)
end
- local chunk_message_count = chunk_size_to_message_count(config[max_chunk_size])
- if (config[lpn_min_queue_length] < config[pull_chunk_request_cnt] * chunk_message_count)
- and config[pull_mode] then
- local problem = "Requested chunks greater than friend queue size"
+ if (config[max_chunks_per_block_inst] * config[max_chunk_size_inst])
+ < (2 ^ config[max_block_size_log_inst]) then
+ local problem = "Block and chunk configuration inconsistent"
local description =
- string.format("The %i max chunk size fits into %i message and " ..
- "%i chunks requested at the same time which does " ..
- "not fit into min lpn queue size (%i message)",
- config[max_chunk_size],
- chunk_message_count,
- config[pull_chunk_request_cnt],
- config[lpn_min_queue_length])
+ string.format("Max Block Size (%i) shall not be greater than product of " ..
+ "max chunk size (%i) and max number of chunks per block (%i)",
+ 2 ^ config[max_block_size_log_inst],
+ config[max_chunk_size_inst],
+ config[max_chunks_per_block_inst])
report_error(problem,
- validation.target_for_defines({max_chunk_size,
- pull_chunk_request_cnt,
- lpn_min_queue_length}),
+ validation.target_for_defines({max_block_size_log_inst,
+ max_chunk_size_inst,
+ max_chunks_per_block_inst}),
description,
nil)
end
+
+ if config[lpn_mode_inst] ~= lpn_component_selected then
+ local problem = "LPN mode and LPN component consistency issue"
+ local description = "The LPN mode configuration option shall be turned on " ..
+ "if and only if the Low Power Node component is present"
+ report_warning(problem,
+ validation.target_for_defines({lpn_mode_inst}),
+ description,
+ nil)
+ elseif config[lpn_mode_inst] then
+ -- LPN mode is turned on and the LPN component exists in the project
+
+ if not config[pull_mode_inst] then
+ local problem = "Transfer mode inconsistency"
+ local description = "Pull transfer mode is recommended for low power nodes"
+ report_warning(problem,
+ validation.target_for_defines({push_mode_inst, pull_mode_inst}),
+ description,
+ nil)
+ end
+
+ local chunk_message_count_inst = chunk_size_to_message_count(config[max_chunk_size_inst])
+ if (config[lpn_min_queue_length] < config[pull_chunk_request_cnt_inst] * chunk_message_count_inst)
+ and config[pull_mode] then
+ local problem = "Requested chunks greater than friend queue size"
+ local description =
+ string.format("The %i max chunk size fits into %i message and " ..
+ "%i chunks requested at the same time which does " ..
+ "not fit into min lpn queue size (%i message)",
+ config[max_chunk_size_inst],
+ chunk_message_count_inst,
+ config[pull_chunk_request_cnt_inst],
+ config[lpn_min_queue_length])
+ report_warning(problem,
+ validation.target_for_defines({max_chunk_size_inst,
+ pull_chunk_request_cnt_inst,
+ lpn_min_queue_length}),
+ description,
+ nil)
+ end
+ end
end
end
\ No newline at end of file
diff --git a/app/btmesh/common/btmesh_blob_transfer_server/config/sl_btmesh_blob_transfer_server_config.h b/app/btmesh/common/btmesh_blob_transfer_server/config/sl_btmesh_blob_transfer_server_config.h
index 24d5077d0a7..63d007d8ad0 100644
--- a/app/btmesh/common/btmesh_blob_transfer_server/config/sl_btmesh_blob_transfer_server_config.h
+++ b/app/btmesh/common/btmesh_blob_transfer_server/config/sl_btmesh_blob_transfer_server_config.h
@@ -1,9 +1,9 @@
/***************************************************************************//**
* @file
- * @brief BLOB Transfer Server Configuration
+ * @brief BLOB Transfer Server Configuration Header
*******************************************************************************
* # License
- * Copyright 2021 Silicon Laboratories Inc. www.silabs.com
+ * Copyright 2023 Silicon Laboratories Inc. www.silabs.com
*******************************************************************************
*
* SPDX-License-Identifier: Zlib
@@ -33,38 +33,14 @@
// <<< Use Configuration Wizard in Context Menu >>>
-// Min Block Size Log <0x6-0x20>
-// Please note, that decreasing the minimum block size will result in increased heap usage.
-// Block states need to be monitored. The smaller the blocks, the bigger the state storage.
-// Change this value with care.
-// 0x9
-#define SL_BTMESH_BLOB_TRANSFER_SERVER_MIN_BLOCK_SIZE_LOG_CFG_VAL 0x9
-
-// Max Block Size Log <0x6-0x20>
-// Please note, that increasing the maximum block size will result in increased heap usage.
-// Blocks are cached on heap before being written into NVM.
-// Change this value with care.
-// The maximum block size shall be less than or equal to the product of max chunks per block and chunk size.
-// 0x9
-#define SL_BTMESH_BLOB_TRANSFER_SERVER_MAX_BLOCK_SIZE_LOG_CFG_VAL 0x9
-
-// Maximum of number of chunks per block <8-64:8>
-// 40
-#define SL_BTMESH_BLOB_TRANSFER_SERVER_MAX_CHUNKS_PER_BLOCK_CFG_VAL 40
-
-// Maximum chunk size <8-241:1>
-// If the max chunk size is 8 then the chunk data fits into a single BT Mesh advertisement message.
-// If the chunk data is segmented then N segments is able to transfer (N*12)-7 byte data.
-// The advantage of higher chunk size is the higher throughput in low noise environment.
-// The advantage of lower chunk size is that fewer messages are retransmitted in high noise environment due to lost chunk messages.
-// LPN only: the number of chunk messages (segments) multiplied by requested chunk count in partial block report shall fit into the friend queue.
-// 241
-#define SL_BTMESH_BLOB_TRANSFER_SERVER_MAX_CHUNK_SIZE_CFG_VAL 241
-
// Logging
// 1
#define SL_BTMESH_BLOB_TRANSFER_SERVER_LOGGING_CFG_VAL 1
+// LPN poll logging
+// 0
+#define SL_BTMESH_BLOB_TRANSFER_SERVER_INSTANCE_LPN_POLL_LOGGING_CFG_VAL 0
+
// Transfer Start user callback
// Enable/disable callback function when BLOB transfer starts.
// 1
@@ -80,63 +56,4 @@
// 1
#define SL_BTMESH_BLOB_TRANSFER_SERVER_TRANSFER_DONE_CALLBACK_CFG_VAL 1
-// Supported Transfer Modes
-
-// Push Mode
-// 1
-#define SL_BTMESH_BLOB_TRANSFER_SERVER_PUSH_MODE_CFG_VAL 1
-
-//
-
-// Pull Mode
-// 1
-#define SL_BTMESH_BLOB_TRANSFER_SERVER_PULL_MODE_CFG_VAL 1
-
-// Number of chunks requested in Block Status or Partial Block Report <1-32>
-// 4
-#define SL_BTMESH_BLOB_TRANSFER_SERVER_PULL_CHUNK_REQUEST_CNT_CFG_VAL 4
-
-// Interval, in milliseconds, between Partial Block Reports, if nothing is received <1000-30000:100>
-// 1000
-#define SL_BTMESH_BLOB_TRANSFER_SERVER_PULL_RETRY_INTERVAL_MS_CFG_VAL 1000
-
-// Number of retries sending the same Partial Block Report, before giving up <1-10>
-// 8
-#define SL_BTMESH_BLOB_TRANSFER_SERVER_PULL_RETRY_CNT_CFG_VAL 8
-
-// LPN Mode
-// Only pull transfer mode can be used on LPN nodes.
-// 0
-#define SL_BTMESH_BLOB_TRANSFER_SERVER_LPN_MODE_CFG_VAL 0
-
-// LPN high throughput mode
-// In high throughput mode the LPN node polls the friend node more frequently to increase the throughput at the expense of power consumption.
-// 1
-#define SL_BTMESH_BLOB_TRANSFER_SERVER_LPN_HIGH_THROUGHPUT_MODE_CFG_VAL 1
-
-// LPN poll delay in milliseconds <100-30000:100>
-// The delay of first LPN poll when the BLOB Transfer Server expects messages from the client after an event.
-// The major part of BLOB transfer to LPN is the waiting for the poll timeout to elapse in order to poll the friend node for BLOB Transfer messages.
-// The maximum number of messages can be transferred per polling equals to friend queue size during BLOB transfer to LPN.
-// This poll delay configuration parameter value makes the polling more frequent when BLOB Transfer messages are expected to increase the throughput.
-// The LPN poll delay shall be less than SL_BTMESH_LPN_POLL_TIMEOUT_CFG_VAL in sl_btmesh_lpn_config.h file.
-// 500
-#define SL_BTMESH_BLOB_TRANSFER_SERVER_LPN_POLL_DELAY_MS_CFG_VAL 500
-
-// LPN poll logging
-// 0
-#define SL_BTMESH_BLOB_TRANSFER_SERVER_LPN_POLL_LOGGING_CFG_VAL 0
-
-//
-//
-//
-
-//
-
-// <<< end of configuration section >>>
-
-#if SL_BTMESH_BLOB_TRANSFER_SERVER_MIN_BLOCK_SIZE_LOG_CFG_VAL > SL_BTMESH_BLOB_TRANSFER_SERVER_MAX_BLOCK_SIZE_LOG_CFG_VAL
-#error The Max Block Size Log value shall be equal to or greater than the Min Block Size Log value.
-#endif
-
#endif // SL_BTMESH_BLOB_TRANSFER_SERVER_CONFIG_H
diff --git a/app/btmesh/common/btmesh_blob_transfer_server/config/sl_btmesh_blob_transfer_server_inst_config.h b/app/btmesh/common/btmesh_blob_transfer_server/config/sl_btmesh_blob_transfer_server_inst_config.h
new file mode 100644
index 00000000000..b40f61dd209
--- /dev/null
+++ b/app/btmesh/common/btmesh_blob_transfer_server/config/sl_btmesh_blob_transfer_server_inst_config.h
@@ -0,0 +1,136 @@
+/***************************************************************************//**
+ * @file
+ * @brief BLOB Transfer Server Instance Configuration Header
+ *******************************************************************************
+ * # License
+ * Copyright 2023 Silicon Laboratories Inc. www.silabs.com
+ *******************************************************************************
+ *
+ * SPDX-License-Identifier: Zlib
+ *
+ * The licensor of this software is Silicon Laboratories Inc.
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ *
+ ******************************************************************************/
+
+#ifndef SL_BTMESH_BLOB_TRANSFER_SERVER_INSTANCE_CONFIG_H
+#define SL_BTMESH_BLOB_TRANSFER_SERVER_INSTANCE_CONFIG_H
+
+#include "sl_btmesh_dcd.h"
+
+// <<< Use Configuration Wizard in Context Menu >>>
+
+// Override Element Index
+// Default element index is provided by project or components.
+// If the default element index is not sufficient then it can be overridden by activating this option.
+#define SL_BTMESH_BLOB_TRANSFER_SERVER_INSTANCE_ELEM_INDEX_OVERRIDE_CFG_VAL 0
+
+// Element Index
+// Element index of BLOB Transfer Server model instance.
+// It has an effect only when "Override Element Index" configuration option is activated.
+// Range: 0-255, Default: 0
+// <0..255:1>
+// 0
+#define SL_BTMESH_BLOB_TRANSFER_SERVER_INSTANCE_ELEM_INDEX_CFG_VAL 0
+
+//
+
+// Min Block Size Log <0x6-0x20>
+// Please note, that decreasing the minimum block size will result in increased heap usage.
+// Block states need to be monitored. The smaller the blocks, the bigger the state storage.
+// Change this value with care.
+// 0x9
+#define SL_BTMESH_BLOB_TRANSFER_SERVER_INSTANCE_MIN_BLOCK_SIZE_LOG_CFG_VAL 0x9
+
+// Max Block Size Log <0x6-0x20>
+// Please note, that increasing the maximum block size will result in increased heap usage.
+// Blocks are cached on heap before being written into NVM.
+// Change this value with care.
+// The maximum block size shall be less than or equal to the product of max chunks per block and chunk size.
+// 0x9
+#define SL_BTMESH_BLOB_TRANSFER_SERVER_INSTANCE_MAX_BLOCK_SIZE_LOG_CFG_VAL 0x9
+
+// Maximum of number of chunks per block <8-64:8>
+// 40
+#define SL_BTMESH_BLOB_TRANSFER_SERVER_INSTANCE_MAX_CHUNKS_PER_BLOCK_CFG_VAL 40
+
+// Maximum chunk size <8-241:1>
+// If the max chunk size is 8 then the chunk data fits into a single BT Mesh advertisement message.
+// If the chunk data is segmented then N segments is able to transfer (N*12)-7 byte data.
+// The advantage of higher chunk size is the higher throughput in low noise environment.
+// The advantage of lower chunk size is that fewer messages are retransmitted in high noise environment due to lost chunk messages.
+// LPN only: the number of chunk messages (segments) multiplied by requested chunk count in partial block report shall fit into the friend queue.
+// 241
+#define SL_BTMESH_BLOB_TRANSFER_SERVER_INSTANCE_MAX_CHUNK_SIZE_CFG_VAL 241
+
+// Supported Transfer Modes
+
+// Push Mode
+// 1
+#define SL_BTMESH_BLOB_TRANSFER_SERVER_INSTANCE_PUSH_MODE_CFG_VAL 1
+
+//
+
+// Pull Mode
+// 1
+#define SL_BTMESH_BLOB_TRANSFER_SERVER_INSTANCE_PULL_MODE_CFG_VAL 1
+
+// Number of chunks requested in Block Status or Partial Block Report <1-32>
+// 4
+#define SL_BTMESH_BLOB_TRANSFER_SERVER_INSTANCE_PULL_CHUNK_REQUEST_CNT_CFG_VAL 4
+
+// Interval, in milliseconds, between Partial Block Reports, if nothing is received <1000-30000:100>
+// 1000
+#define SL_BTMESH_BLOB_TRANSFER_SERVER_INSTANCE_PULL_RETRY_INTERVAL_MS_CFG_VAL 1000
+
+// Number of retries sending the same Partial Block Report, before giving up <1-10>
+// 8
+#define SL_BTMESH_BLOB_TRANSFER_SERVER_INSTANCE_PULL_RETRY_CNT_CFG_VAL 8
+
+// LPN Mode
+// Only pull transfer mode can be used on LPN nodes.
+// 0
+#define SL_BTMESH_BLOB_TRANSFER_SERVER_INSTANCE_LPN_MODE_CFG_VAL 0
+
+// LPN high throughput mode
+// In high throughput mode the LPN node polls the friend node more frequently to increase the throughput at the expense of power consumption.
+// 1
+#define SL_BTMESH_BLOB_TRANSFER_SERVER_INSTANCE_LPN_HIGH_THROUGHPUT_MODE_CFG_VAL 1
+
+//