pythonbluetoothraspberry-pibindrfcomm

Bind Bluetooth device programmatically to rfcomm via python in


i wrote a script in python for serial communication between my M5Stack Stick C (like raduino) and the raspberry pi. all work fine. i can send "X","Y" or "Z" from raspberry py to the stick and he will reply the value (G-Force) back to the raspi! so far so good

Codes:

Python on raspy:

import serial
import time
import threading


ser = serial.Serial('/dev/rfcomm5') #init serial port
input_line = []#init input char array

def process_data(_data):
    #called every time a sream is terminated by \n 
    #and the command string is ready to use
    command = convert(_data)
    print(command)
    
def convert(s):   #convert the char list in a string
    
    new = "" #init string to append all chars from char array
    
    for x in s:     # traverse in the string  
        new += str(x)  
  
    return new    # return string  
    
def processIncomingByte(inByte):#adding incoming chars to input_line
    
    global input_line# globalize the input_line
    
    if(inByte == '\n'):#if \n is incoming, end the chararray and release process data method
        process_data(input_line)
        input_line = []#reset input_line for next incoming string
    elif(inByte == '\r'):
        pass
        
    else:#put all incoming chars in input_line 
        input_line.append(inByte)
        

while True:
    while(ser.in_waiting > 0):#while some data is waiting to read....
        processIncomingByte(ser.read())#.... process bytes whit method
    ser.write(b'X\n')
    time.sleep(0.5)

before the script work, i have to manually bind the m5Stak Stick-C over Blueman to /dev/Rfcomm5. it work just fine over GUI or Console....

but now i would like to connect the stick via python to rfcomm5 (just by know the MAC adress, will be found in a config file later on...) i startet to investigate a bit, but the more i research the more confused i am!! i read some stuff over sockets and server-client aproaches. over a seperated script and so on.... i tested this code:

from bluetooth import *

target_name = "M5-Stick-C"
target_address = None
nearby_devices = discover_devices()

for address in nearby_devices:
    if (target_name == lookup_name( address )):
        target_address = address
        break
if (target_address is not None):
    print ("found target bluetooth device with address ", target_address)
else:
    print ("could not find target bluetooth device nearby")

and indeed it found the device (just testing)! but do i realy need to make a second script/process to connect to from my script?

is the the M5stack Stick-C the server? (i think so)

im so confused about all that stuff. i coded a lot, but never whit sockets, server-client stuff. basically the communication (server/client?) works. i just need to connect the device i found in the second script via macadress to rfcomm5 (or whatever rfcomm). do i need a bluetooth socket? like in this example https://gist.github.com/kevindoran/5428612

isnt the rfcomm the socket or am i wrong?


Solution

  • There are a number of layers that are used in the communication process and depending where you tap into that stack will depend what coding you need to do. The other complication is that BlueZ (the Bluetooth stack on linux) changed how it works over recent times leaving a lot of out of date information on the internet and easy for people to get confused.

    With two Bluetooth devices, they need to establish a pairng. This is typically a one off provisioning step. This can be done with tools like Blueman or on the command line with bluetoothctl. Once you have a pairing established between your RPi and the M5Stack Stick, you shouldn't need to discover nearby devices again. Your script should just be able to connect if you tell it which device to connect to.

    The M5Stack stick is advertising as having a Serial Port Profile (SPP). This is a layer on top of rfcomm.

    There is a blog post about how this type of connection can be done with the standard Python3 installation: http://blog.kevindoran.co/bluetooth-programming-with-python-3/

    My expectation is that you will only have to do the client.py on your RPi as the M5Stack Stick is the server. You will need to know its address and which port to connect on. Might be some trial and error on the port number (1 and 3 seem to be common).

    Another library that I find helpful for SPP, is bluedot as it abstracts away some of the boilerplate code: https://bluedot.readthedocs.io/en/latest/btcommapi.html#bluetoothclient

    So in summary, my recommendation is to use the standard Python Socket library or Bluedot. This will allow you to specify the address of the device you wish to connect to in your code and the underlying libraries will take care of making the connection and setting up the serial port (as long as you have already paired the two devices).

    Example of what the above might look like with Bluedot

    from bluedot.btcomm import BluetoothClient
    from signal import pause
    from time import sleep
    
    # Callback to handle data
    def data_received(data):
        print(data)
        sleep(0.5)
        c.send("X\n")
    
    # Make connection and establish serial connection
    c = BluetoothClient("M5-Stick-C", data_received)
    # Send initial requests
    c.send("X\n")
    
    # Cause the process to sleep until data received
    pause()
    

    Example using the Python socket library:

    import socket
    from time import sleep
    
    # Device specific information
    m5stick_addr = 'xx:xx:xx:xx:xx:xx'
    port = 5 # This needs to match M5Stick setting
    
    # Establish connection and setup serial communication
    s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_STREAM, socket.BTPROTO_RFCOMM)
    s.connect((m5stick_addr, port))
    
    # Send and receive data
    while True:
        s.sendall(b'X\n')
        data = s.recv(1024)
        print(data)
        sleep(0.5)
    s.close()