Jakeler / ble-serial

"RFCOMM for BLE" a UART over Bluetooth low energy (4+) bridge for Linux, Mac and Windows
https://blog.ja-ke.tech/tags/#bluetooth
MIT License
274 stars 38 forks source link

Escape characters printed as literals, and issues with line vs character mode in TCP/telnet #105

Open qupada opened 3 months ago

qupada commented 3 months ago

Describe the bug Using 2.8.0 installed from PIP on Windows 11, with Python 3.12 from the Windows Store. Also used com0com version 2.x (set up using install portname=com10 portname=com11 from CLI installer), as 3.0 had the all too familiar signing issues.

While trying to find a way to operate the serial console entirely inside a WSL2 environment (pass-through of straight COM ports apparently is no longer operational, only works in WSL1), I decided to try the socket option, presuming any standard telnet client would be sufficient.

I created a serial cross-over adapter with two RJ45 punch-down jacks (connecting pins 4+5 straight through, swapping pins 3/6), plugged a USB serial adapter into the PC, attached that to WSL with usbipd.exe and spawned a login prompt on it with sudo agetty -L ttyUSB0 9600 vt102

Connection to the Bluetooth adapter was lanched with: ble-serial.exe -d "48:87:2D:9C:01:51" --expose-tcp-host 172.25.128.1 --expose-tcp-port 5000 -v

Once port 5000 was listening, connecting a telnet client (tested both telnet and inetutils-telnet, or screen //telnet in WSL, and PuTTY on the Windows host) produced the Linux login prompt, but most input was repeated (as if both local and remote echo was happening). Entering the username printed it a second time, and send a 2nd \n which submitted the password prompt too.

Attempting to change local echo, line endings, etc in Telnet made no appreciable difference to the outcome, neither did most of the options to agetty (I didn't try other getty implementations however).

Using COM port mode with ble-serial.exe -d "48:87:2D:9C:01:51" -p COM10 does not exhibit this behaviour, only TCP.

Log messages

Exact series of operations, swinging for minimum number of lines of console output:

09:17:39.329 | DEBUG | main.py: Running: Namespace(verbose=1, timeout=5.0, adapter='hci0', mtu=20, device='48:87:2D:9C:01:51', addr_type='public', service_uuid=None, write_uuid=None, read_uuid=None, mode='rw', filename=None, binlog=False, port='BLE', tcp_host='172.25.128.1', tcp_port=5000)
09:17:39.329 | DEBUG | proactor_events.py: Using proactor: IocpProactor
09:17:39.330 | DEBUG | tcp_socket.py: host='172.25.128.1':port=5000, mtu=20
09:17:39.330 | INFO | ble_interface.py: Receiver set up
09:17:39.471 | INFO | ble_interface.py: Trying to connect with 48:87:2D:9C:01:51: None
09:17:41.621 | INFO | ble_interface.py: Device 48:87:2D:9C:01:51 connected
09:17:41.621 | DEBUG | ble_interface.py: No write uuid specified, trying builtin list
09:17:41.621 | DEBUG | ble_interface.py: Characteristic candidates for write:
        0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX ['read', 'write-without-response', 'write', 'notify']
09:17:41.621 | INFO | ble_interface.py: Found write characteristic 0000ffe1-0000-1000-8000-00805f9b34fb (H. 24)
09:17:41.622 | DEBUG | ble_interface.py: No notify uuid specified, trying builtin list
09:17:41.622 | DEBUG | ble_interface.py: Characteristic candidates for notify:
        0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX ['read', 'write-without-response', 'write', 'notify']
09:17:41.623 | INFO | ble_interface.py: Found notify characteristic 0000ffe1-0000-1000-8000-00805f9b34fb (H. 24)
09:17:41.645 | INFO | main.py: Running main loop!
09:17:41.646 | INFO | tcp_socket.py: TCP server started
09:17:41.646 | DEBUG | tcp_socket.py: Listening on <asyncio.TransportSocket fd=1904, family=2, type=1, proto=6, laddr=('172.25.128.1', 5000)>
09:17:44.651 | DEBUG | tcp_socket.py: Waiting for client connection
09:17:45.543 | INFO | tcp_socket.py: New TCP peer connected: ('172.25.131.135', 34834)
09:17:47.657 | DEBUG | tcp_socket.py: Waiting for client connection
09:17:48.624 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'\r\r\nUbuntu 22.04.4 LTS laptop ttyUSB0\r\n\r\nlaptop login: ')
09:17:48.624 | DEBUG | tcp_socket.py: Sending: bytearray(b'\r\r\nUbuntu 22.04.4 LTS laptop ttyUSB0\r\n\r\nlaptop login: ')
09:17:51.837 | DEBUG | tcp_socket.py: Received b'qupada\r\n'
09:17:51.837 | DEBUG | ble_interface.py: Sending b'qupada\r\n'
09:17:52.041 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'qupada\r\r\nPassword: \r\n')
09:17:52.041 | DEBUG | tcp_socket.py: Sending: bytearray(b'qupada\r\r\nPassword: \r\n')
09:17:55.882 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'\r\nLogin incorrect\r\n')
09:17:55.882 | DEBUG | tcp_socket.py: Sending: bytearray(b'\r\nLogin incorrect\r\n')
09:17:55.883 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'laptop login: ')
09:17:55.883 | DEBUG | tcp_socket.py: Sending: bytearray(b'laptop login: ')
09:17:56.487 | WARNING | ble_interface.py: Device 48:87:2D:9C:01:51 disconnected
09:17:56.487 | INFO | ble_interface.py: Stopping Bluetooth event loop
09:17:56.494 | DEBUG | main.py: Completed Tasks: [(<coroutine object BLE_interface.send_loop at 0x00000232033E6670>, None)]
09:17:56.495 | DEBUG | main.py: Pending Tasks: [<coroutine object TCP_Socket.run_loop at 0x00000232031B7C40>]
09:17:56.495 | WARNING | main.py: Shutdown initiated
09:17:56.495 | INFO | main.py: Shutdown complete.

