This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
en:iot-open:practical:hardware:sut:stm32:iot_5 [2024/05/04 08:03] – [STM_IoT_5: BLE Beacon] ktokarz | en:iot-open:practical:hardware:sut:stm32:iot_5 [2024/05/04 08:41] (current) – [Steps] ktokarz | ||
---|---|---|---|
Line 6: | Line 6: | ||
</ | </ | ||
<note important> | <note important> | ||
- | To do this scenario two stations are needed, one for the beacon and one for the client device. At the time of writing this book the Bluetooth Low Energy library for STM32WB55 didn't support reading the remote advertising raw data. That's why we can implement the beacon device | + | To do this scenario two stations are needed, one for the beacon and one for the client device. At the time of writing this book the Bluetooth Low Energy library for STM32WB55 didn't support reading the remote advertising raw data. That's why we can implement the Eddystone |
</ | </ | ||
===== Prerequisites ===== | ===== Prerequisites ===== | ||
Line 21: | Line 21: | ||
==== Task to be implemented ==== | ==== Task to be implemented ==== | ||
**Task 1** Implement a program that operates as the BLE beacon which advertises itself with the link to the website. This can be done with the Eddystone beacon format. | **Task 1** Implement a program that operates as the BLE beacon which advertises itself with the link to the website. This can be done with the Eddystone beacon format. | ||
- | **Task 2** Implement the receiver of advertising frames broadcasted by the beacon device capable of displaying information on the LCD screen. | + | **Task 2** Implement the receiver of advertising frames broadcasted by the beacon device capable of displaying |
==== Start ==== | ==== Start ==== | ||
- | We need to implement both parts of the software to be able to observe the results (steps 1-4). **The Task 1** we implement in steps 1 and 2. **The Task 2** we implement in steps 3 and 4. | + | We need to implement both parts of the software to be able to observe the results (steps 1-3). **The Task 1** we implement in steps 1 and 2. **The Task 2** we implement in step 3. |
==== Steps ==== | ==== Steps ==== | ||
We can go through the lab in a few steps. In the beginning, we will write a simple program which advertises the device with the default parameters set in the BLE library. As the payload of the advertising frame, we will use the Eddystone format to send the chosen URL address. | We can go through the lab in a few steps. In the beginning, we will write a simple program which advertises the device with the default parameters set in the BLE library. As the payload of the advertising frame, we will use the Eddystone format to send the chosen URL address. | ||
Line 146: | Line 146: | ||
<code c> | <code c> | ||
#include " | #include " | ||
- | #include "BLEDevice.h" | + | #include "STM32duinoBLE.h" |
- | #include "Adafruit_LiquidCrystal.h" | + | #include "LiquidCrystal.h" |
</ | </ | ||
There can be a lot of BLE devices in the range of our receiver. We should find the one that we are interested in so we define the UUID of the Eddystone service. | There can be a lot of BLE devices in the range of our receiver. We should find the one that we are interested in so we define the UUID of the Eddystone service. | ||
<code c> | <code c> | ||
- | # | + | # |
</ | </ | ||
Our program will use objects of two classes and some variables: | Our program will use objects of two classes and some variables: | ||
<code c> | <code c> | ||
- | static BLEAdvertisedDevice* myDevice; // Class for remote BLE device | + | HCISharedMemTransportClass HCISharedMemTransport; |
- | BLEScan* pBLEScan; // Class for local scanner device | + | BLELocalDevice BLEObj(& |
- | + | BLELocalDevice& | |
- | uint8_t * advPayload; | + | |
- | char eddystoneUrl[20]; | + | |
- | String advName; | + | |
- | int advLength; | + | |
- | int advIndex; | + | |
- | int advEddystoneLength; | + | |
- | int i; | + | |
</ | </ | ||
The received information will be displayed on the LCD, so we need to configure it (similar to the scenario EMB5) | The received information will be displayed on the LCD, so we need to configure it (similar to the scenario EMB5) | ||
<code c> | <code c> | ||
- | // LCD display pins and class declaration | + | // LCD class (Constructor uses STM port numbering) |
- | #define LCD_RS 2 | + | const int rs = PC5, en = PB11, d4 = PB12, d5 = PB13, d6 = PB14, d7 = PB15; |
- | #define LCD_ENABLE 1 | + | LiquidCrystal |
- | #define LCD_D4 39 | + | |
- | #define LCD_D5 40 | + | |
- | #define LCD_D6 41 | + | |
- | #define LCD_D7 42 | + | |
- | static Adafruit_LiquidCrystal | + | |
</ | </ | ||
Line 183: | Line 171: | ||
<code c> | <code c> | ||
void setup() { | void setup() { | ||
- | // Initialise LCD | ||
lcd.begin(16, | lcd.begin(16, | ||
- | | + | |
- | lcd.print(" | + | lcd.setCursor(0, |
+ | lcd.print(" | ||
+ | lcd.setCursor(0,1); | ||
+ | lcd.print(" | ||
| | ||
- | // Initialise | + | // initialize |
- | | + | |
- | + | ||
- | // Retrieve the pointer to the scan module | + | // start first scanning for peripherals |
- | | + | |
- | + | | |
- | | + | |
- | | + | ret = BLE.scanForUuid(REMOTE_SERVICE_UUID); |
- | + | | |
- | // Start scan parameters with active scan mode | + | { |
- | pBLEScan-> | + | BLE.end(); |
- | | + | |
- | | + | } |
- | + | | |
- | | + | |
- | pBLEScan-> | + | |
} | } | ||
</ | </ | ||
- | In the loop function, we restart scanning every 5 seconds with 1 second delay. | + | In the loop function, we wait for the scanned device and display info if the Eddystone device is present. |
< | < | ||
- | The start function | + | Using STM32WB55 SoC you can't see the detailed content |
</ | </ | ||
<code c> | <code c> | ||
void loop() { | void loop() { | ||
- | | + | // check if a peripheral has been discovered |
- | | + | |
- | | + | |
- | } | + | |
- | </ | + | |
- | === Step 4 === | + | |
- | The callback function checks if the device which sent the advertising frame implements the Eddystone service. This is how we filter out all other BLE devices available in the range. | + | |
- | < | + | |
- | if we have more than one Eddystone device nearby we would have to implement additional filtering. | + | |
- | </ | + | |
- | After a successful search for a device with Eddystone service, we can display its name and proceed with displaying the content of the Eddystone field. Because the code is quite complex we present the whole callback function below. Notice that we decode chosen compressed fields only (prefix = 0x00, suffice = 0x07). You would need to decode other values for compatibility with the Eddystone format. | + | if (peripheral) { |
- | <code c> | + | |
- | class MyAdvertisedDeviceCallbacks: | + | if (peripheral.localName() == "STM32 Beacon" |
- | void onResult(BLEAdvertisedDevice advertisedDevice) { | + | |
- | // We have found a device, let's see if it contains the service we are looking for. | + | // display |
- | if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(BLEUUID(SERVICE_UUID))) { | + | |
- | + | ||
- | // Display the name of the remote | + | |
lcd.setCursor(0, | lcd.setCursor(0, | ||
- | lcd.print(advertisedDevice.getName().c_str()); | + | lcd.print(peripheral.localName()); |
- | + | | |
- | // Get the length of the payload and the pointer to it | + | |
- | advLength = advertisedDevice.getPayloadLength(); | + | lcd.setCursor(0, |
- | advPayload = advertisedDevice.getPayload(); | + | lcd.print(REMOTE_SERVICE_UUID); |
- | + | } | |
- | // Search for the Eddystone field (field ID = 0x16) | + | |
- | advIndex = 0; | + | |
- | while (advIndex< | + | |
- | | + | |
- | if (advPayload[advIndex+1]==0x16) { | + | |
- | + | ||
- | // Eddystone field found, get the length of it | + | |
- | advEddystoneLength = advPayload[advIndex]; | + | |
- | + | ||
- | // Display the Eddystone field | + | |
- | | + | |
- | + | ||
- | // Prefix decoding | + | |
- | if (advPayload[advIndex+6]==0x00) { | + | |
- | | + | |
- | } | + | |
- | + | ||
- | // ULR name | + | |
- | for(i=0; i< | + | |
- | eddystoneUrl[i]=(char)advPayload[advIndex+7+i]; | + | |
- | } | + | |
- | eddystoneUrl[i]=(char)NULL; | + | |
- | lcd.print(eddystoneUrl); | + | |
- | + | ||
- | // Suffix decoding | + | |
- | if (advPayload[advIndex+advEddystoneLength]==0x07){ | + | |
- | lcd.print(" | + | |
- | } | + | |
- | } // Eddystone field found | + | |
- | advIndex ++; | + | |
- | } // Search for Eddystone | + | |
- | } // Found our server | + | |
- | } // onResult | + | |
- | }; // MyAdvertisedDeviceCallbacks | + | |
- | </ | + | |
- | Additional filtering of remote devices can be done with their names. We can add in the " | + | |
- | <code c> | + | |
- | String name; | + | |
- | name = advertisedDevice.getName().c_str(); | + | |
- | if (name.equals(" | + | |
- | { | + | |
- | // here the internal code of the MyAdvertisedDeviceCallbacks() | + | |
} | } | ||
+ | } | ||
</ | </ | ||
- | |||
==== Result validation ==== | ==== Result validation ==== | ||
- | After the implementation of steps 1-4, you should be able to see the name of the beacon device | + | After the implementation of steps 1-3, you should be able to see the name of the beacon device |
==== Further work ==== | ==== Further work ==== | ||
You can try to implement the beacon device compatible with iBeacon. The description can be found on the website ((https:// | You can try to implement the beacon device compatible with iBeacon. The description can be found on the website ((https:// |