Smart Shutter | Bluetooth Communication Between Android and Arduino using Processing

So, once you get processing for android all installed, if you’re like me the first thing you’ll want to do is get your phone talking with an Arduino over bluetooth. Well maybe not first thing but you get the Idea. Below is two pieces of code that I’ve used for this project. It’s very specific for this project, but it may help somebody and will likely help myself in the future so there you go.

Ardiuno Code:

const char EOPmarker = '.'; //This is the end of packet marker
char serialbuf[32]; //This gives the incoming serial some room. Change it if you want a longer incoming.
char serialbuf2[32];

#include <string.h> // we'll need this for subString
#define MAX_STRING_LEN 20 // like 3 lines above, change as needed.
#include <SoftwareSerial.h>

SoftwareSerial Ble(11, 10); // RX, TX

int Mode = 5;
int Shutter = 6;
int Focus = 7;

void setup(){

  Serial.begin(9600);
  Ble.begin(9600);

  pinMode(Mode, INPUT);
  pinMode(Shutter, OUTPUT);
  pinMode(Focus, OUTPUT);

}

void loop() {
    if (Serial.available() > 0) { //makes sure something is ready to be read
      static int bufpos = 0; //starts the buffer back at the first position in the incoming serial.read
      char inchar = Serial.read(); //assigns one byte (as serial.read()'s only input one byte at a time
      if (inchar != EOPmarker) { //if the incoming character is not the byte that is the incoming package ender
        serialbuf[bufpos] = inchar; //the buffer position in the array get assigned to the current read
        bufpos++; //once that has happend the buffer advances, doing this over and over again until the end of package marker is read.
      }
      else { //once the end of package marker has been read
        serialbuf[bufpos] = 0; //restart the buff
        bufpos = 0; //restart the position of the buff
        Expose(serialbuf,1);

        }
      }

    if (Ble.available() > 0) { //makes sure something is ready to be read
      static int bufpos2 = 0; //starts the buffer back at the first position in the incoming serial.read
      char inchar2 = Ble.read(); //assigns one byte (as serial.read()'s only input one byte at a time
      if (inchar2 != EOPmarker) { //if the incoming character is not the byte that is the incoming package ender
        serialbuf2[bufpos2] = inchar2; //the buffer position in the array get assigned to the current read
        bufpos2++; //once that has happend the buffer advances, doing this over and over again until the end of package marker is read.
        delay(10);
      }
      else { //once the end of package marker has been read
        serialbuf2[bufpos2] = 0; //restart the buff
        bufpos2 = 0; //restart the position of the buff
        Expose(serialbuf2,0);

      }
    }
}

void Expose(char* buf, int Mode){

      int Type = atoi(subStr(buf, ",", 1));
      int Exposures = atoi(subStr(buf, ",", 2));
      int Delay = atoi(subStr(buf, ",", 3));

      int Actual_Delay = Delay;

      String Out = String("Mode: " + String(Mode) + " Type: " + String(Type) + " Exposures: " + String(Exposures) + " Delay: " + String(Delay));

      Serial.println(Out);

      for (int i = 0; i < Exposures; i++){
        if (Type == 0){
          digitalWrite(Shutter, HIGH);   // turn the LED on (HIGH is the voltage level)
          delay(30);               // wait for a second
          digitalWrite(Shutter, LOW);    // turn the LED off by making the voltage LOW
          delay(30);

          if (Delay < 250){
            Actual_Delay = 250;
          }
          else {
            Actual_Delay = Delay;
          }
        }

        if (Type == 1){
          digitalWrite(Focus, HIGH);   // turn the LED on (HIGH is the voltage level)
          delay(30);               // wait for a second
          digitalWrite(Focus, LOW);    // turn the LED off by making the voltage LOW
          delay(30); 

          delay(100);

          digitalWrite(Shutter, HIGH);   // turn the LED on (HIGH is the voltage level)
          delay(30);               // wait for a second
          digitalWrite(Shutter, LOW);    // turn the LED off by making the voltage LOW
          delay(30);

          if (Delay < 700){
            Actual_Delay = 700;
          }
          else {
            Actual_Delay = Delay;
          }         

        }

        delay(Actual_Delay);
      }
}

// below is just function logic, which I do not fully understand. but it works.
char* subStr (char* input_string, char *separator, int segment_number) {
char *act, *sub, *ptr;
static char copy[MAX_STRING_LEN];
int i;
strcpy(copy, input_string);
for (i = 1, act = copy; i <= segment_number; i++, act = NULL) {
  sub = strtok_r(act, separator, &ptr);
  if (sub == NULL) break;
}
 return sub;
}

Processing Code:

import apwidgets.*;

//App-Wide (General) Elements
APWidgetContainer General_Container;
APRadioButton General_Setup_RB;
APRadioButton General_Interact_RB;
APRadioGroup General_RadioGroup;

