Skip to content

Ketai

We could continue with the direct approach and build all of the individual components required from scratch. However, since Bluetooth is widely used, there are many libraries that contain ready-made solutions to these problems. In this exercise you will use the ketai library to handle the Bluetooth operations as well as some more sophisticated UI features. You can find reference documentation on the library by following the link on the right. You may also find that this library could be useful for your project.

The code below is adapted from the examples provided on the ketai site in keeping with the learning-by-doing approach taken by the module overall. The important thing to remember when working from example code is that you need to spend the time working through to understand how it works. Some notes are provided at the end of each listing - again there are three which correspond to the different tabs in your PDE.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
    import android.os.Bundle;
    import android.content.Intent;

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

    KetaiBluetooth bt;

    KetaiList connectionList;
    String info = "";
    PVector remoteCursor = new PVector();
    boolean isConfiguring = true;
    String UIText;
    String LEDText;

    void setup()
    {   
      fullScreen();
      orientation(PORTRAIT);
      background(78, 93, 75);
      stroke(255);
      textSize(48);

      bt.start();

      UIText =  "[d] - discover devices\n" +
        "[c] - pick device to connect to\n" +
        "[p] - list paired devices\n" +
        "[i] - show Bluetooth info";

      LEDText = "[1] - turn LED on\n" +
        "[0] - turn LED off\n";
    }

    void draw()
    {
      ArrayList devices;
      byte[] message = new byte[1];

      if (isConfiguring)
      {
        background(78, 93, 75);

        if (key == 'i')
          info = getBluetoothInformation();
        else
        {
          if (key == 'p')
          {
            info = "Paired Devices:\n";
            devices = bt.getPairedDeviceNames();
          }
          else
          {
            info = "Discovered Devices:\n";
            devices = bt.getDiscoveredDeviceNames();
          }

          for (int i=0; i < devices.size(); i++)
          {
            info += "["+i+"] "+devices.get(i).toString() + "\n";
          }
        }
        text(UIText + "\n\n" + info, 5, 200);
      }
      else
      {
        background(78, 93, 75);
        text(LEDText + "\n\n", 5, 200);

        devices = bt.getConnectedDeviceNames();
        if (key == '1' || key == '2') {
          if (devices.size() > 0) {
            message[0] = (byte)key;
            bt.writeToDeviceName(devices.get(0).substring(0,devices.get(0).indexOf("(")), message );
          }
          else
            text("No connected device", 5, 100);
        }
      else
        text("Invalid key - try again", 5, 100);
      }

      drawUI();
    }

    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 devices = bt.getConnectedDeviceNames();
      for (String device: devices)
      {
        btInfo+= device+"\n";
      }

      return btInfo;
    }

Line 39: Notice that discovered devices are held in a java arraylist.

Line 40: the library function to write to a connected Bluetooth device requires a byte array as its second parameter. The message variable is only there to satisfy this requirement.

Lines 47, 53, 58, etc. Calls to library methods. As in the case of all library code, these simplify the task that you need to do.

Lines 76-77: Translating the character read by the app into the required datatype for sending to the connected device. Some string processing is required on line 77 to get the correct format of the device name.

Because we are using library code, we do not have to interact directly with Android quite so directly. Instead, we make use of the Android lifecycle hook onCreate() to set up the broadcast receiver.

1
2
3
4
5
6
7
8
    void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      bt = new KetaiBluetooth(this);
    }

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

This example takes a different approach to creating UI widgets such as buttons. Both strategies have their advantages - you should spend a little time thinking about which is most appropriate for a particular case and also whether there might be further alternatives.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
    void mousePressed()

    {
      if (mouseY <= 100 && mouseX > 0 && mouseX < width/3)          // keyboard button
        KetaiKeyboard.toggle(this);
      else if 
        (mouseY <= 100 && mouseX > width/3 && mouseX < 2*(width/3)) //config button
        isConfiguring=true;
      else if 
        (mouseY <= 100 && mouseX >  2*(width/3) && mouseX < width)  // draw button
      {
        if (isConfiguring)
        {
          background(78, 93, 75);
          isConfiguring=false;
        }
      }
    }

    void keyPressed() {
      if (key =='c')
      {
        //If we have not discovered any devices, try prior paired devices
        if (bt.getDiscoveredDeviceNames().size() > 0)
          connectionList = new KetaiList(this, bt.getDiscoveredDeviceNames());
        else if (bt.getPairedDeviceNames().size() > 0)
          connectionList = new KetaiList(this, bt.getPairedDeviceNames());
      }

      else if (key == 'd')
        bt.discoverDevices();
    }

    void drawUI()
    {
      pushStyle();
      fill(0);
      stroke(255);
      rect(0, 0, width/3, 100);

      if (isConfiguring)
      {
        noStroke();
        fill(78, 93, 75);
      }
      else
        fill(0);

      rect(width/3, 0, width/3, 100);
      if (!isConfiguring)
      {  
        noStroke();
        fill(78, 93, 75);
      }
      else
      {
        fill(0);
        stroke(255);
      }
      rect((width/3)*2, 0, width/3, 100);
      fill(255);

      text("Keyboard", 5, 70);
      text("Bluetooth", width/3+5, 70);
      text("Interact", width/3*2+5, 70);
      popStyle();
    }

    void onKetaiListSelection(KetaiList connectionList)
    {
      String selection = connectionList.getSelection();
      bt.connectToDeviceByName(selection);
      connectionList = null;
    }

Lines 36, 66: the methods pushStyle() and popStyle() allow you to temporarily change the style characteristics of the drawing and to restore them to their previous settings once you are finished. Don't take my word for it - check them out on the Processing reference site.

Lines 69-74: This is the code for connecting to a discovered device

NB. Don't forget to set the Android permissions for the new sketch

Further reading

Ketai

Processing reference