0

I am using Raspberry Pi to advertise a string stored in a text file on Desktop. I am very new to the concepts of GATT services and characteristics and reading the BlueZ documentation caused me confusion.

How do I do that by modifying the BlueZ code below? I would also like to know how to do it with Bluepy.

I have also setup an ESP32 module as a BLE scanner and I want it to read the advertised string by the pi and then broadcast it. ESP32 is coded in C, how do I make it read the data broadcasted by the Pi and then broadcast the data that was being read? Is pairing compulsory before the ESP32 is allowed to read the string data from the pi?

Any help and explanation is much appreciated!

class TestAdvertisement(Advertisement):

    def __init__(self, bus, index):
        company_id =  0x0118
        type =       [0xBE, 0xAC]
        id1 =        [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
                      0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16]
        id2 =        [0x11, 0x22]
        id3 =        [0x33, 0x44]
        rssi_at_1m = [0xB3]
        feature  =   [0x00]
        Advertisement.__init__(self, bus, index, 'peripheral')
        self.add_manufacturer_data(company_id, type + id1 + id2 + id3 + rssi_at_1m + feature)

update on broadcasting string stored as hex values in a file:

I've tried the method suggested in below solution by ukbaz to broadcast a string which was stored as hex values (without prefix 0x) in a file. I did notice that the hex values were stored as integer as the string was made of digits. But what if the string has a mix of digits and alphabets? I used the same method but below error came out:

enter image description here

Also, does broadcasting the string this way allows bluetooth device to read the string and save it locally? for next step, once this string can be broadcasted successfully, my plan is to setup another pi (or perhaps an ESP32) which needs to read the string when it's broadcasting and store it in a file locally (2nd device).

2
  • 1
    read string from a file and advertise beacon are two different unrelated actions ... which one are you having problems with? – jsotola Sep 29 '20 at 16:03
  • i think i managed to read the string from a file and then convert it to hex. Now it's a matter of broadcasting that string and make it readable by another bluetooth device. essentially i want this other bluetooth device to read that string and save it locally in a file – snowmanolaf Oct 1 '20 at 8:10
2

There are a lot of parts to your question. I would recommend that you break the problem down into a series of smaller problems. Maybe:

  • Get advertisement working with hard coded values
  • Add reading values from file
  • Scanner on ESP32

Get advertisement working with hard coded values

You have mentioned that you want to use the Bluepy library. There are a number of different roles that a Bluetooth device can take. You want your device to be a beacon (broadcaster) which isn't a role that Bluepy supports so you will not be able to use that library.

The extract of code you have appears to be from the example advertisement that is in the BlueZ source code. https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/test/example-advertisement. That should work for you. There are other questions that talk about using that code to make a beacon.

Testing broadcast values

Trying to create the code on both ends of the Bluetooth link is a very difficult challenge especially if you are new to Bluetooth. I would recommend that you use some generic tools to help you so that you are only writing code for one end of the Bluetooth link at a time. Once you are confident you have one end working, then move on to the other end.

For scanning, I would recommend using the nRF Connect tool. This will allow you to see if your beacon is broadcasting what you expect.

Use Beacon Simulator to create a beacon that you can practice learning how to read the data from it.

Beacon format

GATT is not used in the creation of a beacon. GATT is when Bluetooth LE devices connect and exchange data.

For a beacon, you are creating the advertising data, which is typically either Service Data or Manufacturer Data.

Looking at your other questions, you appear to know about Eddystone beacon format. That is a good format and I would suggest that is a good one to stick with as you learn because there are examples around. Once you have that working reliably, then you can try creating your own format.

ESP32

You have said that you want an ESP32 device to be in the scanner (Observer) role. It appears there is an example in the ESP32 source code. Test it with the Beacon Simulator and see if it works.

Divide and conquer

It will be easier for people to help you if you bring smaller problems for people to answer questions on. Decide if you want to start coding the scanner or the beacon first. Use a mobile phone app for the opposite role. If things don't work, show your code and what is on the app.

Simple beacon

Your code was close apart from how you entering the UUID. An example that worked for me was:

