Android Code

All of the code needed to run this project is located at
https://bitbucket.org/alterationx10/8bitbox-adafruit

NOTE: The Android code is targeted to a minimum of Android JellyBean (API 16)! You will have to do a bit of work to make is work with older Android devices (but it is possible).

Take a step back

Let's take a moment to think about what we've done so far.

Hardware design: It might not be too advanced, but we laid out some components on a shield. It may only be an RGB LED and a buzzer, but that has implications for our firmware.

Firmware design: Based on our hardware design, we have laid out our firmware to connect to our components. We know we are going to be talking to a Bluetooth Android device, so we have even implemented our own communication protocol.

Software design: We are now at the point where we can make software to talk to the firmware on the hardware we made! How exciting! But this can be very frustrating too. You writing software (an App) with other peoples software (Android) on other peoples hardware (your phone/tablet) to communicate with the firmware you wrote for your own hardware. What happens when something doesn't work? Is it your software? Is it your firmware? Is it your hardware? Or is it theirs? It's probably a vicious mixture of all of the above :-)

But, we should take a deep breath a step gently though each step, thinking about how each part interacts with each other to isolate and fix any issues that arise.

A full discussion of Android (and some of the problems you might run in to) is outside the scope of this tutorial. Much of the code is commented, or can be further commented if you ask, and the best understanding would come from looking right at the code and tweaking it yourself.

Below, we will think about the "interesting interactions" between your app and your 8BitBox, how the software is going to do it, and how you are going to get the user to tell the software to do it. Since it's an 8BitBox, and 8Bit style controller would make interesting interaction. Our app will be themed as such.

What "interesting interactions" will our app perform?

  1. Connect/Disconnect to the 8BitBox
  2. Send a command to the 8BitBox
  3. Wait for the 8BitBox to respond

What commands will we be sending?

  1. Setting an R/G/B color
  2. Playing a note on the buzzer
  3. Storing a favorite color in EEPROM
With all this in mind, lets look at our simple UI, and figure out which UI elements we are going to assign to which actions.

Lets lay out all the actions we want to perform, and what elements will trigger/show it

Element - Function

  • Action Bar - Show status'
  • Slider (TOP) - Adjust Red LED
  • Slider (Middle) - Adjust Green LED
  • Slider (Bottom) - Adjust Blue LED
  • Start - Connect
  • Select - Disconnect
  • B - Play Song 1
  • A - Play Song 2
  • (Secret) Up,Down,Left,Right,B,A,Start - Store a favorite color to EEPROM if currently connected to the 8BitBox
Now that we have an overview of what the app does, let's discuss a few of the of them in a little more detail.

Start Button

This is what happens when we press to connect to the 8BitBox. We connect to the 8BitBox based on it's MAC address, and provide a custom CallBack to perform events based on the result. We try to connect using Serial Port Protocol, and if successful, we get the socket input out output streams. Then it's just like usual RX/TX on the Arduino! This is very much like Serial.begin() in an Arduino sketch, and once we are connected, we just start sending bytes of data back and forth.
Download: file
// An Interface will allow the user to run their own code when 
// a certain even happens. This is one for Bluetooth Conenctions.
public interface BluetoothConnectCallback {
  public void doOnConnect();
  public  void doOnConnectionFailed();
  public void doOnDisconnect();
}

public boolean btConnect(String MAC, BluetoothConnectCallback myCallback) {
        // Just in case BT wasn't enabled when we started the app
        if (btAdapater == null) {
            btAdapater = BluetoothAdapter.getDefaultAdapter();
        }

        // Are we already connected?
        if (btSocket != null && btSocket.isConnected()) {
            return true;
        }

        // MAC needs to be upper case. Help the user out.
        MAC = MAC.toUpperCase();

        // Get our device
        btDevice = btAdapater.getRemoteDevice(MAC);

        // UUID for Serial Port Protocol
        UUID mUUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

        // Try to connect
        try {
            btSocket = btDevice.createInsecureRfcommSocketToServiceRecord(mUUID);

        } catch (IOException e) {
            Log.d(TAG,"Failed to open the bluetooth socket...");
            // Trigger the fail method that the user has implemented
            myCallback.doOnConnectionFailed();
            return false;
        }

        // Connect ot the socket
        try {
            btSocket.connect();
            iStream = btSocket.getInputStream();
            oStream = btSocket.getOutputStream();

            // Trigger the success event that the user has implemented
            myCallback.doOnConnect();
        } catch (IOException e) {
            Log.d(TAG, "Failed to connect to the bluetooth socket, or open the I/O streams...");
            // Trigger the fail method
            myCallback.doOnConnectionFailed();
            return false;
        }

        // If we've made it this far, everything is good to go!
  		// This is for a job queue to process commands in a
  		// First-In-First-Out manner.
        commService = Executors.newSingleThreadExecutor();
        return true;
    }

