Friday, February 3, 2012

Create a bluetooth joystick for your laptop using your java phone

So, hi all........:)
This is my first technical post. In this session, I hope to share some knowledge about java mobile bluetooth programming using J2ME. This is a project I did after 1 st semester exam in university (So this is bit old). However there are so many mobile technologies available today other than J2ME but there is a considerable amount of crowd using J2ME yet.

In this project main intention is to connect my mobile Nokia 3120 classic with my laptop using bluetooth and control my laptop using the phone. So I had to write two applications for both lap and phone using java.
I hope this works with all mobile phones support java. If not please let me know

First I would like to give the code for bluetooth server for the computer. To use this code there are few requirements
1 - A bluetooth dongle connected to to your pc and properly installed driver softwares
2- Set it to discoverable  mode.

3 - Your mobile phone should be paired with with this dongle



Server application consists with three classes. You can use Netbeans or Eclipse or other IDE to develop the application. In netbeans create a new java application project (With a Main class) and crate 3 classes named Frame , Key and Receive. Then following body to those classes.



***** You will need to add bluecove2.1.0.jar to libraries
http://code.google.com/p/bluecove/downloads/detail?name=bluecove-2.1.0.jar&can=2&q=

----------------------------------------------------------------------------------------

Main.java


public class Main {
    public static void main(String args[]){
        new Frame().setVisible(true);
     
    }
}

----------------------------------------------------------------------------------------
Frame.java


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DataElement;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.ServiceRecord;
import javax.bluetooth.UUID;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
import javax.microedition.io.StreamConnectionNotifier;


public class Frame extends javax.swing.JFrame {

    final Object inquiryCompletedEvent = new Object();
    private LocalDevice local;
    private StreamConnectionNotifier server = null;
    private StreamConnection conn = null;
    private InputStream is;
    private OutputStream os;
    private Key key;

    public Frame() {
        initComponents();
        key = new Key(this);
        Thread t = new Thread(key);
        t.start();
        this.setResizable(false);

    }

