Mano Bhargav
Mano Bhargav

Reputation: 49

Making a Bluetooth speaker with playback controls connected to phone

I am recently working on a project. The idea is to make a Bluetooth speaker which has playback controls like pause,play, next song on the speaker itself. The speaker is to be connected to the mobile phone via Bluetooth. Ultimately i wanted to achieve the button control(on speaker) of songs I play on mobile which is connected to speaker via Bluetooth.

I was able to make the speaker connected via Bluetooth to the phone and could play songs but the biggest challenge is i couldn't pause or forward the playlist on phone from speaker side. What i understood is that the speaker is able to receive signals from phone to play songs but how do i send a command(like next song via physical button) from the speaker to the phone(via a micro controller like arduino). I hope I stated my situation well. I came across idea like having multiple bluetooth pairing, custom programming of the BT module that i'll be using, etc.

I am open to all kinds of way this can be done and I really appreciate if anyone could recommend me the proper BT modules and micro-controller to be used.

Upvotes: 0

Views: 962

Answers (1)

DynoZ
DynoZ

Reputation: 169

About the BT module, I suggest you to use an HC-05. For the MCU, Arduino cannot be used to play music... so you should optain for Rasberry, but I am not sure. Also I never used it...

If you need some code similar, I mede an app for a greenhouse which displays temperature, humidity and light inside it.

If you wanna read these class in order: gitHub. But I have to post the code, too: maybe I could delete the repo a day

BLTSocket class:

public class BLTSocket extends Service {

private static final UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private static boolean isBtConnected = false;
private static BluetoothAdapter adapter = null;
private static BluetoothSocket socket = null;
private static InputStream is = null;
private static OutputStream os = null;

public BLTSocket(){

}

/**
 * Constructor called when a bluetooth device has been clicked in the layout
 * If the phone's bluetooth socket has not already connected , it tries to initialize
 * the new connection with the one clicked.
 * Get the stream and set @isBtConnected as true
 *
 * @param address - MAC address of the bluetooth device
 */
public BLTSocket(String address){
    if (!isBtConnected || !socket.isConnected()) {
        try {
            adapter = BluetoothAdapter.getDefaultAdapter();
            BluetoothDevice device = adapter.getRemoteDevice(address);
            socket = device.createInsecureRfcommSocketToServiceRecord(uuid);
            socket.connect();
            is = socket.getInputStream();
            os = socket.getOutputStream();
            isBtConnected = true;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

@Override
public void onCreate() {
    HandlerThread handlerThread = new HandlerThread("SURVEYS_SERVICE", Process.THREAD_PRIORITY_BACKGROUND);
    handlerThread.start();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    survey();
    return START_STICKY;
}

/**
 * This handler keeps updating the three surveys reading their current value
 * from the bluetooth module's input stream.
 * Set the the circular progress bar and the own text view below as the relative value
 */
Handler handler = new Handler();
Runnable runnable;
private void survey() {

    runnable = new Runnable() {
        @RequiresApi(api = Build.VERSION_CODES.O)
        @Override
        public void run() {
            byte[] survey = new byte[12];
            try {
                /*
                    Once got the current values of the three surveys,
                    it sets the relative progress bar and the text view
                 */
                if(is.read(survey) > 0){
                    for(int i = 0; i < 3; i++){
                        MainActivity.getProgressBars()[i].setProgress(survey[i]);
                        MainActivity.getTextViews()[i].setText(String.valueOf(survey[i]) + (i == 0 ? "°C" : '%'));
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
                return;
            }
            handler.postDelayed(this, 2000);
        }
    };
    runnable.run();

}

/**
 * Called when the user click on the UPDATE SEED button
 * on the popup menu of the ViewSeed layout
 *
 * Send to the HC-05 the new recommended values
 *
 * @param values
 */
public void updateSeed(int[] values){
    try {
        handler.removeCallbacks(runnable);
        for(int value : values) {
            os.write(value);
        }
        os.flush();
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        handler.postDelayed(runnable, 2000);
    }
}


@Nullable
@Override
public IBinder onBind(Intent intent) {
    return null;
}

/**
 * Close the socket and set @isBtConnected as false when the app is closed or crashes
 */
@Override
public void onDestroy() {
    try {
        this.socket.close();
        this.isBtConnected = false;
    }catch (IOException e) {
        e.printStackTrace();
    }
}

}

BLTActivity:

public class BluetoothActivity extends AppCompatActivity {

ActivityBluetoothBinding activityBluetoothBinding;

private BluetoothAdapter adapter;
private Set<BluetoothDevice> pairedDevices;
private ListView listBLTDevice;
private TextView bltTV;

private BluetoothDevice deviceSelected;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    activityBluetoothBinding = DataBindingUtil.setContentView(this, R.layout.activity_bluetooth);
    activityBluetoothBinding.setSettings(MainActivity.loadSettings);
    this.bltTV = findViewById(R.id.bltTV);
    this.listBLTDevice = findViewById(R.id.listBLTDevices);
    setBlt();
}

public static BLTSocket bltSocket;

private void setBlt(){
    this.adapter = BluetoothAdapter.getDefaultAdapter();
    //If the adapter is null, the user's phone hasn't a bluetooth radio
    if(adapter != null){
        if(!adapter.isEnabled()){
            //If the bluetooth is disabled, it asks to the user to enable it
            startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE), 1);
        }

        //The bonded devices are those ones which have already been compared
        pairedDevices = adapter.getBondedDevices();
        this.bltTV.setText(pairedDevices.size() + " Devices Found");

        if(pairedDevices.size() > 0){

            /*
             *  Create a new list of strings which every one contains the name of one compared
             *  bluetooth radio. The array list is given to the adapter.
             */
            final ArrayList<String> arrayList = new ArrayList<>();
            for (BluetoothDevice device : pairedDevices){
                arrayList.add(device.getName());
            }
            final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, getListViewLayout(), arrayList);
            this.listBLTDevice.setAdapter(adapter);

            /*
                When an item (string that represents a bluetooth radio) has been clicked,
                its MAC address is given to the BLTSocket constructor, which will set
                the opportune variable, objects and connects our radio with the selected one
             */
            this.listBLTDevice.setOnItemClickListener( (parent, v, pos, id) -> {
                deviceSelected = (BluetoothDevice) pairedDevices.toArray()[pos];
                Toast.makeText(getApplicationContext(), "Device Selected: " + deviceSelected.getName() + "\n" + deviceSelected.getAddress(), Toast.LENGTH_SHORT).show();
                bltSocket = new BLTSocket(deviceSelected.getAddress());
                startService(new Intent(this, BLTSocket.class));
            } );
        }
    }else{
        Toast.makeText(getApplicationContext(), "Seems like your device hasn't any bluetooth adapter...", Toast.LENGTH_SHORT).show();
    }
}

/**
 * Called when setting the ArrayAdapter to chose list layout with the right color of the text
 * If the user has the dark mode enabled, this method returns the white list and vice versa
 *
 * @return the id of the opportune listview layout
 */
private int getListViewLayout() {
    return !MainActivity.loadSettings.isDarkMode() ? R.layout.listview_blacktext_layout : R.layout.listview_whitetext_layout;
}

}

Upvotes: 0

Related Questions