How can i send/receive data over bluetooth between ANDROID app and Raspberry Pi?

2.4k views Asked by At

My goal is : send via bluetooth certain string comand from my Android app to a Raspberry Pi device so it can runs a script,and after it is done, send back the message to my Android app

My approach:

1.I used this tutorial for inspiration of my bluetooth server class that runs on my Android app (and convert it to kotlin)

  1. Used this python code(from my Pi3) for tryn to connect as a client on my server class

import socket

serverMACAddress = 'F8:8F:07:3D:7A:7B'
port = 2
s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_STREAM, socket.BTPROTO_RFCOMM)
s.connect((serverMACAddress,port))
while 1:
    text = input()
    if text == "quit":
        break
    s.send(bytes(text, 'UTF-8'))
s.close()

The problem: Rpi3 connects just at the level of my phone os bluetooth, but the server client that i wrote in my android app doesnt accept it, also, can't send/receive data from one to another (ofc)

I tried a lot of things in this app for a lot of time,changed a lot of libraries for the client app in python, nothing worked, i spent at least 5 weeks on this part.

I will provide you my kotlin code(note: i dont use ClientClass but maybe it will help posting it, ofc that i want that android client class that i dont use, somehow be translated to python to work on my pi) and also pics with what i have achieved:

class TrackYourWorkoutFragment : Fragment() {

private lateinit var binding: FragmentTrackYourWorkoutDataBinding

var MAC_ADDRESS = "E4:5F:01:0A:7D:20"
var MAC_ADDRESS_SAMSUNG = "F8:8F:07:3D:7A:7B"
val DEVICE_NAME = "raspberrypi"
var bluetoothSocket: BluetoothSocket? = null
var isConnected: Boolean = false
private lateinit var bluetoothAdapter: BluetoothAdapter
private lateinit var sendReceive: SendReceive

companion object {
    private const val APP_NAME: String = "RealStats"
    var SHARED_UUID: UUID = UUID.fromString("f4b18e71-c048-43a0-99cc-06f99eb6b654")
    private const val STATE_LISTENING = 1
    private const val STATE_CONNECTING = 2
    private const val STATE_CONNECTED = 3
    private const val STATE_CONNECTION_FAILED = 4
    private const val STATE_MESSAGE_RECEIVED = 5
}

private val viewModel: TrackYourWorkoutFragmentViewModel by lazy {
    ViewModelProvider(this, viewModelFactory {
        TrackYourWorkoutFragmentViewModel(
            onConnectToPiClick = { startServerSoPiConnects() },
            onStartWorkoutClick = { startWorkout() }
        )
    })[TrackYourWorkoutFragmentViewModel::class.java]
}

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    binding = DataBindingUtil.inflate(
        inflater,
        R.layout.fragment_track_your_workout,
        container,
        false
    )
    binding.viewModel = viewModel
    binding.lifecycleOwner = viewLifecycleOwner
    return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    (requireActivity() as? AppCompatActivity)?.supportActionBar?.setTitle(R.string.toolbar_track_your_workout)
    bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
    enableBluetooth()
}

var handler = Handler { msg ->
    when (msg.what) {
        STATE_LISTENING -> binding.tvInfoConnection.text = "Listening"
        STATE_CONNECTING -> binding.tvInfoConnection.text = "Connecting"
        STATE_CONNECTED -> binding.tvInfoConnection.text = "Connected"
        STATE_CONNECTION_FAILED -> binding.tvInfoConnection.text = "Connection Failed"
        STATE_MESSAGE_RECEIVED -> {
            val readBuff = msg.obj as ByteArray
            val tempMsg = String(readBuff, 0, msg.arg1)
            binding.tvInfo.text = tempMsg
        }
    }
    true
}

@SuppressLint("MissingPermission")
private fun startServerSoPiConnects() {
    //val piDevice: BluetoothDevice = bluetoothAdapter.getRemoteDevice(MAC_ADDRESS)
    val serverClass = ServerClass()
    serverClass.start()
}

private fun startWorkout() {
    val startMessageForPi = "s"
    sendReceive.write(startMessageForPi.toByteArray())
}

private fun enableBluetooth() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        requestMultiplePermissions.launch(
            arrayOf(
                Manifest.permission.BLUETOOTH_SCAN,
                Manifest.permission.BLUETOOTH_CONNECT
            )
        )
    }
}

@SuppressLint("MissingPermission")
private val requestMultiplePermissions =
    registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
        if (!bluetoothAdapter.isEnabled) {
            bluetoothAdapter.enable()
            Toast.makeText(this.context, "Bluetooth enabled", Toast.LENGTH_SHORT).show()
        }
    }

