1

I wrote a script in Python for serial communication between my M5Stack Stick C (like Arduino) and my Raspberry Pi. I can send "X","Y" or "Z" from the Raspberry Pi to the stick and it will reply back to the Pi with the G-Force value.

This Python code runs on the Pi:

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)

However, for the script to work, I have to manually bind the M5Stack Stick-C to /dev/rfcomm5 with Blueman. It works via GUI or console.

But now I would like to connect the stick to rfcomm5 via Python. You can assume the MAC address is known, it will be specified in a config file later on. I started to investigate a bit, but the more I research the more confused I am. I read about sockets and server-client approaches using a separate script. 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. But do I really need to make a second script/process to connect to from my script?

Is the M5stack Stick-C the server and rfcomm the socket, or is this a wrong assumption? I've coded a lot, but never with sockets or server-client, which confuses me.

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?

1 Answer 1

5

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 paring. 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: 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.

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()
Sign up to request clarification or add additional context in comments.

5 Comments

thanks for your reply! maybe i done it all wrong... is it not normal to send a request to the bluetooth device and get a reply? most examples go the way that the device(M5Stack) just send data or just receive data.... or should i not communicate whit serial over rfcomm? when the examples have a own "send" or "receive" method then i dont need to send my stuff over serial?!
ok! the example from you do the job replacing the rfcomm thing and communicate over the socket!!!!! now i just have to read the code for automatic scanning and pairing before c = BluetoothClient("M5-Stick-C", data_received). the method data_recieved is a callback method called from bluetoothClient right? thanks a lot!
There is now a bluedot and python socket example. These should be complete examples that should establish a connection and exchange data. I don't have your hardware so there could be a little bit of debug needed on your setup. Try them and let me know if you have issues
thank you very much!!!!! both examples work fine! i managed it to get it working whit bluetooth module too. the only down-side for the bluedot module is that it only works whit allready paired devices (i hope im not wrong) while the socket and the bluetooth version can work whit unpaired devices. I found another method that works very well: in /etc/rc.local add the line : rfcomm bind 0 XX:XX:XX:XX:XX:XX 1 above the exit tag. i dont know exactly how it works, but it works very well!!!! raspberry will bind it to rfcomm no matter if its binded allready or not! TY VERY MUCH!!!!!!!!!!!!!
Cool! As long as you have something working that you are happy with. One word of caution is that the rfcomm tool has been marked as deprecated so it will disappear at some point. git.kernel.org/pub/scm/bluetooth/bluez.git/commit/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.