    // <editor-fold defaultstate="collapsed" desc="Generated Code">
    private void initComponents() {

        lblMessage = new javax.swing.JLabel();
        btStart = new javax.swing.JButton();
        lblTitle = new javax.swing.JLabel();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        getContentPane().setLayout(new org.netbeans.lib.awtextra.AbsoluteLayout());

        lblMessage.setText("W.D.Upeksha, University of Moratuwa");
        getContentPane().add(lblMessage, new org.netbeans.lib.awtextra.AbsoluteConstraints(20, 40, 230, 20));

        btStart.setText("Start");
        btStart.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                btStartActionPerformed(evt);
            }
        });
        getContentPane().add(btStart, new org.netbeans.lib.awtextra.AbsoluteConstraints(90, 60, -1, -1));

        lblTitle.setText("Bluetooth Server");
        getContentPane().add(lblTitle, new org.netbeans.lib.awtextra.AbsoluteConstraints(20, 20, 180, -1));

        pack();
    }// </editor-fold>

    private void btStartActionPerformed(java.awt.event.ActionEvent evt) {                                         
        try {
            try {

                //doDeviceDiscovery();
                lblMessage.setText("Waiting");
                createService();
                conn = server.acceptAndOpen();
                lblMessage.setText("Connected");
            } catch (IOException ex) {
                lblMessage.setText("Error");
                Logger.getLogger(Frame.class.getName()).log(Level.SEVERE, null, ex);
            }


            String msg = "hello there, client";
            is = conn.openInputStream();
            os = conn.openOutputStream();
            // send data to the server
            os.write(msg.getBytes());
            os.flush();
            // read data from the server
            connection();
            conn.close();
            btStart.setVisible(false);
            lblMessage.setText("Connected");
        } catch (IOException ex) {
            Logger.getLogger(Frame.class.getName()).log(Level.SEVERE, null, ex);
        }

    }                                        

    void connection() {
        Receive rec = new Receive(is, this);
        Thread thr = new Thread(rec);
        thr.start();

    }

    void reset(String str) {
        lblMessage.setText(str);
        key.setStr(str.trim());    
    }

    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new Frame().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify
    private javax.swing.JButton btStart;
    private javax.swing.JLabel lblMessage;
    private javax.swing.JLabel lblTitle;
    // End of variables declaration

    private void createService() {
        try {
            ServiceRecord record = null;
            try {
                try {
                    local = LocalDevice.getLocalDevice();
                    local.setDiscoverable(DiscoveryAgent.GIAC);
                } catch (BluetoothStateException ex) {
                    Logger.getLogger(Frame.class.getName()).log(Level.SEVERE, null, ex);
                }
                String connectionURL = "btspp://localhost:393a84ee7cd111d89527000bdb544cb1;" + "authenticate=false;encrypt=false;name=RFCOMM Server";
                server = (StreamConnectionNotifier) Connector.open(connectionURL);
            } catch (IOException ex) {
                Logger.getLogger(Frame.class.getName()).log(Level.SEVERE, null, ex);
            }
            record = local.getRecord(server);
            DataElement elm = null;
            elm = new DataElement(DataElement.DATSEQ);
            elm.addElement(new DataElement(DataElement.UUID, new UUID(0x1002)));
            record.setAttributeValue(0x0005, elm);

            elm = new DataElement(DataElement.STRING, "BT Benchmark");
            record.setAttributeValue(0x101, elm);

            elm = new DataElement(DataElement.STRING, "Upeksha");
            record.setAttributeValue(0x102, elm);

        } catch (Exception ex) {
            Logger.getLogger(Frame.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

---------------------------------------------------------------------------------------------------------
Key.java

import java.awt.AWTException;
import java.awt.Robot;
import java.util.logging.Level;
import java.util.logging.Logger;


public class Key implements Runnable{
    private String str = "RELEASE";
    private Robot robo;
    private Frame frm;
    public Key(Frame frm){
        try {
            this.frm = frm;
            robo = new Robot();
        } catch (AWTException ex) {
            Logger.getLogger(Key.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void setStr(String str) {
        this.str = str;
    }

    
    @Override
    public void run() {
       while(true){
            try {
                if (str.equals("UP")) {
                    robo.keyPress(65);
                } else if (str.equals("DOWN")) {
                    robo.keyPress(66);
                } else if (str.equals("RIGHT")) {
                    robo.keyPress(67);
                } else if (str.equals("LEFT")) {
                    robo.keyPress(68);
                } else if (str.equals("UP_RELEASE")) {
                    robo.keyRelease(65);
                
                }else if (str.equals("DOWN_RELEASE")) {
                    robo.keyRelease(66);
                
                }else if (str.equals("RIGHT_RELEASE")) {
                    robo.keyRelease(67);
                
                }else if (str.equals("LEFT_RELEASE")) {
                    robo.keyRelease(68);
                
                } 
                Thread.sleep(100);
                
            } catch (InterruptedException ex) {
                Logger.getLogger(Key.class.getName()).log(Level.SEVERE, null, ex);
            }           
       }
    }    
}

--------------------------------------------------------------------------------------
Receive.java

import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Receive implements Runnable {

    InputStream is;
    Frame frm;

    public Receive(InputStream is, Frame frm) {
        this.is = is;
        this.frm = frm;
    }

    public void run() {
        String msg;
        try {
            while (true) {
                byte[] buffer = new byte[100];
                is.read(buffer);
                msg = new String(buffer);
                frm.reset(msg.trim());
                Thread.sleep(100);
            }
        } catch (InterruptedException ex) {
            Logger.getLogger(Receive.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(Receive.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}


----------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------

Then we need an application to install into mobile phone to connect to server
Here is the code for it. For that it is recommended to use a netbeans mobile application or you can use eclipse (I'm not familiar with eclipse :) ).


Then create a new midlet named Midlet and two java classes named KeyCanvas and Communication

-------------------------------------------------------------------------------------------------------

Midlet.java

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Vector;
import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DataElement;
import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.DiscoveryListener;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRecord;
import javax.bluetooth.UUID;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

public class Midlet extends MIDlet implements DiscoveryListener{

    private Display display;
    final Object inquiryCompletedEvent = new Object();
    private Vector devicesDiscovered;
    private ServiceRecord[] servicesFound = null;
    private ServiceRecord service;
    private DiscoveryAgent agent = null;
    private LocalDevice local;
    private StreamConnection conn = null;
    private Form form;
    private List lst;
    private Command cmd;
    int face = 0;

    public void startApp() {
        try {
            StringItem stringItem = new StringItem("Remort", "Testing Connectio\n");
            doDeviceDiscovery();
            form = new Form(null, new Item[]{stringItem});
            lst = new List("Devices", List.IMPLICIT);
            display = Display.getDisplay(this);
            display.setCurrent(form);
            cmd = new Command("select", Command.EXIT, 0);
            lst.addCommand(cmd);
            lst.setCommandListener(new CommandListener() {
                public void commandAction(Command arg0, Displayable arg1) {
                    if (face == 0) {
                        int num = lst.getSelectedIndex();
                        doServiceSearch((RemoteDevice) (devicesDiscovered.elementAt(num)));
                    }
                    if (face == 1) {
                        try {
                            int num = lst.getSelectedIndex();
                            service = servicesFound[num];
                            DataElement el = (DataElement) service.getAttributeValue(0x100);
                            form.deleteAll();
                            form.append(el.getValue() + "");
                            display.setCurrent(form);

                            String connectionURL = service.getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
                            conn = (StreamConnection) Connector.open(connectionURL);
                            form.append(" Connected");
                            OutputStream os = conn.openOutputStream();
                            String msg = "Connected to Client";
                            os.write(msg.getBytes());
                            os.flush();
                            form.append(" Data sent");
                            byte[] buffer = new byte[100];
                            connection();
                            Displayable can = new KeyCanvas(os);
                            display.setCurrent(can);
                        } catch (IOException ex) {
                            ex.printStackTrace();
                        }
                    }

                }
            });
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public void pauseApp() {
    }

    public void destroyApp(boolean unconditional) {
    }

    void connection() throws IOException {
        InputStream is = conn.openInputStream();
        Communication com = new Communication(this, is);
        new Thread(com).start();

    }

    void scan() {
        synchronized (inquiryCompletedEvent) {
            try {
                boolean started = LocalDevice.getLocalDevice().getDiscoveryAgent().startInquiry(DiscoveryAgent.GIAC, this);
                if (started) {
                    form.append("wait for device inquiry to complete...");
                    inquiryCompletedEvent.wait();
                    form.append(devicesDiscovered.size() + " device(s) found");
                }
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            } catch (BluetoothStateException ex) {
                ex.printStackTrace();
            }
        }
    }

    public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) {
        try {
            form.append("Device " + btDevice.getBluetoothAddress() + " found");
            devicesDiscovered.addElement(btDevice);
            try {
                form.append(" name " + btDevice.getFriendlyName(false));
            } catch (IOException cantGetDeviceName) {
            }
            lst.append(btDevice.getFriendlyName(false), null);
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    public void servicesDiscovered(int transID, ServiceRecord[] serviceRecord) {
        DataElement nameElement = null;
        lst.deleteAll();
        servicesFound = serviceRecord;
        for (int i = 0; i < serviceRecord.length; i++) {
            nameElement = (DataElement) serviceRecord[i].getAttributeValue(0x100);
            if (nameElement != null && nameElement.getDataType() == DataElement.STRING) {
                lst.append((String) nameElement.getValue(), null);
            }
        }

    }

    public void serviceSearchCompleted(int arg0, int arg1) {
        face = 1;
    }

    public void inquiryCompleted(int discType) {
        form.append("Device Inquiry completed!");
        display.setCurrent(lst);
        synchronized (inquiryCompletedEvent) {
            inquiryCompletedEvent.notifyAll();

        }
    }

    private void doDeviceDiscovery() {
        try {
            local = LocalDevice.getLocalDevice();
        } catch (BluetoothStateException bse) {
            // Error handling code here
        }
        agent = local.getDiscoveryAgent();
        devicesDiscovered = new Vector();
        try {
            if (!agent.startInquiry(DiscoveryAgent.GIAC, this)) {
             
            }
        } catch (BluetoothStateException bse) {
        }
    }

    private void doServiceSearch(RemoteDevice device) {

        int[] attributes = {0x100, 0x101, 0x102};
        UUID[] uuids = new UUID[1];
        uuids[0] = new UUID(0x1002);
        try {
            agent.searchServices(attributes, uuids, device, this);
        } catch (BluetoothStateException e) {
            // Error handling code here
        }
    }

    public Form getForm() {
        return form;
    }
}

------------------------------------------------------------------------------------------

Communication.java


import java.io.IOException;
import java.io.InputStream;

public class Communication implements Runnable {
    Midlet mid;
    InputStream is;
    Communication(Midlet aThis, InputStream is) {
        this.mid=aThis;
        this.is=is;
    }
    public void run() {
        while(true){
            try {
                Thread.sleep(100);
                byte buffer[] = new byte[100];
                is.read(buffer);
                String msg = new String(buffer);
                mid.getForm().deleteAll();
                mid.getForm().append(msg.trim());
            } catch (IOException ex) {
                ex.printStackTrace();
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
         
        }
    }
 
}

----------------------------------------------------------------------------------------
KeyCanvas.java


import java.io.IOException;
import java.io.OutputStream;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;

public class KeyCanvas extends Canvas {

    private Font mFont = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_PLAIN, Font.SIZE_MEDIUM);
    private String mMessage = "[Press keys]";
    private OutputStream os;

    public KeyCanvas(OutputStream os) {
        this.os = os;
    }

    public void paint(Graphics g) {
        int w = getWidth();
        int h = getHeight();

        g.setGrayScale(255);
        g.fillRect(0, 0, w - 1, h - 1);
        g.setGrayScale(0);
        g.drawRect(0, 0, w - 1, h - 1);

        g.setFont(mFont);

        int x = w / 2;
        int y = h / 2;

        g.drawString(mMessage, x, y, Graphics.BASELINE | Graphics.HCENTER);
    }

    protected void keyPressed(int keyCode) {
        try {
            int gameAction = getGameAction(keyCode);
            switch (gameAction) {
                case UP:
                    mMessage = "UP";
                    os.write(mMessage.getBytes());
                    break;
                case DOWN:
                    mMessage = "DOWN";
                    os.write(mMessage.getBytes());
                    break;
                case LEFT:
                    mMessage = "LEFT";
                    os.write(mMessage.getBytes());
                    break;
                case RIGHT:
                    mMessage = "RIGHT";
                    os.write(mMessage.getBytes());
                    break;
                case FIRE:
                    mMessage = "FIRE";
                    os.write(mMessage.getBytes());
                    break;
                case GAME_A:
                    mMessage = "GAME_A";
                    os.write(mMessage.getBytes());
                    break;
                case GAME_B:
                    mMessage = "GAME_B";
                    os.write(mMessage.getBytes());
                    break;
                case GAME_C:
                    mMessage = "GAME_C";
                    os.write(mMessage.getBytes());
                    break;
                case GAME_D:
                    mMessage = "GAME_D";
                    os.write(mMessage.getBytes());
                    break;
                default:
                    mMessage = "";
                    os.write(mMessage.getBytes());
                    break;
            }
            os.flush();
            repaint();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    protected void keyReleased(int keyCode) {
        try {
            int gameAction = getGameAction(keyCode);
            switch (gameAction) {
                case UP:
                    mMessage = "UP_RELEASE";
                    os.write(mMessage.getBytes());
                    break;
                case DOWN:
                    mMessage = "DOWN_RELEASE";
                    os.write(mMessage.getBytes());
                    break;
                case LEFT:
                    mMessage = "LEFT_RELEASE";
                    os.write(mMessage.getBytes());
                    break;
                case RIGHT:
                    mMessage = "RIGHT_RELEASE";
                    os.write(mMessage.getBytes());
                    break;
                case FIRE:
                    mMessage = "RELEASE";
                    os.write(mMessage.getBytes());
                    break;
                case GAME_A:
                    mMessage = "RELEASE";
                    os.write(mMessage.getBytes());
                    break;
                case GAME_B:
                    mMessage = "RELEASE";
                    os.write(mMessage.getBytes());
                    break;
                case GAME_C:
                    mMessage = "RELEASE";
                    os.write(mMessage.getBytes());
                    break;
                case GAME_D:
                    mMessage = "RELEASE";
                    os.write(mMessage.getBytes());
                    break;
                default:
                    mMessage = "RELEASE";
                    os.write(mMessage.getBytes());
                    break;
            }
            os.flush();
            repaint();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

-----------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------
After compiling the project you will get a jar file and jad file in the dist folder of your project. You can simply copy those files in to your mobile phone and run it.


So... Now we have completed the programming part. Then lets's run the projects. First you have to run Server application. Make sure your bluetooth device is turned on. You will get a window like this





He he .... You can put your name instead of my name :D. Then click start. You will realize that start button is   pressed. It means that server waits for a client. Then you can run the application in your mobile phone. Fisrt turn on your mobile phone's bluetooth device.


As soon as you turn on the application, it will search for the mobile devices. If your computer's bluetooth device is discoverable, it will be shown.


Then you have to select it from the list.


After selecting the device, application will search for the services given by the server. Choose RFCOMM Server. It creates serial data transfer between client and server.



Then you will see that Server application shows that it has connected with the client. It means everything is ok. :)



Press down button of the phone. Can you see that server gets the message? if yes you are done. :)



Open notepad and press top, down, left and right buttons. You will see that something is written on the notepad. Server generates keyboard events according to the input you gave to the phone. If you want to change these key events go to Key.java and change those events. Ahh haaaa :)
Then open your favorite racing game and configure the controllers according to the key events generated by server. Then enjoy your wireless Joystick

I know that there is lot of stuff in this post is bit hard to the people who are new to bluetooth programming. Same happened to me at the start of the project. However I can provide you the referring  materials I have used.
Use this thesis to learn about bluetooth programming in J2ME.
All the stuff I have used is clearly described here.

Finally.. It's your part. If you feel this post was useful to you in anyway please put a comment. Doesn't matter whether it is good or bad. I only need is a feedback to improve myself

Thank you



6 comments:

  1. ela macho.. keep it up.
    BTW me coding dakkama java epaaa wenawaaa........

    ReplyDelete
  2. ලොවෙත් නෑ. කොහේ ගියත් ඉතින් අපේ එකෙක් ම තමයි. එල එල. ඉදිරියටත් මේ වගේම වටිනා පෝස්ට වැටේවා.. දැන්මම මේක හදල බලන්න ඕන.

    ReplyDelete
  3. Mcn. if you explain how each part of code works it'll be more useful. Anyway great work mcn, keep it up..........

    ReplyDelete
    Replies
    1. Ok machan I'll edit this asap :). Anyway thnx for the suggestion

      Delete
  4. I didn't get: does it use SPP profile for communications? How do you set up the bluetooth connection?
    I'm looking for a similar code. I need my Nokia to start sending 12345678 characters depending on the joystick position. And stop sending immediately after the joystick is released.
    It seems, I can't just alter the mMessage variable for this purpose as your code sends message just once. or I can alter the firmware of my toy car! yahooooooo! probably not that bad idea. what happens if up and left are pressed simultaneously? it sends 'up' then 'left' and then it waits till the button is released?
    the last big question is how do I compile the code without installing eclipse etc (running a mac here)

    ReplyDelete
  5. got a problem here
    http://www.youtube.com/watch?v=9ldoaW6MnD0&feature=youtu.be
    instead of a server I want to connect with your client to my 'robot'
    the rest setup is probably the same as yours. what could be the problem?

    ReplyDelete