Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrades #1

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 55 additions & 11 deletions ubit_kernel/kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
import re
import sys
import time
import uuid

from ipykernel.kernelbase import Kernel
from .ubit import connect
from .ubit import connect, disconnect

__version__ = '0.3'
__version__ = '0.4'

class MicrobitKernel(Kernel):
implementation = 'ubit_kernel'
Expand All @@ -28,27 +29,62 @@ class MicrobitKernel(Kernel):
]

banner = "Welcome to MicroPython on the BBC micro:bit"
INIT_CODE = '''try:
g['UUID'] = {}
except NameError:
g = {'UUID': {}}
try:
l['UUID'] = {}
except NameError:
l = {'UUID': {}}
'''

def __init__(self, **kwargs):
super().__init__(**kwargs)
self.uuid = uuid.uuid4().hex
self.serial = connect()

out, err = self.run_code(self.INIT_CODE.replace('UUID', self.uuid))
self.send_response(self.iopub_socket, 'stream', {
'name': 'stdout',
'text': 'Kernel connected to microbit, uuid: %s' % self.uuid,
})

def run_code(self, code):
'''Run a string of code, return strings for stdout and stderr'''
self.serial.write(code.encode('utf-8') + b'\x04')
result = bytearray()
while not result.endswith(b'\x04>'):
try:
self.serial.write(b'\x03' + code.encode('utf-8') + b'\x04')
result = bytearray()
while not result.endswith(b'\x04>'):
time.sleep(0.1)
result.extend(self.serial.read_all())

return self._parse_result(result)

except KeyboardInterrupt:
self.serial.write(b'\x03')
time.sleep(0.1)
result.extend(self.serial.read_all())
#print('Read', repr(result), file=sys.__stderr__)
self.serial.reset_output_buffer()
self.serial.reset_input_buffer()
return self._parse_result(result)

assert result.startswith(b'OK')
out, err = result[2:-2].split(b'\x04', 1)
return out.decode('utf-8', 'replace'), err.decode('utf-8', 'replace')
def _parse_result(self, result):
if result.startswith(b'OK'):
results = result[2:-2].split(b'\x04', 1)
if len(results) == 2:
out, err = results
else:
out, err = result, b''
return out.decode('utf-8', 'replace'), err.decode('utf-8', 'replace')
else:
return '', 'something is not ok'

def do_execute(self, code, silent, store_history=True,
user_expressions=None, allow_stdin=False):
out, err = self.run_code(code)
real_code = 'exec({code!r}, g[{uuid!r}], l[{uuid!r}])'
formatted_code = real_code.format(code=code, uuid=self.uuid)
print(formatted_code)
out, err = self.run_code(formatted_code)
if out:
self.send_response(self.iopub_socket, 'stream', {
'name': 'stdout',
Expand Down Expand Up @@ -88,3 +124,11 @@ def do_complete(self, code, cursor_pos):
return {'matches': [],
'cursor_start': cursor_pos, 'cursor_end': cursor_pos,
'metadata': {}, 'status': 'ok'}

def do_shutdown(self, restart):
code = 'del g[{uuid!r}]; del l[{uuid!r}]'
out, err = self.run_code(code.format(uuid=self.uuid))
disconnect()
return {
'restart': restart,
}
34 changes: 29 additions & 5 deletions ubit_kernel/ubit.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from serial import Serial
from serial.tools.list_ports import comports

from threading import RLock

MICROBIT_PID = 516
MICROBIT_VID = 3368
BAUDRATE = 115200
Expand All @@ -14,12 +16,34 @@ def find_microbit():
if port.vid == MICROBIT_VID and port.pid == MICROBIT_PID:
return port.device

_connection = None
_connection_counts = 0
lock = RLock()


def connect():
"""
Returns a pySerial Serial object to talk to the microbit
"""
s = Serial(find_microbit(), BAUDRATE, parity=PARITY)
s.write(b'\x03\x01') # Ctrl-C: interrupt, Ctrl-A: switch to raw REPL
s.read_until(b'raw REPL')
s.read_until(b'\r\n>') # Wait for prompt
return s
global _connection, _connection_counts
with lock:
if _connection is not None:
_connection_counts += 1
return _connection
s = Serial(find_microbit(), BAUDRATE, parity=PARITY)
s.write(b'\x03\x01') # Ctrl-C: interrupt, Ctrl-A: switch to raw REPL
s.read_until(b'raw REPL')
s.read_until(b'\r\n>') # Wait for prompt

_connection_counts += 1
_connection = s
return s


def disconnect():
global _connection, _connection_counts
with lock:
_connection_counts -= 1
if _connection_counts <= 0:
_connection.close()
_connection = None