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).
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.
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?
- Connect/Disconnect to the 8BitBox
- Send a command to the 8BitBox
- Wait for the 8BitBox to respond
What commands will we be sending?
- Setting an R/G/B color
- Playing a note on the buzzer
- Storing a favorite color in EEPROM
Lets lay out all the actions we want to perform, and what elements will trigger/show it
Element - Function
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
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.// 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 eventpublic 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.// 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.
// 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 } } }
Page last edited January 21, 2014
Text editor powered by tinymce.