And the output received in the telnet window (repeated newlines are visible):

qupada@laptop:~$ telnet 172.25.128.1 5000
Trying 172.25.128.1...
Connected to 172.25.128.1.
Escape character is '^]'.

Ubuntu 22.04.4 LTS laptop ttyUSB0

laptop login: qupada
qupada
Password:

Login incorrect

Switching from line to character mode in telnet with ^] mode character almost fixes the issue, but some escape characters appear to be misinterpreted:

$ telnet 172.25.128.1 5000
Trying 172.25.128.1...
Connected to 172.25.128.1.
Escape character is '^]'.
^]
telnet> mode character

Ubuntu 22.04.4 LTS laptop ttyUSB0

laptop login: qupada
Password:
^@
Login incorrect
10:30:49.400 | DEBUG | main.py: Running: Namespace(verbose=1, timeout=5.0, adapter='hci0', mtu=20, device='48:87:2D:9C:01:51', addr_type='public', service_uuid=None, write_uuid=None, read_uuid=None, mode='rw', filename=None, binlog=False, port='BLE', tcp_host='172.25.128.1', tcp_port=5000)
10:30:49.401 | DEBUG | proactor_events.py: Using proactor: IocpProactor
10:30:49.402 | DEBUG | tcp_socket.py: host='172.25.128.1':port=5000, mtu=20
10:30:49.402 | INFO | ble_interface.py: Receiver set up
10:30:53.334 | INFO | ble_interface.py: Trying to connect with 48:87:2D:9C:01:51: None
10:30:55.687 | INFO | ble_interface.py: Device 48:87:2D:9C:01:51 connected
10:30:55.687 | DEBUG | ble_interface.py: No write uuid specified, trying builtin list
10:30:55.687 | DEBUG | ble_interface.py: Characteristic candidates for write:
        0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX ['read', 'write-without-response', 'write', 'notify']
10:30:55.688 | INFO | ble_interface.py: Found write characteristic 0000ffe1-0000-1000-8000-00805f9b34fb (H. 24)
10:30:55.688 | DEBUG | ble_interface.py: No notify uuid specified, trying builtin list
10:30:55.688 | DEBUG | ble_interface.py: Characteristic candidates for notify:
        0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX ['read', 'write-without-response', 'write', 'notify']
