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!

1 Comment

  1. Nice! Would this work for a Bluetooth BLE device? I am trying to send commands from the processing android mode to a BLE device, but it seems like the KATAI library does not have this functionality. Any idea how I could do it? Thanks!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.