In this article, we would use the Raspberry Pi 4 and Samsung tablet. This is server-client connection. We create an advertise service.
Bluetooth Low Energy (BLE) has two ways of communicating. The first one is using advertisements, where a BLE peripheral device broadcasts packets to every device around it. The receiving device can then act on this information or connect to receive more information. This is essentially what Beacons do: they just transmit packets. The second way to communicate is to receive packets using a connection, where both the peripheral and central send packets. I introduce two ways to do this.
Installation
Please follow the instruction in Reference section.
sudo apt-get install bluez python3-bluez
sudo hciconfig hci0 piscan
Use Advertise Service
Advertising is by design unidirectional. A Central device can’t send any data to the Peripheral device without a connection. But a single peripheral can advertise to multiple masters in the area.
Full Server Code
import bluetooth
import time
def sendFile(client_sock):
print("sending file...")
client_sock.sendall(open("./data.csv", "rb").read())
print(open("./data.csv", "rb").read())
print("data.csv file sent")
def receiveData(client_sock):
while True:
try:
data = client_sock.recv(1024)
if not data:
break
print("Received", data)
except OSError:
print("OSError")
client_sock.close()
pass
def main(sendFile, receiveData):
server_sock = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
server_sock.bind(("", bluetooth.PORT_ANY))
server_sock.listen(1)
port = server_sock.getsockname()[1]
uuid = "94f39d29-7d6d-437d-973b-fba39e49d4ee"
bluetooth.advertise_service(server_sock, "SampleServer", service_id=uuid,
service_classes=[uuid, bluetooth.SERIAL_PORT_CLASS],
profiles=[bluetooth.SERIAL_PORT_PROFILE],
# protocols=[bluetooth.OBEX_UUID]
)
print("Waiting for connection on RFCOMM channel", port)
client_sock, client_info = server_sock.accept()
print("Accepted connection from", client_info)
time.sleep(3)
sendFile(client_sock)
# receiveData(client_sock)
print("Disconnected.")
client_sock.close()
server_sock.close()
print("All done.")
main(sendFile, receiveData)
The socket will wait for a connection. It then sends a file to client (tablet) as binary.
def sendFile(client_sock):
print("sending file...")
client_sock.sendall(open("./data.csv", "rb").read())
print(open("./data.csv", "rb").read())
print("data.csv file sent")
Dart Client Code
final flutterReactiveBle = FlutterReactiveBle();
void createCsv(fileName, filecontent) async {
var csvStr = filecontent;
print(csvStr.toString());
var path = await ExternalPath.getExternalStoragePublicDirectory(
ExternalPath.DIRECTORY_DOWNLOADS);
path = '$path/$fileName';
print("creating file...at $path");
File file = File(path);
file.writeAsString(csvStr);
print("$path CREATED");
}
Future<void> bluetoothConnection() async {
print(
'****************************** connecting... ******************************');
try {
BluetoothConnection connection = await BluetoothConnection.toAddress(foundDeviceId);
print('Connected to the device');
// sendBluetoothMessage(connection);
var fileName = "data.csv" ;
var filecontent = '';
connection.input?.listen((Uint8List data) {
print('--------------------------------------------------------------');
print('Data incoming: ${ascii.decode(data)}');
// connection.output.add(data); // Sending data
filecontent = filecontent + ascii.decode(data);
if (ascii.decode(data).contains('!')) {
connection.finish(); // Closing connection
print('Disconnecting by local host');
}
}).onDone(() {
print('FINAL CONTENT: $filecontent');
createCsv(fileName, filecontent);
print('Disconnected by remote request');
});
}
catch (exception) {
print('Cannot connect, exception occured');
}
}
void sendBluetoothMessage(BluetoothConnection connection) {
connection!.output.add(Uint8List.fromList(utf8.encode("Will says hello" + "\r\n")));
}
I have commented out the code for sending back message to server. Play around if you want. In this code, we want to concatenate the data. Because server splits the entire data to many parts. When we use the listen method which means we listen for the whole data being sent in loop. When the last part was sent, it will trigger onDone callback.
connection.input?.listen((Uint8List data) {
}).onDone(() {
print('FINAL CONTENT: $filecontent');
createCsv(fileName, filecontent);
print('Disconnected by remote request');
});
Hence, we want to put the code for creating CSV file at this callback when the whole data concatenated.
Use OBEX service built-in tablet
The method, we only send the file directly from server to tablet using OBEX Object Push service which is existing in tablet.
from bluetooth import *
from PyOBEX.client import Client
import sys
addr = sys.argv[1]
print("Searching for OBEX service on %s" % addr)
service_matches = find_service(name='OBEX Object Push', address = addr )
# print(find_service(address = addr ))
if len(service_matches) == 0:
print("Couldn't find the service.")
sys.exit(0)
first_match = service_matches[0]
port = first_match["port"]
name = first_match["name"]
host = first_match["host"]
print("Connecting to \"%s\" on %s" % (name, host))
client = Client(host, port)
client.connect()
client.put("Bluetooth/test.txt", open("./test.txt", "rb").read())
client.disconnect()
Using this way, we have to press the confirm button on tablet every time file transfer. We can’t do more at client side. What we can do is listening for Download folder if any file created. But this is tricky. Many files which are not what we want.
Bonus
We might want to place the file name inside csv file. Because bluetooth splits file to many parts to send. Below is python code to add the File Name at first row.
import csv
path = 'salary_123.csv'
List = ['File Name:', 'salary_123.csv']
with open(path,newline='') as f:
r = csv.reader(f)
data = [line for line in r]
print(data[0][0])
if (data[0][0] != 'File Name:'):
with open(path,'w',newline='') as f:
w = csv.writer(f)
w.writerow(List)
w.writerows(data)
Reference
https://github.com/EnableTech/raspberry-bluetooth-demo
https://github.com/EnableTech/raspberry-bluetooth-demo
https://pythonic.rapellys.biz/articles/interfacing-with-bluetooth-devices-using-python/
https://stackoverflow.com/questions/37913796/bluetooth-error-no-advertisable-device