private inner class ServerClass @SuppressLint("MissingPermission") constructor() : Thread() {
    private lateinit var serverSocket: BluetoothServerSocket

    init {
        try {
            serverSocket = bluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord(
                APP_NAME,
                SHARED_UUID
            )
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

    override fun run() {
        var socket: BluetoothSocket? = null

        while (socket == null) {
            try {
                val message: Message = Message.obtain()
                message.what = STATE_CONNECTING
                handler.sendMessage(message)
                socket = serverSocket.accept()
            } catch (e: IOException) {
                e.printStackTrace()
                val message: Message = Message.obtain()
                message.what = STATE_CONNECTION_FAILED
                handler.sendMessage(message)
            }

            if (socket != null) {
                val message: Message = Message.obtain()
                message.what = STATE_CONNECTED
                handler.sendMessage(message)
                sendReceive = SendReceive(socket)
                sendReceive.start()
                break
            }
        }
    }
}

private inner class ClientClass @SuppressLint("MissingPermission") constructor(deviceR: BluetoothDevice) :
    Thread() {
    private var device: BluetoothDevice
    private lateinit var socket: BluetoothSocket

    init {
        device = deviceR
        try {
            socket = device.createRfcommSocketToServiceRecord(SHARED_UUID)
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

    @SuppressLint("MissingPermission")
    override fun run() {
        try {
            socket.connect()
            val message = Message.obtain()
            message.what = STATE_CONNECTED
            handler.sendMessage(message)

            sendReceive = SendReceive(socket)
            sendReceive.start()
        } catch (e: IOException) {
            e.printStackTrace()
            val message = Message.obtain()
            message.what = STATE_CONNECTION_FAILED
            handler.sendMessage(message)
        }
    }
}

private inner class SendReceive(socket: BluetoothSocket) :
    Thread() {
    private var bluetoothSocket: BluetoothSocket
    private lateinit var inputStream: InputStream
    private lateinit var outputStream: OutputStream

    init {
        bluetoothSocket = socket
        var tempIn: InputStream? = null
        var tempOut: OutputStream? = null

        try {
            tempIn = bluetoothSocket.inputStream
            tempOut = bluetoothSocket.outputStream
        } catch (e: IOException) {
            e.printStackTrace()
        }
        if (tempIn != null) {
            inputStream = tempIn
        }
        if (tempOut != null) {
            outputStream = tempOut
        }
    }

    override fun run() {
        val buffer = ByteArray(1024)
        var bytes: Int

        while (true) {
            try {
                bytes = inputStream.read(buffer)
                handler.obtainMessage(STATE_MESSAGE_RECEIVED, bytes, -1, buffer).sendToTarget()
            } catch (e: IOException) {
                e.printStackTrace()
            }

        }
    }

    fun write(bytes: ByteArray) {
        try {
            outputStream.write(bytes)
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }
}

}

initial state of the app

after starting the server, by clicking connect to pi

after clicked connect to pi(started the server) and runned the pi program note: the text doesnt change, it remains in connecting state

If someone manages to solve this somehow, or provide me some usefull notes at least, please, i want to send you a NICE(i hope) gift.

1

There are 1 answers

1
Radu On

FINALLY FOUND IT, THIS IS THE ANSWER import bluetooth import threading import time import random import sys

#address="00:17:E8:B2:50:28"
#uuid="66841278-c3d1-11df-ab31-001de000a901" 
#service_matches = bluetooth.find_service( uuid = uuid, address = address )

uuid="f4b18e71-c048-43a0-99cc-06f99eb6b654"
while True:
  try:
    print ("Searching ...")
    try: service_matches
    except NameError:
      service_matches = bluetooth.find_service( uuid = uuid)
    else:
      if not service_matches:
        print ("without address")
        service_matches = bluetooth.find_service( uuid = uuid)
      else:
        print ("with address")
        service_matches_with_addr = bluetooth.find_service( uuid = uuid, address = "F8:8F:07:3D:7A:7B" )
        if service_matches_with_addr:
          service_matches = service_matches_with_addr
        else:
          continue

    if service_matches:
      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))
      sock=bluetooth.BluetoothSocket( bluetooth.RFCOMM )
      sock.connect((host, port))

      print ("Happy Spamming")

      while True:
        try:
          string = 'test' + random.choice('abcdefghij') + '\n'
          try:
            sock.send(string)
            time.sleep(0.5)
          except:
            print ("Android no longer interested in my spam, socket not valid, going back to searching")
            break
        except KeyboardInterrupt:
          print ("Done with Spamming, Press Ctrl-C again if you wish to quit, otherwise I'll keep searching")
          break

  except KeyboardInterrupt:
    print ("Phew! Done Searching")
    sys.exit()

if service_matches:
  first_match = service_matches[0]
  port = first_match["port"]
  name = first_match["name"]
  host = first_match["host"]

  sock=bluetooth.BluetoothSocket( bluetooth.RFCOMM )
  sock.connect((host, port))

  print ("Happy Spamming")

  while True:
    try:
      string = 'test' + random.choice('abcdefghij') + '\n'
      sock.send(string)
      time.sleep(0.5)
    except KeyboardInterrupt:
      print ("Done with Spamming")
      sys.exit()


sock.close()