Skip to content

Commit

Permalink
Merge pull request #155 from martinohanlon/dev
Browse files Browse the repository at this point in the history
v2.0.0
  • Loading branch information
Martin O'Hanlon committed Nov 1, 2020
2 parents 489eb61 + 3174a1c commit 831c013
Show file tree
Hide file tree
Showing 57 changed files with 3,392 additions and 1,980 deletions.
24 changes: 20 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Blue Dot allows you to control your Raspberry Pi projects wirelessly - it's a Bl

|bluedotfeature|

|bluedotapp| |bluedotpython|
|bluedotapp| |bluedotjoypad| |bluedotpython|

Created by `Martin O'Hanlon`_ (`@martinohanlon`_, `stuffaboutco.de`_).

Expand Down Expand Up @@ -38,13 +38,17 @@ See the `getting started`_ guide to 'get started'!
More
----

The Blue Dot is a `joystick`_ as well as `button`_. You can tell if the dot was pressed in the middle, on the top, bottom, left or right. You can easily create a `BlueDot controlled Robot`_.
Blue Dot is more than just one `button_`. You can create as many buttons as you want and change their appearance to create your own controller.

Why be restricted by such vague positions like top and bottom though: you can get the exact (x, y) position or even the angle and distance from centre where the dot was pressed.
|bluedotjoypad|

Every `button`_ is also a `joystick`_. You can tell if a button was pressed in the middle, on the top, bottom, left or right. You can easily create a `BlueDot controlled Robot`_.

Why be restricted by such vague positions like top and bottom though: you can get the exact (x, y) position or even the angle and distance from centre where the button was pressed.

Its not all about when the button was pressed either - pressed, released or moved they all work.

The dot doesn't have to be blue, or a dot, you can change its colour, make it square or give it a border.
A button can be any colour, square, given give or hidden!

You can press it, `slide it`_, `swipe it`_, `rotate it`_ - one blue circle can do a lot!

Expand Down Expand Up @@ -89,6 +93,18 @@ Production - under active development. Be sure to raise an `issue`_ if you have
:scale: 100 %
:alt: blue dot python app

.. |bluedotjoypad| image:: https://raw.githubusercontent.com/martinohanlon/BlueDot/master/docs/images/bluedotjoypad_small.png
:height: 148 px
:width: 294 px
:scale: 100 %
:alt: blue dot app as a joy pad controller

.. |bluedotjoypad| image:: https://raw.githubusercontent.com/martinohanlon/BlueDot/master/docs/images/layout_many_buttons_small.png
:height: 148 px
:width: 294 px
:scale: 100 %
:alt: blue dot app with 10 buttons in a 2x5 grid

.. |bluedotfeature| image:: https://raw.githubusercontent.com/martinohanlon/BlueDot/master/docs/images/blue_dot_feature_small.png
:height: 247 px
:width: 506 px
Expand Down
3 changes: 2 additions & 1 deletion bluedot/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .dot import BlueDot, BlueDotPosition, BlueDotInteraction, BlueDotSwipe, BlueDotRotation
from .dot import BlueDot, BlueDotButton
from .interactions import BlueDotInteraction, BlueDotPosition, BlueDotRotation, BlueDotSwipe
from .colors import COLORS
from .mock import MockBlueDot
65 changes: 44 additions & 21 deletions bluedot/app.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import os
os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = '1'

from argparse import ArgumentParser
import pygame
import sys
Expand All @@ -14,11 +17,12 @@
CLIENT_NAME = "Blue Dot Python app"
BORDER_THICKNESS = 0.025

class BlueDotClient(object):
def __init__(self, device, server, fullscreen, width, height):
class BlueDotClient:
def __init__(self, device, server, port, fullscreen, width, height):

self._device = device
self._server = server
self._port = 1 if port is None else port
self._fullscreen = fullscreen

#init pygame
Expand Down Expand Up @@ -54,37 +58,37 @@ def __init__(self, device, server, fullscreen, width, height):
pygame.quit()