//Setup Mode Elements
APWidgetContainer SetupMode_Container;
APButton SetupMode_ConnectButton;
APButton SetupMode_DisconnectButton;

//Interact Mode Elements
APWidgetContainer InteractMode_Container;
APButton InteractMode_SendButton;

APEditText InteractMode_TypeTB;
APEditText InteractMode_ExposuresTB;
APEditText InteractMode_DelayTB;

/* -------------- Bluetooth Stuff -------------- */

//required for BT enabling on startup
import android.content.Intent;
import android.os.Bundle;

import ketai.net.bluetooth.*;
import ketai.ui.*;
import ketai.net.*;

import oscP5.*;

KetaiBluetooth bt;
String info = "";
KetaiList klist;
PVector remoteMouse = new PVector();

ArrayList<String> devicesDiscovered = new ArrayList();
boolean isConfiguring = true;
String UIText;

//********************************************************************
// The following code is required to enable bluetooth at startup.
//********************************************************************
void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  bt = new KetaiBluetooth(this);
}

void onActivityResult(int requestCode, int resultCode, Intent data) {
  bt.onActivityResult(requestCode, resultCode, data);
}

void setup(){
  //App-Wide (General) Elements
  General_Container = new APWidgetContainer(this); //create new container for widgets

  General_RadioGroup = new APRadioGroup(10, 10); //create a new radiogroup
  General_RadioGroup.setOrientation(APRadioGroup.HORIZONTAL);

  General_Setup_RB = new APRadioButton("Setup "); //create new radiobutton from label.
  General_Interact_RB = new APRadioButton("Interact "); //create new radiobutton from label.

  General_RadioGroup.addRadioButton(General_Setup_RB); //place radiobutton in radiogroup
  General_RadioGroup.addRadioButton(General_Interact_RB); //place radiobutton in radiogroup

  General_Container.addWidget(General_RadioGroup);

  //Setup Mode Elements
  SetupMode_Container = new APWidgetContainer(this);

  SetupMode_ConnectButton = new APButton(5, 200, 900, 100, "Start Connection");
  SetupMode_DisconnectButton = new APButton(5, 300, 900, 100, "Stop Connection");

  SetupMode_Container.addWidget(SetupMode_ConnectButton);
  SetupMode_Container.addWidget(SetupMode_DisconnectButton);

  //Interact Mode Elements
  InteractMode_Container = new APWidgetContainer(this);

  InteractMode_SendButton = new APButton(5, 500, 900, 100, "SendToBT");

  InteractMode_TypeTB = new APEditText(5, 150, 350, 100);
  InteractMode_ExposuresTB = new APEditText(5, 250, 350, 100);
  InteractMode_DelayTB = new APEditText(5, 350, 350, 100);

  InteractMode_Container.addWidget(InteractMode_SendButton);

  InteractMode_Container.addWidget(InteractMode_TypeTB);
  InteractMode_Container.addWidget(InteractMode_ExposuresTB);
  InteractMode_Container.addWidget(InteractMode_DelayTB);

  //Finishing Touches
  General_Setup_RB.setChecked(true); //Setup mode is selected by default
  SetupMode_Container.hide();
  InteractMode_Container.hide();
}

void draw(){

  if(General_Setup_RB.isChecked()){
    background(4, 49, 50); 

    SetupMode_Container.show();
    InteractMode_Container.hide();
  }
  else if(General_Interact_RB.isChecked()){
    background(50, 4, 48);

    //creates text on screen
    textSize(32);
    text("Mode: " + InteractMode_TypeTB.getText(), 370, 200); 

    textSize(32);
    text("Exposures: " + InteractMode_ExposuresTB.getText(), 370, 300); 

    textSize(32);
    text("Delay: " + InteractMode_DelayTB.getText(), 370, 400); 

    SetupMode_Container.hide();
    InteractMode_Container.show();
  }
}

void onClickWidget(APWidget widget){
  if(widget == SetupMode_ConnectButton){
    bt.start(); //start listening for BT connections
    if (bt.getDiscoveredDeviceNames().size() > 0)  //If we have not discovered any devices, try prior paired devices
      klist = new KetaiList(this, bt.getDiscoveredDeviceNames());
    else if (bt.getPairedDeviceNames().size() > 0)
      klist = new KetaiList(this, bt.getPairedDeviceNames());

  }
  if(widget == SetupMode_ConnectButton){
    bt.stop(); //start listening for BT connections

  }

  if (widget == InteractMode_SendButton){

    String m = InteractMode_TypeTB.getText() + ',' + InteractMode_ExposuresTB.getText() + ',' + InteractMode_DelayTB.getText() + '.'; //translates the selection by the user to 

    print(m.getBytes()+ " , "+ m);
    bt.broadcast(m.getBytes()) ; 

  }

}

void onKetaiListSelection(KetaiList klist) { //Recives your selection
  String selection = klist.getSelection();
  text(str(bt.connectToDeviceByName(selection)),10,100);

  //dispose of list for now
  klist = null;
}