class TestAdvertisement(Advertisement):

    def __init__(self, bus, index):
        Advertisement.__init__(self, bus, index, 'peripheral')
        company_id =   0x004C
        beacon_type = [0x02, 0x15]
        beacon_uuid = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
                       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16]
        major =       [0x11, 0x22]
        minor =       [0x33, 0x44]
        tx_power =    [0xB3]
        manu_data = beacon_type + beacon_uuid + major + minor + tx_power
        print('Using data:', manu_data)
        self.add_manufacturer_data(company_id, manu_data)

text file

If you are going read data from a text file, using a format that is easy to read might be a good option. For example json is very easy to read in Python.

An example of you data in json might be:

{
    "beacon_uuid": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
}

reading text file

Something like the following can be added to the start of the file to put the json data into the data variable that we can access later

import json
from pathlib import Path

here = Path(__file__).parent
data_file = here.joinpath('beacon_string.json')
with data_file.open() as f:
    data = json.load(f)

print('Json data:', data['beacon_uuid'], type(data['beacon_uuid']))

Beacon with data from file

This makes it a small modification to the original class to get the value from data into the beacon advertisement:

class TestAdvertisement(Advertisement):

    def __init__(self, bus, index):
        Advertisement.__init__(self, bus, index, 'peripheral')
        company_id =   0x004C
        beacon_type = [0x02, 0x15]
        beacon_uuid = data['beacon_uuid']
        major =       [0x11, 0x22]
        minor =       [0x33, 0x44]
        tx_power =    [0xB3]
        manu_data = beacon_type + beacon_uuid + major + minor + tx_power
        print('Using data:', manu_data)
        self.add_manufacturer_data(company_id, manu_data)

Strings, lists, and bytes

With Bluetooth you can only send octets (bytes that are 8 bits). This means we need to present BlueZ with a list of integers (or a bytearray). If anything isn't a list bytes, it needs to be converted into a list of bytes to be sent. And then at the other end it needs to be converted back to from bytes.

I haven't understood exactly what your original data is so I'll show a few examples that are generally useful when working with Bluetooth and python.

for big_number in [7650, -4321]:
    big_number_bytes = big_number.to_bytes(2, byteorder='little', signed=True)
    print('big number bytes:', big_number_bytes)
    big_number_int = int.from_bytes(big_number_bytes, byteorder='little', signed=True)
    print('big number int:', big_number_int)

# big number bytes: [226, 29]
# big number int: 7650
# big number bytes: [31, 239]
# big number int: -4321


hex_str_list = ['32', '36', '33', '34', 'be', 'da']
dec_value_list = []
for hex_str_item in hex_str_list:
    dec_value_list.append(int(hex_str_item, 16))
print('list of denary int values:', dec_value_list)

# list of denary int values: [50, 54, 51, 52, 190, 218]


msg_str = 'This is not a test'
byte_list = []
for letter_str in msg_str:
    byte_list.append(ord(letter_str))
print('byte list to send:', byte_list)
recv_msg = ''
for letter_byte in byte_list:
    recv_msg += chr(letter_byte)
print('Received msg:', recv_msg)

# byte list to send: [84, 104, 105, 115, 32, 105, 115, 32, 110, 111, 116, 32, 97, 32, 116, 101, 115, 116]
# Received msg: This is not a test

7
  • thanks for the feedback. I'll try my best to approach it step-by-step. I've managed to come to the point where I converted my string to hex and append them as a list before passing it to uuid variable under TestAdvertisement class and i received below error: – snowmanolaf Oct 1 '20 at 7:49
  • 1
    sorry for the mess! still learning my way around here. I've edited my question above – snowmanolaf Oct 1 '20 at 8:11
  • Updated answer based on your additional information – ukBaz Oct 1 '20 at 10:11
  • The code helps! Ive another qn, if i have a list of hexadecimals without the prefix 0x, as follow: ['32', '36', '33', '34', '35', '31', '32', '38', '36', '37', '34', '35', '36', '38', '32', '33', '35', '38', '32', '33', '35', '34', '73', '6a', '68', '64', '61'], how do I convert it into json data format that you've shown above? It seemed that the values above are tied to a single key in a dictionary? – snowmanolaf Oct 1 '20 at 15:49
  • i think i managed to assign a key to the list of hexadecimals above and write it to json. However, there was an error when I ran the code. I believe it's due to string type list instead of integer? really appreciate all the help ! – snowmanolaf Oct 1 '20 at 16:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.