def _run(self):
#has a server been specified? If so connected directly
# has a server been specified? If so connected directly
if self._server:
button_screen = ButtonScreen(self._screen, self._font, self._device, self._server, self._width, self._height)
button_screen = ButtonScreen(self._screen, self._font, self._device, self._server, self._port, self._width, self._height)
button_screen.run()
else:
#start the devices screen
devices_screen = DevicesScreen(self._screen, self._font, self._device, self._width, self._height)
# start the devices screen
devices_screen = DevicesScreen(self._screen, self._font, self._device, self._port, self._width, self._height)
devices_screen.run()


class BlueDotScreen(object):
class BlueDotScreen:
def __init__(self, screen, font, width, height):
self.screen = screen
self.font = font
self.width = width
self.height = height

#setup screen attributes
# setup screen attributes
self.frame_rect = pygame.Rect(BORDER, BORDER, self.width - (BORDER * 2) - FONTSIZE - FONTPAD, self.height - (BORDER * 2))
self.close_rect = pygame.Rect(self.width - FONTSIZE - FONTPAD - BORDER, BORDER, FONTSIZE + FONTPAD, FONTSIZE + FONTPAD)

self.draw_screen()

def draw_screen(self):
#set the screen background
# set the screen background
self.screen.fill(GRAY86.rgb)

self.draw_close_button()

