Monday 24 April 2017

Blue Dot - a bluetooth remote for Raspberry Pi

Blue Dot is a really simple way to add Bluetooth remote control to your Raspberry Pi projects.


I created Blue Dot after being asked many times at Picademy “how can I get rid of all these wires?”. 

Blue dot is an android app (client) and really easy to use Python library which allows you to wirelessly control your Python projects, whether that is a light switch, remote camera, robot or anything else you can think of!


See the getting started guide for more info on 'getting started', or follow the tutorial below.

Installation & Use
These instructions assume your Raspberry Pi is running the latest version of Raspbian with Pixel.

You will need a Raspberry Pi with built-in Bluetooth (such as the Pi 3 or Pi Zero W) or a Raspberry Pi and a USB Bluetooth dongle.

Get the app
Download and install the Blue Dot app from the google play store.


If you are wondering why there is no iOS app? Its because iOS doesn't support Bluetooth serial comms; you can only really talk to 'standard devices' (cars, speakers, fitness trackers, etc).

Python library
Open a terminal (Menu > Accessories > Terminal) and type:
sudo apt-get install python3-dbus
sudo pip3 install bluedot
Or if you need to use Python 2 (please dont tho!):
sudo apt-get install python-dbus
sudo pip install bluedot

Pairing
In order to communicate over Bluetooth securely you need to pair your phone to your Raspberry Pi.

On your Android phone:
  1. Open Settings
  2. Select Bluetooth
  3. This will make your phone Discoverable
Using your Raspberry Pi
  1. Click the bluetooth icon on the taskbar
  2. Turn on Bluetooth (if its off)
  3. Click Make Discoverable
  4. Click Add Device
  5. Your phone will appear in the list, select it and click Pair
  6. Enter a PIN code
On your Android phone
  1. Enter the same PIN code when prompted
  2. Click Ok
Code
The simplest way to use the Blue Dot is as a button:
  1. Open Python 3 (Menu > Programming > Python 3)
  2. Create a new file (File > New File)
  3. The following code, will start up the Blue Dot, and wait for it to be pressed:
  4. from bluedot import BlueDot
    bd = BlueDot()
    bd.wait_for_press()
    print("You pressed the blue dot!")
  5. Save your program (File > Save) as mydot.py
  6. Run your program (Run > Run Module)
  7. Open the Blue Dot app
  8. Connect to your Raspberry Pi
  9. Press the Blue Dot
As well as waiting for something to happen you can also call functions when the button is pressed, released or the position its pressed moves.
from bluedot import BlueDot
from signal import pause

def say_hello():
    print("Hello World")

def say_goodbye():
    print("goodbye")

bd = BlueDot()
bd.when_pressed = say_hello
bd.when_released = say_goodbye

pause()
By using the position of where the button is pressed you can use the Blue Dot like a joystick:
from bluedot import BlueDot
from signal import pause

def dpad(pos):
    if pos.top:
        print("up")
    elif pos.bottom:
        print("down")
    elif pos.left:
        print("left")
    elif pos.right:
        print("right")
    elif pos.middle:
        print("fire")

bd = BlueDot()
bd.when_pressed = dpad

pause()
Add to this gpiozero's Robot functions, you can create a Bluetooth controlled robot with very little code.
from bluedot import BlueDot
from gpiozero import Robot
from signal import pause

bd = BlueDot()
robot = Robot(left=(lfpin, lbpin), right=(rfpin, rbpin))

def move(pos):
    if pos.top:
        robot.forward()
    elif pos.bottom:
        robot.backward()
    elif pos.left:
        robot.left()
    elif pos.right:
        robot.right()

def stop():
    robot.stop()

bd.when_pressed = move
bd.when_moved = move
bd.when_released = stop

pause()
Check out the Blue Dot documentation for more information and ideas - you really can do a lot with a simple circle :)