10:30:55.689 | INFO | ble_interface.py: Found notify characteristic 0000ffe1-0000-1000-8000-00805f9b34fb (H. 24)
10:30:55.716 | INFO | main.py: Running main loop!
10:30:55.717 | INFO | tcp_socket.py: TCP server started
10:30:55.718 | DEBUG | tcp_socket.py: Listening on <asyncio.TransportSocket fd=1616, family=2, type=1, proto=6, laddr=('172.25.128.1', 5000)>
10:30:58.723 | DEBUG | tcp_socket.py: Waiting for client connection
10:30:59.434 | INFO | tcp_socket.py: New TCP peer connected: ('172.25.131.135', 46308)
10:31:01.719 | DEBUG | tcp_socket.py: Waiting for client connection
10:31:03.297 | DEBUG | tcp_socket.py: Received b'\xff\xfd\x03\xff\xfd\x01'
10:31:03.297 | DEBUG | ble_interface.py: Sending b'\xff\xfd\x03\xff\xfd\x01'
10:31:06.565 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'\r\r\nUbuntu 22.04.4 LTS laptop ttyUSB0\r\n\r\nlaptop login: ')
10:31:06.565 | DEBUG | tcp_socket.py: Sending: bytearray(b'\r\r\nUbuntu 22.04.4 LTS laptop ttyUSB0\r\n\r\nlaptop login: ')
10:31:08.737 | DEBUG | tcp_socket.py: Received b'q'
10:31:08.737 | DEBUG | ble_interface.py: Sending b'q'
10:31:08.878 | DEBUG | tcp_socket.py: Received b'u'
10:31:08.962 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'q')
10:31:08.962 | DEBUG | tcp_socket.py: Sending: bytearray(b'q')
10:31:08.963 | DEBUG | ble_interface.py: Sending b'u'
10:31:09.054 | DEBUG | tcp_socket.py: Received b'p'
10:31:09.081 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'u')
10:31:09.083 | DEBUG | tcp_socket.py: Sending: bytearray(b'u')
10:31:09.083 | DEBUG | ble_interface.py: Sending b'p'
10:31:09.110 | DEBUG | tcp_socket.py: Received b'a'
10:31:09.202 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'p')
10:31:09.203 | DEBUG | tcp_socket.py: Sending: bytearray(b'p')
10:31:09.203 | DEBUG | ble_interface.py: Sending b'a'
10:31:09.223 | DEBUG | tcp_socket.py: Received b'd'
10:31:09.318 | DEBUG | tcp_socket.py: Received b'a'
10:31:09.322 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'a')
10:31:09.322 | DEBUG | tcp_socket.py: Sending: bytearray(b'a')
10:31:09.323 | DEBUG | ble_interface.py: Sending b'd'
10:31:09.442 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'd')
10:31:09.442 | DEBUG | tcp_socket.py: Sending: bytearray(b'd')
10:31:09.442 | DEBUG | ble_interface.py: Sending b'a'
10:31:09.623 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'a')
10:31:09.623 | DEBUG | tcp_socket.py: Sending: bytearray(b'a')
10:31:09.682 | DEBUG | tcp_socket.py: Received b'\r\x00'
10:31:09.682 | DEBUG | ble_interface.py: Sending b'\r\x00'
10:31:09.802 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'\r\r\nPassword: ')
10:31:09.803 | DEBUG | tcp_socket.py: Sending: bytearray(b'\r\r\nPassword: ')
10:31:11.712 | DEBUG | tcp_socket.py: Received b'p'
10:31:11.713 | DEBUG | ble_interface.py: Sending b'p'
10:31:11.816 | DEBUG | tcp_socket.py: Received b'a'
10:31:11.816 | DEBUG | ble_interface.py: Sending b'a'
10:31:11.998 | DEBUG | tcp_socket.py: Received b's'
10:31:11.999 | DEBUG | ble_interface.py: Sending b's'
10:31:12.112 | DEBUG | tcp_socket.py: Received b's'
10:31:12.112 | DEBUG | ble_interface.py: Sending b's'
10:31:12.303 | DEBUG | tcp_socket.py: Received b'w'
10:31:12.304 | DEBUG | ble_interface.py: Sending b'w'
10:31:12.414 | DEBUG | tcp_socket.py: Received b'o'
10:31:12.414 | DEBUG | ble_interface.py: Sending b'o'
10:31:12.509 | DEBUG | tcp_socket.py: Received b'r'
10:31:12.510 | DEBUG | ble_interface.py: Sending b'r'
10:31:12.657 | DEBUG | tcp_socket.py: Received b'd'
10:31:12.658 | DEBUG | ble_interface.py: Sending b'd'
10:31:12.759 | DEBUG | tcp_socket.py: Received b'\r\x00'
10:31:12.802 | DEBUG | ble_interface.py: Sending b'\r\x00'
10:31:12.923 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'\r\n')
10:31:12.924 | DEBUG | tcp_socket.py: Sending: bytearray(b'\r\n')
10:31:12.924 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'^@')
10:31:12.924 | DEBUG | tcp_socket.py: Sending: bytearray(b'^@')
10:31:15.924 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'\r\nLogin incorrect\r\n')
10:31:15.924 | DEBUG | tcp_socket.py: Sending: bytearray(b'\r\nLogin incorrect\r\n')
10:31:15.925 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'laptop login: ')
10:31:15.925 | DEBUG | tcp_socket.py: Sending: bytearray(b'laptop login: ')
10:31:19.441 | WARNING | main.py: Shutdown initiated
10:31:22.825 | WARNING | ble_interface.py: Device 48:87:2D:9C:01:51 disconnected
10:31:22.825 | INFO | ble_interface.py: Stopping Bluetooth event loop
10:31:22.827 | INFO | ble_interface.py: Bluetooth disconnected
10:31:22.827 | INFO | main.py: Shutdown complete.
10:31:22.828 | DEBUG | main.py: Exit due to KeyboardInterrupt