def draw_close_button(self):
#draw close button
# draw close button
pygame.draw.rect(self.screen, BLUE.rgb, self.close_rect, 2)
pygame.draw.line(self.screen, BLUE.rgb,
(self.close_rect[0], self.close_rect[1]),
Expand Down Expand Up @@ -153,11 +157,12 @@ def draw_text(self, text, colour, start_y, antiaalias=False, background=None, bo


class DevicesScreen(BlueDotScreen):
def __init__(self, screen, font, device, width, height):
def __init__(self, screen, font, device, port, width, height):
self.bt_adapter = BluetoothAdapter(device = device)
self.port = port

super(DevicesScreen, self).__init__(screen, font, width, height)
#self.draw_screen()
# self.draw_screen()

def draw_screen(self):
self.device_rects = []
Expand Down Expand Up @@ -193,7 +198,7 @@ def run(self):
if self.device_rects[d].collidepoint(pos):
# show the button
self.draw_status_message("Connecting")
button_screen = ButtonScreen(self.screen, self.font, self.bt_adapter.device, self.bt_adapter.paired_devices[d][0], self.width, self.height)
button_screen = ButtonScreen(self.screen, self.font, self.bt_adapter.device, self.bt_adapter.paired_devices[d][0], self.port, self.width, self.height)
button_screen.run()

#redraw the screen
Expand All @@ -213,9 +218,10 @@ def run(self):


class ButtonScreen(BlueDotScreen):
def __init__(self, screen, font, device, server, width, height):
def __init__(self, screen, font, device, server, port, width, height):
self.device = device
self.server = server
self.port = port
self._data_buffer = ""

self.last_x = 0
Expand Down Expand Up @@ -281,7 +287,7 @@ def _process(self, op, pos):
x = round(x, 4)
y = ((pos[1] - self.dot_centre[1]) / float(self.dot_radius)) * -1
y = round(y, 4)
message = "{},{},{}\n".format(op, x, y)
message = "{},0,0,{},{}\n".format(op, x, y)
if op == 2:
if x != self.last_x or y != self.last_y:
self._send_message(message)
Expand All @@ -304,30 +310,46 @@ def _send_message(self, message):
self.draw_error(e)

def _data_received(self, data):
#add the data received to the buffer
# add the data received to the buffer
self._data_buffer += data

#get any full commands ended by \n
# get any full commands ended by \n
last_command = self._data_buffer.rfind("\n")
if last_command != -1:
commands = self._data_buffer[:last_command].split("\n")
#remove the processed commands from the buffer
# remove the processed commands from the buffer
self._data_buffer = self._data_buffer[last_command + 1:]
self._process_commands(commands)

def _process_commands(self, commands):
for command in commands:
params = command.split(",")

invalid_command = False
if len(params) == 5:
if len(params) == 7:

if params[0] == "4":
# currently the python blue dot client only supports 1 button
if params[5] != "1" or params[6] != "1":
print("Error - The BlueDot python client only supports a single button.")

self._colour = parse_color(params[1])
self._square = True if params[2] == "1" else False
self._border = True if params[3] == "1" else False
self._visible = True if params[4] == "1" else False

self._draw_dot()

elif params[0] == "5":
if params[5] == "0" and params[6] == "0":

self._colour = parse_color(params[1])
self._square = True if params[2] == "1" else False
self._border = True if params[3] == "1" else False
self._visible = True if params[4] == "1" else False

self._draw_dot()

else:
invalid_command = True

Expand Down Expand Up @@ -388,7 +410,7 @@ def run(self):
self.bt_client.disconnect()

def _connect(self):
self.bt_client = BluetoothClient(self.server, self._data_received, device = self.device, auto_connect = False)
self.bt_client = BluetoothClient(self.server, self._data_received, port = self.port, device = self.device, auto_connect = False)
try:
self.bt_client.connect()
except:
Expand All @@ -400,13 +422,14 @@ def main():
parser = ArgumentParser(description="Blue Dot Python App")
parser.add_argument("--device", help="The name of the bluetooth device to use (default is hci0)")
parser.add_argument("--server", help="The name or mac address of the bluedot server")
parser.add_argument("--port", help="The port number to use when connecting (default is 1)", type=int)
parser.add_argument("--fullscreen", help="Fullscreen app", action="store_true")
parser.add_argument("--width", type=int, help="A custom screen width (default is {})".format(DEFAULTSIZE[0]))
parser.add_argument("--height", type=int, help="A customer screen height (default is {})".format(DEFAULTSIZE[1]))
args = parser.parse_args()

#start the blue dot client
blue_dot_client = BlueDotClient(args.device, args.server, args.fullscreen, args.width, args.height)
blue_dot_client = BlueDotClient(args.device, args.server, args.port, args.fullscreen, args.width, args.height)

if __name__ == "__main__":
main()
9 changes: 5 additions & 4 deletions bluedot/btcomm.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
BLUETOOTH_TIMEOUT = 0.01


class BluetoothAdapter(object):
class BluetoothAdapter:
"""
Represents and allows interaction with a Bluetooth Adapter.
Expand Down Expand Up @@ -138,7 +138,7 @@ def _expire_pairing(self, timeout):
self.pairable = False


class BluetoothServer(object):
class BluetoothServer:
"""
Creates a Bluetooth server which will allow connections and accept incoming
RFCOMM serial data.
Expand Down Expand Up @@ -380,6 +380,7 @@ def send(self, data):
:param str data:
The data to be sent.
"""
# print(data)
if self._client_connected:
if self._encoding is not None:
data = data.encode(self._encoding)
Expand All @@ -395,7 +396,7 @@ def _send_data(self, data):
:param bytes data:
The data to be sent.
"""
self._client_sock.send(data)
self._client_sock.sendall(data)

def disconnect_client(self):
"""
Expand Down Expand Up @@ -703,7 +704,7 @@ def _send_data(self, data):
:param bytes data:
The data to be sent.
"""
self._client_sock.send(data)
self._client_sock.sendall(data)

def _read(self):
#read until the client is stopped or the client disconnects
Expand Down
2 changes: 1 addition & 1 deletion bluedot/colors.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# color codes obtained from https://www.webucator.com/blog/2015/03/python-color-constants-module/

class Color(object):
class Color:
"""
Represents a color within bluedot. Used to change the color of the dot.
Expand Down
2 changes: 1 addition & 1 deletion bluedot/constants.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
PROTOCOL_VERSION = 1
PROTOCOL_VERSION = 2
CHECK_PROTOCOL_TIMEOUT = 2

0 comments on commit 831c013

Please sign in to comment.