Select Button

When the select button is pressed, we want to disconnect from the device. This is the type of code you would run for that event
Download: file
public boolean btDisconnect(BluetoothConnectCallback myCallback) {

        // Don't bother to disconnect if we're not already connected
        if (btSocket != null && btSocket.isConnected()) {
            try {
                // Try and close down the I/O streams and close the socket.
                iStream.close();
                oStream.close();
                btSocket.close();
                if (commService != null && !commService.isShutdown()) {
                    commService.shutdown();
                }
            } catch (IOException e) {
                Log.d(TAG,"Had trouble properly closing down the I/O and Bluetooth socket...");
            }
        } else {
            // Return false if we weren't connected
            return false;
        }

        // Run the user specified code
        myCallback.doOnDisconnect();

        // Return true if we made it this far
        return true;
    }

Slider

We will use the slider (SeekBar) to adjust our RGB values. By setting the maximum possible value to 255, every time it is changed, we can just send a command with the progress value, and adjust the LED.
Download: file
// Get the reference to the SeekBar from the layout XML file
redSeeker = (SeekBar)findViewById(R.id.seekRed);
		// This is used to set an LED, so we only want it to go between
		// 0 and 255
        redSeeker.setMax(255);
        redSeeker.setProgress(0);
        redSeeker.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
          		// When the SeekBar is changed, we get the 0-255 progress value,
          		// and send it over the serial port.
          		// BoxConstants.RED[0] is "R", the command of our protocol to
          		// Adjust the Red LED.
          		// This means that setLevel looks something like 
          		// [0x52, 0xff] if the slider was to the max
                byte[]setLevel = {BoxConstants.RED[0], (byte) progress};
                myService.writeData(setLevel);

		// This method just sets the color of the picture on the screen
		// to be the same as the 8BitBox
                setBitBoxColor();
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });

A and B Buttons

These buttons simply send a preconfigured array of bytes to play a song over the piezo buzzer. The interesting thing here, is that music presents a challenge due to the timing. It's only a quarter note if it's played for a quarter of the time...

This is where out protocol comes in handy! We don't expect a response when we change the LED, because the timing isn't so important, but for this it is. For the case of beeping out some music, we don't want to send the next note until the last note has played. Playing a note is started with the command "Z", and on the Arduino side, once we are finished playing a note, we will return a "z" to indicate that we're done and ready for the next note.
Download: file
// For the length of the array of all the notes to play (underworld.length)
for (int i=0; i < underworld.length; i++) {
  
  // To play a musical note, we use command "Z" (BoxConstants.MUSIC[0])
  // Once the note plays, it will return "z",
  // and we will know that we can play the next note
                    
  // Our (musical) note is composed of "Z",
  // a 16 bit integer value (as two bytes)
  // and a duration to play the note for
  byte[] note = 
       {BoxConstants.MUSIC[0],
         Tone.toneMSB(underworld[i]),
           Tone.toneLSB(underworld[i]), 
             (byte) underworld_tempo[i]};
                    
	// We send the command data
	myBox.writeData(note);
                    
	// For everything but the last note
	if (i != underworld.length -1) {
 		while(myBox.rawRead() != BoxConstants.MUSIC_RESPONSE[0]) {
          // Block until we get feedback that we're ready for the next note 
        }    
    }
}
This guide was first published on Jun 13, 2014. It was last updated on Jun 13, 2014.
This page (Android Code) was last updated on Jun 27, 2020.