19 comments:

  1. brilliant! Will play with this soon

    ReplyDelete
  2. We've been getting stuck into using Blue Dot since seeing you demo it at the Manchester Raspberry Pi Jamboree in May. We're using David Glaude's stuff to control our Unicorn Hat/ Ikea lamp at https://github.com/dglaude/Blue-Dot-Colour-Picker . Many thanks.

    ReplyDelete
    Replies
    1. Excellent, I saw David's work and instantly thought of you but I didnt have any contact details for you (and couldn't find you on twitter), so Im glad you found it.

      Delete
  3. I'm afraid social media has passed me by but obviously some sort of telekinesis got me to enter the right things in Google. Perhaps it just shows how Google tends to constrain discovery ?

    ReplyDelete
  4. Martin, thank you very much for this incredibly brilliant blue dot! Fine documentation, good API, ... very-very well done! Is the Android app source code also available?

    ReplyDelete
  5. Oh, never mind, missed https://github.com/martinohanlon/BlueDot/tree/master/clients/android

    ReplyDelete
  6. Hi Martin,

    Exactly what I was looking for.
    Just installed and used your project with my robot. Worked like a dream. Well done, keep up the good work.

    ReplyDelete
  7. This comment has been removed by the author.

    ReplyDelete
  8. Martin,
    I was wondering if it would be a good idea to add couple of circles to the BlueDot to create zones. Only visual. The code to determine which zone is being used/pressed/released can be done using simple distance/angle conditions like so:

    def move(pos):
    ....# outer circle:
    ....if pos.distance > .85:
    ........if -45 < pos.angle <= 45:
    ............robot.forward()
    ........elif pos.angle > 135 or pos.angle <= -135:
    ............robot.backward()
    ........elif 45 < pos.angle <= 135:
    ............robot.right()
    ........elif -135 < pos.angle <= -45:
    ............robot.left()
    ....# middle circle:
    ....elif .45 < pos.distance <= .85:
    ........if -45 < pos.angle <= 45:
    ............robot.spine_up()
    ........elif pos.angle > 135 or pos.angle <= -135:
    ............robot.spine_down()
    ........elif 45 < pos.angle <= 135:
    ............robot.base_clockwise()
    ........elif -135 < pos.angle <= -45:
    ............robot.base_counterclockwise()
    ....# inner circle:
    ....elif pos.distance <= .45:
    ........if -45 < pos.angle <= 45:
    ............robot.arm_up()
    ........elif pos.angle > 135 or pos.angle <= -135:
    ............robot.arm_down()
    ........elif 45 < pos.angle <= 135:
    ............robot.neck_up()
    ........elif -135 < pos.angle <= -45:
    ............robot.neck_down()

    ReplyDelete
  9. Hi Martin.
    why will my pi and phone pair up but they wont connect! the pi says no usable services. have tried brand new install plus update etc and followed instructions.

    ReplyDelete
    Replies
    1. Do you get the message "no usable services" when you do the pairing? If so, you can ignore it, you phone and Pi are still paired, it basically telling you that there are no registered services running on the Pi (yet).

      Run Blue Dot and it should connect.

      Delete
    2. I have put a small note on the getting started guide. I had already put it on the pairing Pi to Pi instructions, but not the Pi to Android instructions!

      Delete
  10. Hi Martin!

    This is really good stuff. Thanks a lot. I have a quick question. In the BT Comm code sample you provide, is there a way to terminate the server side at some point? I've tried "s.stop()", but that did not work.

    Thanks in advance.

    ReplyDelete
    Replies
    1. There is a s.stop() method. http://bluedot.readthedocs.io/en/latest/btcommapi.html#bluedot.btcomm.BluetoothServer.stop

      When you tried it, what happened, did you receive an error?

      Delete
    2. This is the return:

      Exception in thread Thread-1:
      Traceback (most recent call last):
      File "/usr/lib/python3.4/threading.py", line 920, in _bootstrap_inner
      self.run()
      File "/usr/lib/python3.4/threading.py", line 868, in run
      self._target(*self._args, **self._kwargs)
      File "/usr/local/lib/python3.4/dist-packages/bluedot/btcomm.py", line 397, in _wait_for_connection
      self._read()
      File "/usr/local/lib/python3.4/dist-packages/bluedot/btcomm.py", line 418, in _read
      self.data_received_callback(data)
      File "/home/pi/Scripts/startup_bluetooth.py", line 39, in data_received
      s.stop()
      File "/usr/local/lib/python3.4/dist-packages/bluedot/btcomm.py", line 360, in stop
      self._conn_thread.stop()
      File "/usr/local/lib/python3.4/dist-packages/bluedot/threads.py", line 26, in stop
      self.join()
      File "/usr/local/lib/python3.4/dist-packages/bluedot/threads.py", line 29, in join
      super(WrapThread, self).join()
      File "/usr/lib/python3.4/threading.py", line 1057, in join
      raise RuntimeError("cannot join current thread")
      RuntimeError: cannot join current thread

      Delete
    3. Thanks. Could you raise an issue on github.com/martinohanlon/BlueDot please. Could you also include your code and I will take a look at it for you.

      Delete
    4. Just had a quick look at your stack trace. Are you by any chance trying to stop the server within your data_received function? i.e. you code looks a bit like this:

      |def data_received(data):
      | # do stuff
      | s.stop()
      |
      |s = BluetoothServer(data_received)


      if so I can understand why you are getting an error. In summary, you cant stop the server because the thread you are trying to stop is the one running the data_received function i.e. you cant stop yourself!

      If you could raise an issue I'll have a think about how that might be resolved, but in the mean time, call stop outside the data_received function, something like this:

      |run_server = False
      |def data_received(data):
      | # do stuff
      | stop_server = True
      |
      |s = BluetoothServer(data_received)
      |while stop_server == False:
      | time.sleep(0.1)
      |s.stop()

      Delete
  11. Excellent work Martin, I really like the dot!!!
    In the "Variable Speed Robot"-example, only 4 pins are used to control 2 motors? However, a h-bridge like L298n requires 6 pins, where 2 are for PWM. Can you please explain how it works with only 2 pins per motor and how to wire to a L298n board. Ref pic here: https://tronixlabs.com.au/news/tutorial-l298n-dual-motor-controller-module-2a-and-arduino

    Many thanks!
    Martin

    ReplyDelete

Note: only a member of this blog may post a comment.