Specifically, if switching to COM port mode, I do not see these lines when pressing enter on the password prompt:

10:31:12.759 | DEBUG | tcp_socket.py: Received b'\r\x00'
10:31:12.802 | DEBUG | ble_interface.py: Sending b'\r\x00'
10:31:12.923 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'\r\n')
10:31:12.924 | DEBUG | tcp_socket.py: Sending: bytearray(b'\r\n')
10:31:12.924 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'^@')
10:31:12.924 | DEBUG | tcp_socket.py: Sending: bytearray(b'^@')

but instead this (ignoring the "no job control" message, this is at least a successful login):

10:56:55.655 | DEBUG | windows_com0com.py: Read: b'\r'
10:56:55.655 | DEBUG | ble_interface.py: Sending b'\r'
10:56:55.730 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'\r\n')
10:56:55.731 | DEBUG | windows_com0com.py: Write: bytearray(b'\r\n')
10:56:55.909 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'-bash: cannot set terminal process group (5669): Inappropriate ioctl for device\r\n-bash: no job control in this shell\r\n')
10:56:55.910 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'\x1b[?2004hqupada@laptop:~$ ')
10:56:55.910 | DEBUG | windows_com0com.py: Write: bytearray(b'-bash: cannot set terminal process group (5669): Inappropriate ioctl for device\r\n-bash: no job control in this shell\r\n')
10:56:55.910 | DEBUG | windows_com0com.py: Write: bytearray(b'\x1b[?2004hqupada@laptop:~$ ')

Amongst other things I've tried, trying to force character mode from either .telnetrc does not appear to be functional as it appears to refuse to apply these options unless port 23 is in use, nor by sending telnet control characters from the host (ie per https://stackoverflow.com/questions/273261/force-telnet-client-into-character-mode) directly via echo $'\377\375\042\377\373\001' | sudo tee /dev/ttyUSB0 or similar. The ^] method to input mode character seems most reliable, but is irritatingly non-automatable.

At this point I'm not sure whether it's a matter of one too many translations between layers of this unholy pancake stack, or now it's something that I personally am doing wrong.

I should also note my attempt to use TCP here is in large part in aid of not having to consider terminal baud rate at any part of the process. The Bluetooth adapter I purchased features hardware switching between 2400/4800/9600/19200/38400/57600/115200 rates, and I presume what arrives over the air has shed any notion of that serial port speed, indeed I can connect the virtual com port at 115200 while the terminal connected from the PC to the bluetooth adapter is at 9600.

Setup:

Additional Context While using a USB serial adapter in this way is my contrived but easily-reproduced (as both ends of the connection can exist and be controlled on the same PC) example, I tested this with the real hardware console port on an switch made by Arista Networks and it exhibits the same behaviour. Again, direct COM port mode in PuTTY works perfectly.

After successful login, am still seing random ^@ appear, and some newlines being printed as a literal ^M in the console instead of a newline:

Line mode (duplicated text, duplicate newlines, password not hidden):

sw1 login: admin
admin
Password: password

Last login: Sun Jun  9 13:37:41 from 10.2.20.60

sw1#
sw1#

sw1#
sw1#show int stat
show int stat
Port       Name              Status       Vlan     Duplex Speed  Type         Flags Encapsulation

Character mode (escape characters printed literally):