void onBluetoothDataEvent(String who, byte[] data) { //Call back method to manage data received
  if (isConfiguring)
    return;

  //KetaiOSCMessage is the same as OscMessage
  //   but allows construction by byte array
  KetaiOSCMessage m = new KetaiOSCMessage(data);
  if (m.isValid())
  {
    if (m.checkAddrPattern("/remoteMouse/"))
    {
      if (m.checkTypetag("ii"))
      {
        remoteMouse.x = m.get(0).intValue();
        remoteMouse.y = m.get(1).intValue();
      }
    }
  }
}

String getBluetoothInformation(){
  String btInfo = "Server Running: ";
  btInfo += bt.isStarted() + "\n";
  btInfo += "Discovering: " + bt.isDiscovering() + "\n";
  btInfo += "Device Discoverable: "+bt.isDiscoverable() + "\n";
  btInfo += "\nConnected Devices: \n";

  ArrayList<String> devices = bt.getConnectedDeviceNames();
  for (String device: devices)
  {
    btInfo+= device+"\n";
  }
  return btInfo;
}

Let me know if you ever use this!

Smart Shutter | Installing Processing For Android (With Pictures!)

This was a horrible (!) experience. I ran in to a slew of errors from the “Android Mode” menu not showing up, to having to adjust my PATH variable. Hopefully this guide helps somebody. Most of this is taken from this guide from processing, but a lot of the errors I ran into I resolved using various forums.

First things first, download the Android SDK for an existing API.

From there, you need to install the SDK as well as some pretty specific packages using the SDK manager. [Android SDK Platform-tools], [Android 2.3.3 (API 10) > SDK Platform] and [Google USB Driver under Extras]. My setup worked once I hit “deselect all”. Note the location of the SDK.

The install of Processing is very simple. Please note that the “modes” folder inside the folder is NOT the folder you manually install modes in. Please note the location of your sketchbook folder from the preferences inside of processing.

Next, you need to download and install Java’s JDK here.

So this is where stuff started to go south. For some unknown reason, when I installed processing, “Android Mode” didn’t appear in the modes box in the top right corner.

I had to manually install AndroidMode. To do that, you must download and uncompress it into the modes folder IN your sketchbook folder.

Once you can see “Android Mode” you will need to locate the SDK.

Once I got this working, upon compiling a demo app with my phone connected resulted in errors!

BUILD FAILED
C:\ADT\adt-bundle-windows-x86_64-20131030\sdk\tools\ant\build.xml:720: The following error occurred while executing this line:
C:\ADT\adt-bundle-windows-x86_64-20131030\sdk\tools\ant\build.xml:734: Error running javac.exe compiler

Total time: 1 second

You must edit the “Path” variable by adding a semicolon with the location of your JDK’s bin folder.

And there you go. Everything should be working now.

In order to connect your phone and upload your apps to it, you need to set your phone to developer mode, which is very simple. Look here for instructions.

Smart Shutter | Setting up a BlueSMiRF with Software Serial and Arduino

I was recently accepted into a beta test for MIT dealing with a prototype Arduino board and their website. I’ll create a final post showcasing the completed project, but for step by step updates check out their website here, and my personal project page here. Now on to the tutorial.


I always reset my module after I dust it off for use, jussttt in case:

Because I know I’ll need to do this again, I’ve decided to take the time writing a post explaining how to get communication going between an Arduino and a PC using serial over bluetooth in windows 8.

First thing’s first, connect to your device, as stated in the title, I’m using a BlueSMiRF Silver from Sparkfun.

Pairing is very easy. From there, upload the following code to your Arduino and connect your board as dictated by the code.

#include <SoftwareSerial.h>
SoftwareSerial mySerial(10, 11); // RX, TX

void setup()  {
  Serial.begin(115200);
  Serial.println("Send Hardware");

  mySerial.begin(115200);
  mySerial.println("Send Bluetooth");
}

void loop(){
  if (mySerial.available())
    Serial.write(mySerial.read());
  if (Serial.available())
    mySerial.write(Serial.read());
}

 

NOTE: IN ORDER TO USE DIFFERENT SERIAL SPEEDS, YOU WILL NEED TO MANUALLY CHANGE THE BAUD RATE USING THE DEBUG MODE IN THE BLUESMIRF.

https://learn.sparkfun.com/tutorials/using-the-bluesmirf

Then open your device manager and see how the bluetooth configuration went and how the ports were assigned:

 

For some reason, the lower COM port number is the correct one, I have no idea why. I’ll be using Putty to connect to the bluetooth COM port, the config is very simple:

<a href=”http://imgur.com/lXBc69L”><img src=”http://i.imgur.com/lXBc69L.png?1″ title=”Hosted by imgur.com” /></a>

From there, start typing in either console and it should all work!

Thanks for reading!