sw1 login: admin
^@Password:
Last login: Sun Jun  9 13:41:19 on ttyS0
^@sw1#^M^M^M^M^M^M^M^M^M^M
13:45:53.806 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'\r\nsw1 login: ')
13:45:53.806 | DEBUG | tcp_socket.py: Sending: bytearray(b'\r\nsw1 login: ')
13:45:54.623 | DEBUG | tcp_socket.py: Received b'a'
13:45:54.623 | DEBUG | ble_interface.py: Sending b'a'
13:45:54.669 | DEBUG | tcp_socket.py: Received b'd'
13:45:54.706 | DEBUG | ble_interface.py: Sending b'd'
13:45:54.749 | DEBUG | tcp_socket.py: Received b'm'
13:45:54.765 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'a')
13:45:54.765 | DEBUG | tcp_socket.py: Sending: bytearray(b'a')
13:45:54.827 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'd')
13:45:54.827 | DEBUG | tcp_socket.py: Sending: bytearray(b'd')
13:45:54.828 | DEBUG | ble_interface.py: Sending b'm'
13:45:54.830 | DEBUG | tcp_socket.py: Received b'i'
13:45:54.933 | DEBUG | tcp_socket.py: Received b'n'
13:45:54.945 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'm')
13:45:54.945 | DEBUG | tcp_socket.py: Sending: bytearray(b'm')
13:45:54.946 | DEBUG | ble_interface.py: Sending b'i'
13:45:55.066 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'i')
13:45:55.066 | DEBUG | tcp_socket.py: Sending: bytearray(b'i')
13:45:55.066 | DEBUG | ble_interface.py: Sending b'n'
13:45:55.146 | DEBUG | tcp_socket.py: Received b'\r\x00'
13:45:55.187 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'n')
13:45:55.188 | DEBUG | tcp_socket.py: Sending: bytearray(b'n')
13:45:55.189 | DEBUG | ble_interface.py: Sending b'\r\x00'
13:45:55.306 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'\r\n')
13:45:55.306 | DEBUG | tcp_socket.py: Sending: bytearray(b'\r\n')
13:45:55.366 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'Password: ')
13:45:55.366 | DEBUG | tcp_socket.py: Sending: bytearray(b'Password: ')
13:45:56.000 | DEBUG | tcp_socket.py: Received b'p'
13:45:56.000 | DEBUG | ble_interface.py: Sending b'p'
13:45:56.206 | DEBUG | tcp_socket.py: Received b'a'
13:45:56.206 | DEBUG | ble_interface.py: Sending b'a'
13:45:56.294 | DEBUG | tcp_socket.py: Received b's'
13:45:56.326 | DEBUG | ble_interface.py: Sending b's'
13:45:56.344 | DEBUG | tcp_socket.py: Received b's'
13:45:56.447 | DEBUG | ble_interface.py: Sending b's'
13:45:56.527 | DEBUG | tcp_socket.py: Received b'w'
13:45:56.567 | DEBUG | ble_interface.py: Sending b'w'
13:45:56.692 | DEBUG | tcp_socket.py: Received b'o'
13:45:56.693 | DEBUG | ble_interface.py: Sending b'o'
13:45:56.749 | DEBUG | tcp_socket.py: Received b'r'
13:45:56.807 | DEBUG | ble_interface.py: Sending b'r'
13:45:56.901 | DEBUG | tcp_socket.py: Received b'd'
13:45:56.926 | DEBUG | ble_interface.py: Sending b'd'
13:45:57.521 | DEBUG | tcp_socket.py: Received b'\r\x00'
13:45:57.522 | DEBUG | ble_interface.py: Sending b'\r\x00'
13:45:57.646 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'\r\n')
13:45:57.647 | DEBUG | tcp_socket.py: Sending: bytearray(b'\r\n')
13:45:57.767 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'Last login: Sun Jun  9 13:41:59 on ttyS0\r\n')
13:45:57.767 | DEBUG | tcp_socket.py: Sending: bytearray(b'Last login: Sun Jun  9 13:41:59 on ttyS0\r\n')
13:45:58.125 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'^@')
13:45:58.125 | DEBUG | tcp_socket.py: Sending: bytearray(b'^@')
13:45:58.186 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'sw1#')
13:45:58.186 | DEBUG | tcp_socket.py: Sending: bytearray(b'sw1#')
13:45:58.610 | DEBUG | tcp_socket.py: Received b'\r\x00'
13:45:58.611 | DEBUG | ble_interface.py: Sending b'\r\x00'
13:45:58.726 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'^M')
13:45:58.727 | DEBUG | tcp_socket.py: Sending: bytearray(b'^M')
13:45:59.121 | DEBUG | tcp_socket.py: Received b'\r\x00'
13:45:59.122 | DEBUG | ble_interface.py: Sending b'\r\x00'
13:45:59.206 | DEBUG | ble_interface.py: Received notify from 0000ffe1-0000-1000-8000-00805f9b34fb (Handle: 24): TX & RX: bytearray(b'^M')

Note I've attempted to obfuscate hostnames, usernames and passwords, so please excuse if I've cut out a line anywhere I shouldn't. I've run a serial line from my switch at home to my office now, so I can plug into it without getting up from my desk, if there's anything needing to be clarified or further tested.