From cdbfc124784eecc057af3689045bda976992170e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20B=C3=B6hm?= Date: Mon, 26 Aug 2019 15:35:15 +0200 Subject: [PATCH 1/9] Add RAW repl mode support --- pycardium/main.c | 13 +- pycardium/tools/pyboard.py | 461 +++++++++++++++++++++++++++++++++++++ 2 files changed, 473 insertions(+), 1 deletion(-) create mode 100755 pycardium/tools/pyboard.py diff --git a/pycardium/main.c b/pycardium/main.c index 56a7575a..e3353441 100644 --- a/pycardium/main.c +++ b/pycardium/main.c @@ -60,7 +60,18 @@ int main(void) } epic_uart_write_str(header, sizeof(header)); - pyexec_friendly_repl(); + + for (;;) { + if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { + if (pyexec_raw_repl() != 0) { + break; + } + } else { + if (pyexec_friendly_repl() != 0) { + break; + } + } + } mp_deinit(); } diff --git a/pycardium/tools/pyboard.py b/pycardium/tools/pyboard.py new file mode 100755 index 00000000..6b0b9951 --- /dev/null +++ b/pycardium/tools/pyboard.py @@ -0,0 +1,461 @@ +#!/usr/bin/env python +# +# This file is part of the MicroPython project, http://micropython.org/ +# +# The MIT License (MIT) +# +# Copyright (c) 2014-2016 Damien P. George +# Copyright (c) 2017 Paul Sokolovsky +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +""" +pyboard interface + +This module provides the Pyboard class, used to communicate with and +control a MicroPython device over a communication channel. Both real +boards and emulated devices (e.g. running in QEMU) are supported. +Various communication channels are supported, including a serial +connection, telnet-style network connection, external process +connection. + +Example usage: + + import pyboard + pyb = pyboard.Pyboard('/dev/ttyACM0') + +Or: + + pyb = pyboard.Pyboard('192.168.1.1') + +Then: + + pyb.enter_raw_repl() + pyb.exec('import pyb') + pyb.exec('pyb.LED(1).on()') + pyb.exit_raw_repl() + +Note: if using Python2 then pyb.exec must be written as pyb.exec_. +To run a script from the local machine on the board and print out the results: + + import pyboard + pyboard.execfile('test.py', device='/dev/ttyACM0') + +This script can also be run directly. To execute a local script, use: + + ./pyboard.py test.py + +Or: + + python pyboard.py test.py + +""" + +import sys +import time +import os + +try: + stdout = sys.stdout.buffer +except AttributeError: + # Python2 doesn't have buffer attr + stdout = sys.stdout + +def stdout_write_bytes(b): + b = b.replace(b"\x04", b"") + stdout.write(b) + stdout.flush() + +class PyboardError(Exception): + pass + +class TelnetToSerial: + def __init__(self, ip, user, password, read_timeout=None): + self.tn = None + import telnetlib + self.tn = telnetlib.Telnet(ip, timeout=15) + self.read_timeout = read_timeout + if b'Login as:' in self.tn.read_until(b'Login as:', timeout=read_timeout): + self.tn.write(bytes(user, 'ascii') + b"\r\n") + + if b'Password:' in self.tn.read_until(b'Password:', timeout=read_timeout): + # needed because of internal implementation details of the telnet server + time.sleep(0.2) + self.tn.write(bytes(password, 'ascii') + b"\r\n") + + if b'for more information.' in self.tn.read_until(b'Type "help()" for more information.', timeout=read_timeout): + # login successful + from collections import deque + self.fifo = deque() + return + + raise PyboardError('Failed to establish a telnet connection with the board') + + def __del__(self): + self.close() + + def close(self): + if self.tn: + self.tn.close() + + def read(self, size=1): + while len(self.fifo) < size: + timeout_count = 0 + data = self.tn.read_eager() + if len(data): + self.fifo.extend(data) + timeout_count = 0 + else: + time.sleep(0.25) + if self.read_timeout is not None and timeout_count > 4 * self.read_timeout: + break + timeout_count += 1 + + data = b'' + while len(data) < size and len(self.fifo) > 0: + data += bytes([self.fifo.popleft()]) + return data + + def write(self, data): + self.tn.write(data) + return len(data) + + def inWaiting(self): + n_waiting = len(self.fifo) + if not n_waiting: + data = self.tn.read_eager() + self.fifo.extend(data) + return len(data) + else: + return n_waiting + + +class ProcessToSerial: + "Execute a process and emulate serial connection using its stdin/stdout." + + def __init__(self, cmd): + import subprocess + self.subp = subprocess.Popen(cmd, bufsize=0, shell=True, preexec_fn=os.setsid, + stdin=subprocess.PIPE, stdout=subprocess.PIPE) + + # Initially was implemented with selectors, but that adds Python3 + # dependency. However, there can be race conditions communicating + # with a particular child process (like QEMU), and selectors may + # still work better in that case, so left inplace for now. + # + #import selectors + #self.sel = selectors.DefaultSelector() + #self.sel.register(self.subp.stdout, selectors.EVENT_READ) + + import select + self.poll = select.poll() + self.poll.register(self.subp.stdout.fileno()) + + def close(self): + import signal + os.killpg(os.getpgid(self.subp.pid), signal.SIGTERM) + + def read(self, size=1): + data = b"" + while len(data) < size: + data += self.subp.stdout.read(size - len(data)) + return data + + def write(self, data): + self.subp.stdin.write(data) + return len(data) + + def inWaiting(self): + #res = self.sel.select(0) + res = self.poll.poll(0) + if res: + return 1 + return 0 + + +class ProcessPtyToTerminal: + """Execute a process which creates a PTY and prints slave PTY as + first line of its output, and emulate serial connection using + this PTY.""" + + def __init__(self, cmd): + import subprocess + import re + import serial + self.subp = subprocess.Popen(cmd.split(), bufsize=0, shell=False, preexec_fn=os.setsid, + stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + pty_line = self.subp.stderr.readline().decode("utf-8") + m = re.search(r"/dev/pts/[0-9]+", pty_line) + if not m: + print("Error: unable to find PTY device in startup line:", pty_line) + self.close() + sys.exit(1) + pty = m.group() + # rtscts, dsrdtr params are to workaround pyserial bug: + # http://stackoverflow.com/questions/34831131/pyserial-does-not-play-well-with-virtual-port + self.ser = serial.Serial(pty, interCharTimeout=1, rtscts=True, dsrdtr=True) + + def close(self): + import signal + os.killpg(os.getpgid(self.subp.pid), signal.SIGTERM) + + def read(self, size=1): + return self.ser.read(size) + + def write(self, data): + return self.ser.write(data) + + def inWaiting(self): + return self.ser.inWaiting() + + +class Pyboard: + def __init__(self, device, baudrate=115200, user='micro', password='python', wait=0): + if device.startswith("exec:"): + self.serial = ProcessToSerial(device[len("exec:"):]) + elif device.startswith("execpty:"): + self.serial = ProcessPtyToTerminal(device[len("qemupty:"):]) + elif device and device[0].isdigit() and device[-1].isdigit() and device.count('.') == 3: + # device looks like an IP address + self.serial = TelnetToSerial(device, user, password, read_timeout=10) + else: + import serial + delayed = False + for attempt in range(wait + 1): + try: + self.serial = serial.Serial(device, baudrate=baudrate, interCharTimeout=1) + break + except (OSError, IOError): # Py2 and Py3 have different errors + if wait == 0: + continue + if attempt == 0: + sys.stdout.write('Waiting {} seconds for pyboard '.format(wait)) + delayed = True + time.sleep(1) + sys.stdout.write('.') + sys.stdout.flush() + else: + if delayed: + print('') + raise PyboardError('failed to access ' + device) + if delayed: + print('') + + def close(self): + self.serial.close() + + def read_until(self, min_num_bytes, ending, timeout=10, data_consumer=None): + # if data_consumer is used then data is not accumulated and the ending must be 1 byte long + assert data_consumer is None or len(ending) == 1 + + data = self.serial.read(min_num_bytes) + if data_consumer: + data_consumer(data) + timeout_count = 0 + while True: + if data.endswith(ending): + break + elif self.serial.inWaiting() > 0: + new_data = self.serial.read(1) + if data_consumer: + data_consumer(new_data) + data = new_data + else: + data = data + new_data + timeout_count = 0 + else: + timeout_count += 1 + if timeout is not None and timeout_count >= 100 * timeout: + break + time.sleep(0.01) + return data + + def enter_raw_repl(self): + self.serial.write(b'\r\x03\x03') # ctrl-C twice: interrupt any running program + + # flush input (without relying on serial.flushInput()) + n = self.serial.inWaiting() + while n > 0: + self.serial.read(n) + n = self.serial.inWaiting() + + self.serial.write(b'\r\x01') # ctrl-A: enter raw REPL + data = self.read_until(1, b'raw REPL; CTRL-B to exit\r\n>') + if not data.endswith(b'raw REPL; CTRL-B to exit\r\n>'): + print(data) + raise PyboardError('could not enter raw repl') + + self.serial.write(b'\x04') # ctrl-D: soft reset + data = self.read_until(1, b'OK') + if not data.endswith(b'OK'): + print(data) + raise PyboardError('couldn\'t leave raw repl') + + def exit_raw_repl(self): + self.serial.write(b'\r\x02') # ctrl-B: enter friendly REPL + + def follow(self, timeout, data_consumer=None): + # wait for normal output + data = self.read_until(1, b'\x04', timeout=timeout, data_consumer=data_consumer) + if not data.endswith(b'\x04'): + raise PyboardError('timeout waiting for first EOF reception') + data = data[:-1] + + # wait for error output + data_err = self.read_until(1, b'\x04', timeout=timeout) + if not data_err.endswith(b'\x04'): + raise PyboardError('timeout waiting for second EOF reception') + data_err = data_err[:-1] + + # return normal and error output + return data, data_err + + def exec_raw_no_follow(self, command): + if isinstance(command, bytes): + command_bytes = command + else: + command_bytes = bytes(command, encoding='utf8') + + # check we have a prompt + data = self.read_until(1, b'>') + if not data.endswith(b'>'): + raise PyboardError('could not enter raw repl') + + # write command + for i in range(0, len(command_bytes), 256): + self.serial.write(command_bytes[i:min(i + 256, len(command_bytes))]) + time.sleep(0.01) + self.serial.write(b'\x04') + + # check if we could exec command + data = self.serial.read(2) + if data != b'OK': + raise PyboardError('could not exec command (response: %r)' % data) + + def exec_raw(self, command, timeout=10, data_consumer=None): + self.exec_raw_no_follow(command); + return self.follow(timeout, data_consumer) + + def eval(self, expression): + ret = self.exec_('print({})'.format(expression)) + ret = ret.strip() + return ret + + def exec_(self, command): + ret, ret_err = self.exec_raw(command) + if ret_err: + raise PyboardError('exception', ret, ret_err) + return ret + + def execfile(self, filename): + with open(filename, 'rb') as f: + pyfile = f.read() + return self.exec_(pyfile) + + def get_time(self): + t = str(self.eval('pyb.RTC().datetime()'), encoding='utf8')[1:-1].split(', ') + return int(t[4]) * 3600 + int(t[5]) * 60 + int(t[6]) + +# in Python2 exec is a keyword so one must use "exec_" +# but for Python3 we want to provide the nicer version "exec" +setattr(Pyboard, "exec", Pyboard.exec_) + +def execfile(filename, device='/dev/ttyACM0', baudrate=115200, user='micro', password='python'): + pyb = Pyboard(device, baudrate, user, password) + pyb.enter_raw_repl() + output = pyb.execfile(filename) + stdout_write_bytes(output) + pyb.exit_raw_repl() + pyb.close() + +def main(): + import argparse + cmd_parser = argparse.ArgumentParser(description='Run scripts on the pyboard.') + cmd_parser.add_argument('--device', default='/dev/ttyACM0', help='the serial device or the IP address of the pyboard') + cmd_parser.add_argument('-c', '--command', help='program passed in as string') + cmd_parser.add_argument('-w', '--wait', default=0, type=int, help='seconds to wait for USB connected board to become available') + cmd_parser.add_argument('--follow', action='store_true', help='follow the output after running the scripts [default if no scripts given]') + cmd_parser.add_argument('files', nargs='*', help='input files') + args = cmd_parser.parse_args() + + # open the connection to the pyboard + try: + pyb = Pyboard(args.device, 115200, None, None, args.wait) + except PyboardError as er: + print(er) + sys.exit(1) + + # run any command or file(s) + if args.command is not None or len(args.files): + # we must enter raw-REPL mode to execute commands + # this will do a soft-reset of the board + try: + pyb.enter_raw_repl() + except PyboardError as er: + print(er) + pyb.close() + sys.exit(1) + + def execbuffer(buf): + try: + ret, ret_err = pyb.exec_raw(buf, timeout=None, data_consumer=stdout_write_bytes) + except PyboardError as er: + print(er) + pyb.close() + sys.exit(1) + except KeyboardInterrupt: + sys.exit(1) + if ret_err: + pyb.exit_raw_repl() + pyb.close() + stdout_write_bytes(ret_err) + sys.exit(1) + + # run the command, if given + if args.command is not None: + execbuffer(args.command.encode('utf-8')) + + # run any files + for filename in args.files: + with open(filename, 'rb') as f: + pyfile = f.read() + execbuffer(pyfile) + + # exiting raw-REPL just drops to friendly-REPL mode + pyb.exit_raw_repl() + + # if asked explicitly, or no files given, then follow the output + if args.follow or (args.command is None and len(args.files) == 0): + try: + ret, ret_err = pyb.follow(timeout=None, data_consumer=stdout_write_bytes) + except PyboardError as er: + print(er) + sys.exit(1) + except KeyboardInterrupt: + sys.exit(1) + if ret_err: + pyb.close() + stdout_write_bytes(ret_err) + sys.exit(1) + + # close the connection to the pyboard + pyb.close() + +if __name__ == "__main__": + main() -- GitLab From b6005316db385b7739f08cbca9c52eaae809d0c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20B=C3=B6hm?= Date: Tue, 27 Aug 2019 10:50:09 +0200 Subject: [PATCH 2/9] Added call for disable_all_sensors in sys_bhi160 for upy --- pycardium/modules/bhi160-sys.c | 13 +++++++++++++ pycardium/modules/qstrdefs.h | 1 + 2 files changed, 14 insertions(+) diff --git a/pycardium/modules/bhi160-sys.c b/pycardium/modules/bhi160-sys.c index db0a5c9a..7320885c 100644 --- a/pycardium/modules/bhi160-sys.c +++ b/pycardium/modules/bhi160-sys.c @@ -66,6 +66,17 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1( mp_bhi160_disable_sensor_obj, mp_bhi160_disable_sensor ); +STATIC mp_obj_t mp_bhi160_disable_all_sensors() +{ + epic_bhi160_disable_all_sensors(); + + return mp_const_none; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_0( + mp_bhi160_disable_all_sensors_obj, mp_bhi160_disable_all_sensors +); + STATIC const mp_rom_map_elem_t bhi160_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sys_bhi160) }, { MP_ROM_QSTR(MP_QSTR_enable_sensor), @@ -74,6 +85,8 @@ STATIC const mp_rom_map_elem_t bhi160_module_globals_table[] = { MP_ROM_PTR(&mp_bhi160_read_sensor_obj) }, { MP_ROM_QSTR(MP_QSTR_disable_sensor), MP_ROM_PTR(&mp_bhi160_disable_sensor_obj) }, + { MP_ROM_QSTR(MP_QSTR_disable_all_sensors), + MP_ROM_PTR(&mp_bhi160_disable_all_sensors_obj) }, }; STATIC MP_DEFINE_CONST_DICT(bhi160_module_globals, bhi160_module_globals_table); diff --git a/pycardium/modules/qstrdefs.h b/pycardium/modules/qstrdefs.h index a02bcbd8..5a693d74 100644 --- a/pycardium/modules/qstrdefs.h +++ b/pycardium/modules/qstrdefs.h @@ -67,6 +67,7 @@ Q(RTC_ALARM) Q(sys_bhi160) Q(enable_sensor) Q(disable_sensor) +Q(disable_all_sensors) Q(read_sensor) Q(x) Q(y) -- GitLab From 2b93855a2475d237276798fc277c7fb78ba9b3ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20B=C3=B6hm?= Date: Tue, 27 Aug 2019 13:39:39 +0200 Subject: [PATCH 3/9] Refactored out executing tool in pycard10.py --- pycardium/tools/pyboard.py | 17 ++- pycardium/tools/pycard10.py | 273 ++++++++++++++++++++++++++++++++++++ 2 files changed, 286 insertions(+), 4 deletions(-) create mode 100755 pycardium/tools/pycard10.py diff --git a/pycardium/tools/pyboard.py b/pycardium/tools/pyboard.py index 6b0b9951..d9062350 100755 --- a/pycardium/tools/pyboard.py +++ b/pycardium/tools/pyboard.py @@ -302,10 +302,16 @@ class Pyboard: raise PyboardError('could not enter raw repl') self.serial.write(b'\x04') # ctrl-D: soft reset - data = self.read_until(1, b'OK') - if not data.endswith(b'OK'): + data = self.read_until(1, b'soft reboot\r\n') + if not data.endswith(b'soft reboot\r\n'): print(data) - raise PyboardError('couldn\'t leave raw repl') + raise PyboardError('could not enter raw repl') + # By splitting this into 2 reads, it allows boot.py to print stuff, + # which will show up after the soft reboot and before the raw REPL. + data = self.read_until(1, b'raw REPL; CTRL-B to exit\r\n') + if not data.endswith(b'raw REPL; CTRL-B to exit\r\n'): + print(data) + raise PyboardError('could not enter raw repl') def exit_raw_repl(self): self.serial.write(b'\r\x02') # ctrl-B: enter friendly REPL @@ -388,6 +394,9 @@ def main(): import argparse cmd_parser = argparse.ArgumentParser(description='Run scripts on the pyboard.') cmd_parser.add_argument('--device', default='/dev/ttyACM0', help='the serial device or the IP address of the pyboard') + cmd_parser.add_argument('-b', '--baudrate', default=115200, help='the baud rate of the serial device') + cmd_parser.add_argument('-u', '--user', default='micro', help='the telnet login username') + cmd_parser.add_argument('-p', '--password', default='python', help='the telnet login password') cmd_parser.add_argument('-c', '--command', help='program passed in as string') cmd_parser.add_argument('-w', '--wait', default=0, type=int, help='seconds to wait for USB connected board to become available') cmd_parser.add_argument('--follow', action='store_true', help='follow the output after running the scripts [default if no scripts given]') @@ -396,7 +405,7 @@ def main(): # open the connection to the pyboard try: - pyb = Pyboard(args.device, 115200, None, None, args.wait) + pyb = Pyboard(args.device, args.baudrate, args.user, args.password, args.wait) except PyboardError as er: print(er) sys.exit(1) diff --git a/pycardium/tools/pycard10.py b/pycardium/tools/pycard10.py new file mode 100755 index 00000000..64477245 --- /dev/null +++ b/pycardium/tools/pycard10.py @@ -0,0 +1,273 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# This file is part of the card10 project card10.badge.events.ccc.de +# +# The MIT License (MIT) +# +# Copyright (c) 2019 Alexander Böhm +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +""" +card10 interface + +This module provides is an extenstion of the Pyboard class, used to +communicate with the card10 badge over the serial connection via USB. + +For description of the card10 board look at the project website . + +Python2 isn't supported. + +Example usage: + + import pycard10 + card10 = pycard10.PyCard10('/dev/ttyACM0') + +Then: + + card10.enter_raw_repl() + card10.exec('import leds') + card10.exec('leds.set_rocket(0, 31)') + card10.exit_raw_repl() + + import pycard10 + pycard10.execfile('test.py', device='/dev/ttyACM0') + +This script can also be run directly. To execute a local script, use: + + pycardium/tools/pycard10.py test.py + +Or: + python3 pycardium/tools/pycard10.py test.py + +""" + +__author__ = "Alexander Böhm" +__copyright__ = "Copyright 2019, Alexander Böhm" +__license__ = "MIT" +__email__ = "alexander.boehm@malbolge.net" + +import sys +import time +import os +from pyboard import stdout_write_bytes, PyboardError, Pyboard, ProcessPtyToTerminal, ProcessToSerial, TelnetToSerial + +class PyCard10(Pyboard): + ''' + Python card10 connector. + ''' + + def __init__(self, device, wait=0): + ''' + Open a connection to the card10 over the serial device *device*. + ''' + Pyboard.__init__(self, device=device, baudrate=115200, user=None, password=None, wait=wait) + + def exec_raw_no_follow(self, command): + ''' + Execute a the command 'command' on the card10. + + Parameters: + command (bytes): Command or multiple commands + + Returns: + None + ''' + + if isinstance(command, bytes): + command_bytes = command + else: + command_bytes = bytes(command, encoding='utf8') + + data = self.read_until(1, b'>') + if not data.endswith(b'>'): + raise PyboardError('card10 not in raw repl mode: (response: %r)' % (data)) + + # write command + for i in range(0, len(command_bytes), 256): + self.serial.write(command_bytes[i:min(i + 256, len(command_bytes))]) + time.sleep(0.01) + + self.serial.write(b'\x04') + + # check if we could exec command + data = self.serial.read(2) + if data != b'OK': + raise PyboardError('could not exec command (response: %r)' % data) + + def enter_raw_repl(self): + ''' + Enter the RAW repl mode. After the prompt character ('>') left in the buffer of the serial line. + + Returns: + None + ''' + self.serial.write(b'\x03\x03') # ctrl-C twice: interrupt any running program + + # flush input (without relying on serial.flushInput()) + n = self.serial.inWaiting() + while n > 0: + self.serial.read(n) + n = self.serial.inWaiting() + + self.serial.write(b'\x01') # ctrl-A: enter raw REPL + data = self.read_until(1, b'raw REPL; CTRL-B to exit\r\n>') + if not data.endswith(b'raw REPL; CTRL-B to exit\r\n>'): + raise PyboardError('could not enter raw repl') + + self.serial.write(b'\r\x04') # execute nothing + data = self.serial.read(2) + if data != b'OK': + raise PyboardError('could not enter raw repl') + + def exec(self, command): + ''' + Execute a command on the card10 and return the output of the card10. + + Parameters: + command (bytes): Command or multiple commands + + Returns: + data(bytes), data_err(bytes): data is standard out, data_err is standard error + ''' + return self.exec_(command) + + def soft_reset(self): + ''' + Doing a soft reset on the board and going to menu. + + Returns: + None + ''' + + self.serial.write(b'\x03\x03') # ctrl-C twice: interrupt any running program + + self.serial.write(b'\x01') # ctrl-B: ensue it's the normal mode + + self.serial.write(b'\x04') # ctrl-D: do the reset + + n = self.serial.inWaiting() + while n > 0: + self.serial.read(n) + n = self.serial.inWaiting() + +def execfile(filename, device='/dev/ttyACM0'): + ''' + Execute python source from *filename* via the RAW repl mode on the card10 connected via serial line *device*. + + Parameters: + filename(str): Path text file with commands + device(str): Path to the card10 device. + + Returns: + None + ''' + c = PyCard10(device) + c.enter_raw_repl() + output = c.execfile(filename) + stdout_write_bytes(output) + c.exit_raw_repl() + c.close() + +def main(): + ''' + The main method. + + Returns: + None + ''' + + import argparse + cmd_parser = argparse.ArgumentParser(description='Run scripts on the card10.') + cmd_parser.add_argument('--device', default='/dev/ttyACM0', help='the serial device of the card10') + cmd_parser.add_argument('-c', '--command', help='program passed in as string') + cmd_parser.add_argument('-w', '--wait', default=0, type=int, help='seconds to wait for USB connected board to become available') + cmd_parser.add_argument('--follow', action='store_true', help='follow the output after running the scripts [default if no scripts given]') + cmd_parser.add_argument('--reset', action='store_true', help='Soft reseting the card10') + cmd_parser.add_argument('files', nargs='*', help='input files') + args = cmd_parser.parse_args() + + # open the connection to the card10 + try: + card10 = PyCard10(args.device, args.wait) + except PyboardError as er: + print(er) + sys.exit(1) + + if args.reset: + card10.soft_reset() + + elif args.command is not None or len(args.files): + # we must enter raw-REPL mode to execute commands + # this will do a soft-reset of the board + try: + card10.enter_raw_repl() + except PyboardError as er: + print(er) + card10.close() + sys.exit(1) + + def execbuffer(buf): + try: + ret, ret_err = card10.exec_raw(buf, timeout=None, data_consumer=stdout_write_bytes) + except PyboardError as er: + print(er) + card10.close() + sys.exit(1) + except KeyboardInterrupt: + sys.exit(1) + if ret_err: + card10.exit_raw_repl() + card10.close() + stdout_write_bytes(ret_err) + sys.exit(1) + + # run the command, if given + if args.command is not None: + execbuffer(args.command.encode('utf-8')) + + # run any files + for filename in args.files: + with open(filename, 'rb') as f: + pyfile = f.read() + execbuffer(pyfile) + + # exiting raw-REPL just drops to friendly-REPL mode + card10.exit_raw_repl() + + # if asked explicitly, or no files given, then follow the output + elif args.follow or (args.command is None and len(args.files) == 0): + try: + ret, ret_err = card10.follow(timeout=None, data_consumer=stdout_write_bytes) + except PyboardError as er: + print(er) + sys.exit(1) + except KeyboardInterrupt: + sys.exit(1) + if ret_err: + card10.close() + stdout_write_bytes(ret_err) + sys.exit(1) + + # close the connection to the card10 + card10.close() + +if __name__ == "__main__": + main() -- GitLab From fff7db566477a2f45b0e6ed8e7698ca9c2b3eaed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20B=C3=B6hm?= Date: Tue, 27 Aug 2019 14:12:35 +0200 Subject: [PATCH 4/9] Document REPL modes in the overview of pycardium --- Documentation/pycardium/overview.rst | 44 ++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/Documentation/pycardium/overview.rst b/Documentation/pycardium/overview.rst index 0d3056c8..085d19b4 100644 --- a/Documentation/pycardium/overview.rst +++ b/Documentation/pycardium/overview.rst @@ -36,8 +36,46 @@ Baud-rate is 115200. Some options are: * **screen**: ``sudo screen /dev/ttyACM0 115200`` * **picocom**: ``sudo picocom -b 115200 /dev/ttyACM0`` -After connecting, reboot card10 and you should see the MicroPython REPL pop up. +After connecting, reboot reset the card10 via the power button (left upper +corner) and you should see the output of **menu.py** script (it's located in +*preload/menu.py*). You can press CTRL-C to interrupt the script and jump into +the MicroPython prompt. + +To switch on the blue fairy dust you must import the led python module:: + + import leds + +and power it on:: + + leds.set_rocket(0, 31) + + +REPL modes +^^^^^^^^^^ + +MicroPython supports a different REPL modes over the serial console. The modes +can be changed on every new line. + +Normal mode +""""""""""" +This is the mode you will first see. You can switch to it by pressing CTRL-B. +If you are in a other mode you can return to this mode by pressing CTRL-B too. + +Paste mode +"""""""""" +You can enter the paste mode by pressing CTRL-E and you can simple copy 'n' +paste your source code into the console and it will be interpreted and executed +line by line. Every new line will be reply by the prompt with **===**. + +RAW mode +"""""""" +The RAW mode to be intendend for the usage with tools. By pressing CTRL-A you +will enter the RAW REPL mode. The type in code will not printed. By pressing +CTRL-D the whole entered code will be evaluated and executed. The board will +reply with **OK** and print after that the output (print commands) of the code +or give you tracebacks if an error occured. + +You can use **pycard10** (pycardium/tools/pycard10.py) to execute python files +from your PC directly on the card10. -.. todo:: - Getting Started Guide for people interested in writing Python code. -- GitLab From 34e7470c1bce6c836bd450f2114663b3984eaab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20B=C3=B6hm?= Date: Tue, 27 Aug 2019 14:40:43 +0200 Subject: [PATCH 5/9] Fix some linter errors --- pycardium/main.c | 22 ++-- pycardium/tools/pyboard.py | 211 ++++++++++++++++++++++++------------ pycardium/tools/pycard10.py | 132 +++++++++++++--------- 3 files changed, 231 insertions(+), 134 deletions(-) diff --git a/pycardium/main.c b/pycardium/main.c index e3353441..b87b4a1e 100644 --- a/pycardium/main.c +++ b/pycardium/main.c @@ -61,17 +61,17 @@ int main(void) epic_uart_write_str(header, sizeof(header)); - for (;;) { - if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { - if (pyexec_raw_repl() != 0) { - break; - } - } else { - if (pyexec_friendly_repl() != 0) { - break; - } - } - } + for (;;) { + if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { + if (pyexec_raw_repl() != 0) { + break; + } + } else { + if (pyexec_friendly_repl() != 0) { + break; + } + } + } mp_deinit(); } diff --git a/pycardium/tools/pyboard.py b/pycardium/tools/pyboard.py index d9062350..148e07d9 100755 --- a/pycardium/tools/pyboard.py +++ b/pycardium/tools/pyboard.py @@ -77,35 +77,42 @@ except AttributeError: # Python2 doesn't have buffer attr stdout = sys.stdout + def stdout_write_bytes(b): b = b.replace(b"\x04", b"") stdout.write(b) stdout.flush() + class PyboardError(Exception): pass + class TelnetToSerial: def __init__(self, ip, user, password, read_timeout=None): self.tn = None import telnetlib + self.tn = telnetlib.Telnet(ip, timeout=15) self.read_timeout = read_timeout - if b'Login as:' in self.tn.read_until(b'Login as:', timeout=read_timeout): - self.tn.write(bytes(user, 'ascii') + b"\r\n") + if b"Login as:" in self.tn.read_until(b"Login as:", timeout=read_timeout): + self.tn.write(bytes(user, "ascii") + b"\r\n") - if b'Password:' in self.tn.read_until(b'Password:', timeout=read_timeout): + if b"Password:" in self.tn.read_until(b"Password:", timeout=read_timeout): # needed because of internal implementation details of the telnet server time.sleep(0.2) - self.tn.write(bytes(password, 'ascii') + b"\r\n") + self.tn.write(bytes(password, "ascii") + b"\r\n") - if b'for more information.' in self.tn.read_until(b'Type "help()" for more information.', timeout=read_timeout): + if b"for more information." in self.tn.read_until( + b'Type "help()" for more information.', timeout=read_timeout + ): # login successful from collections import deque + self.fifo = deque() return - raise PyboardError('Failed to establish a telnet connection with the board') + raise PyboardError("Failed to establish a telnet connection with the board") def __del__(self): self.close() @@ -123,11 +130,14 @@ class TelnetToSerial: timeout_count = 0 else: time.sleep(0.25) - if self.read_timeout is not None and timeout_count > 4 * self.read_timeout: + if ( + self.read_timeout is not None + and timeout_count > 4 * self.read_timeout + ): break timeout_count += 1 - data = b'' + data = b"" while len(data) < size and len(self.fifo) > 0: data += bytes([self.fifo.popleft()]) return data @@ -151,24 +161,33 @@ class ProcessToSerial: def __init__(self, cmd): import subprocess - self.subp = subprocess.Popen(cmd, bufsize=0, shell=True, preexec_fn=os.setsid, - stdin=subprocess.PIPE, stdout=subprocess.PIPE) + + self.subp = subprocess.Popen( + cmd, + bufsize=0, + shell=True, + preexec_fn=os.setsid, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + ) # Initially was implemented with selectors, but that adds Python3 # dependency. However, there can be race conditions communicating # with a particular child process (like QEMU), and selectors may # still work better in that case, so left inplace for now. # - #import selectors - #self.sel = selectors.DefaultSelector() - #self.sel.register(self.subp.stdout, selectors.EVENT_READ) + # import selectors + # self.sel = selectors.DefaultSelector() + # self.sel.register(self.subp.stdout, selectors.EVENT_READ) import select + self.poll = select.poll() self.poll.register(self.subp.stdout.fileno()) def close(self): import signal + os.killpg(os.getpgid(self.subp.pid), signal.SIGTERM) def read(self, size=1): @@ -182,7 +201,7 @@ class ProcessToSerial: return len(data) def inWaiting(self): - #res = self.sel.select(0) + # res = self.sel.select(0) res = self.poll.poll(0) if res: return 1 @@ -198,8 +217,16 @@ class ProcessPtyToTerminal: import subprocess import re import serial - self.subp = subprocess.Popen(cmd.split(), bufsize=0, shell=False, preexec_fn=os.setsid, - stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + self.subp = subprocess.Popen( + cmd.split(), + bufsize=0, + shell=False, + preexec_fn=os.setsid, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) pty_line = self.subp.stderr.readline().decode("utf-8") m = re.search(r"/dev/pts/[0-9]+", pty_line) if not m: @@ -213,6 +240,7 @@ class ProcessPtyToTerminal: def close(self): import signal + os.killpg(os.getpgid(self.subp.pid), signal.SIGTERM) def read(self, size=1): @@ -226,36 +254,46 @@ class ProcessPtyToTerminal: class Pyboard: - def __init__(self, device, baudrate=115200, user='micro', password='python', wait=0): + def __init__( + self, device, baudrate=115200, user="micro", password="python", wait=0 + ): if device.startswith("exec:"): - self.serial = ProcessToSerial(device[len("exec:"):]) + self.serial = ProcessToSerial(device[len("exec:") :]) elif device.startswith("execpty:"): - self.serial = ProcessPtyToTerminal(device[len("qemupty:"):]) - elif device and device[0].isdigit() and device[-1].isdigit() and device.count('.') == 3: + self.serial = ProcessPtyToTerminal(device[len("qemupty:") :]) + elif ( + device + and device[0].isdigit() + and device[-1].isdigit() + and device.count(".") == 3 + ): # device looks like an IP address self.serial = TelnetToSerial(device, user, password, read_timeout=10) else: import serial + delayed = False for attempt in range(wait + 1): try: - self.serial = serial.Serial(device, baudrate=baudrate, interCharTimeout=1) + self.serial = serial.Serial( + device, baudrate=baudrate, interCharTimeout=1 + ) break - except (OSError, IOError): # Py2 and Py3 have different errors + except (OSError, IOError): # Py2 and Py3 have different errors if wait == 0: continue if attempt == 0: - sys.stdout.write('Waiting {} seconds for pyboard '.format(wait)) + sys.stdout.write("Waiting {} seconds for pyboard ".format(wait)) delayed = True time.sleep(1) - sys.stdout.write('.') + sys.stdout.write(".") sys.stdout.flush() else: if delayed: - print('') - raise PyboardError('failed to access ' + device) + print("") + raise PyboardError("failed to access " + device) if delayed: - print('') + print("") def close(self): self.serial.close() @@ -287,7 +325,7 @@ class Pyboard: return data def enter_raw_repl(self): - self.serial.write(b'\r\x03\x03') # ctrl-C twice: interrupt any running program + self.serial.write(b"\r\x03\x03") # ctrl-C twice: interrupt any running program # flush input (without relying on serial.flushInput()) n = self.serial.inWaiting() @@ -295,38 +333,38 @@ class Pyboard: self.serial.read(n) n = self.serial.inWaiting() - self.serial.write(b'\r\x01') # ctrl-A: enter raw REPL - data = self.read_until(1, b'raw REPL; CTRL-B to exit\r\n>') - if not data.endswith(b'raw REPL; CTRL-B to exit\r\n>'): + self.serial.write(b"\r\x01") # ctrl-A: enter raw REPL + data = self.read_until(1, b"raw REPL; CTRL-B to exit\r\n>") + if not data.endswith(b"raw REPL; CTRL-B to exit\r\n>"): print(data) - raise PyboardError('could not enter raw repl') + raise PyboardError("could not enter raw repl") - self.serial.write(b'\x04') # ctrl-D: soft reset - data = self.read_until(1, b'soft reboot\r\n') - if not data.endswith(b'soft reboot\r\n'): + self.serial.write(b"\x04") # ctrl-D: soft reset + data = self.read_until(1, b"soft reboot\r\n") + if not data.endswith(b"soft reboot\r\n"): print(data) - raise PyboardError('could not enter raw repl') + raise PyboardError("could not enter raw repl") # By splitting this into 2 reads, it allows boot.py to print stuff, # which will show up after the soft reboot and before the raw REPL. - data = self.read_until(1, b'raw REPL; CTRL-B to exit\r\n') - if not data.endswith(b'raw REPL; CTRL-B to exit\r\n'): + data = self.read_until(1, b"raw REPL; CTRL-B to exit\r\n") + if not data.endswith(b"raw REPL; CTRL-B to exit\r\n"): print(data) - raise PyboardError('could not enter raw repl') + raise PyboardError("could not enter raw repl") def exit_raw_repl(self): - self.serial.write(b'\r\x02') # ctrl-B: enter friendly REPL + self.serial.write(b"\r\x02") # ctrl-B: enter friendly REPL def follow(self, timeout, data_consumer=None): # wait for normal output - data = self.read_until(1, b'\x04', timeout=timeout, data_consumer=data_consumer) - if not data.endswith(b'\x04'): - raise PyboardError('timeout waiting for first EOF reception') + data = self.read_until(1, b"\x04", timeout=timeout, data_consumer=data_consumer) + if not data.endswith(b"\x04"): + raise PyboardError("timeout waiting for first EOF reception") data = data[:-1] # wait for error output - data_err = self.read_until(1, b'\x04', timeout=timeout) - if not data_err.endswith(b'\x04'): - raise PyboardError('timeout waiting for second EOF reception') + data_err = self.read_until(1, b"\x04", timeout=timeout) + if not data_err.endswith(b"\x04"): + raise PyboardError("timeout waiting for second EOF reception") data_err = data_err[:-1] # return normal and error output @@ -336,53 +374,57 @@ class Pyboard: if isinstance(command, bytes): command_bytes = command else: - command_bytes = bytes(command, encoding='utf8') + command_bytes = bytes(command, encoding="utf8") # check we have a prompt - data = self.read_until(1, b'>') - if not data.endswith(b'>'): - raise PyboardError('could not enter raw repl') + data = self.read_until(1, b">") + if not data.endswith(b">"): + raise PyboardError("could not enter raw repl") # write command for i in range(0, len(command_bytes), 256): - self.serial.write(command_bytes[i:min(i + 256, len(command_bytes))]) + self.serial.write(command_bytes[i : min(i + 256, len(command_bytes))]) time.sleep(0.01) - self.serial.write(b'\x04') + self.serial.write(b"\x04") # check if we could exec command data = self.serial.read(2) - if data != b'OK': - raise PyboardError('could not exec command (response: %r)' % data) + if data != b"OK": + raise PyboardError("could not exec command (response: %r)" % data) def exec_raw(self, command, timeout=10, data_consumer=None): - self.exec_raw_no_follow(command); + self.exec_raw_no_follow(command) return self.follow(timeout, data_consumer) def eval(self, expression): - ret = self.exec_('print({})'.format(expression)) + ret = self.exec_("print({})".format(expression)) ret = ret.strip() return ret def exec_(self, command): ret, ret_err = self.exec_raw(command) if ret_err: - raise PyboardError('exception', ret, ret_err) + raise PyboardError("exception", ret, ret_err) return ret def execfile(self, filename): - with open(filename, 'rb') as f: + with open(filename, "rb") as f: pyfile = f.read() return self.exec_(pyfile) def get_time(self): - t = str(self.eval('pyb.RTC().datetime()'), encoding='utf8')[1:-1].split(', ') + t = str(self.eval("pyb.RTC().datetime()"), encoding="utf8")[1:-1].split(", ") return int(t[4]) * 3600 + int(t[5]) * 60 + int(t[6]) + # in Python2 exec is a keyword so one must use "exec_" # but for Python3 we want to provide the nicer version "exec" setattr(Pyboard, "exec", Pyboard.exec_) -def execfile(filename, device='/dev/ttyACM0', baudrate=115200, user='micro', password='python'): + +def execfile( + filename, device="/dev/ttyACM0", baudrate=115200, user="micro", password="python" +): pyb = Pyboard(device, baudrate, user, password) pyb.enter_raw_repl() output = pyb.execfile(filename) @@ -390,17 +432,39 @@ def execfile(filename, device='/dev/ttyACM0', baudrate=115200, user='micro', pas pyb.exit_raw_repl() pyb.close() + def main(): import argparse - cmd_parser = argparse.ArgumentParser(description='Run scripts on the pyboard.') - cmd_parser.add_argument('--device', default='/dev/ttyACM0', help='the serial device or the IP address of the pyboard') - cmd_parser.add_argument('-b', '--baudrate', default=115200, help='the baud rate of the serial device') - cmd_parser.add_argument('-u', '--user', default='micro', help='the telnet login username') - cmd_parser.add_argument('-p', '--password', default='python', help='the telnet login password') - cmd_parser.add_argument('-c', '--command', help='program passed in as string') - cmd_parser.add_argument('-w', '--wait', default=0, type=int, help='seconds to wait for USB connected board to become available') - cmd_parser.add_argument('--follow', action='store_true', help='follow the output after running the scripts [default if no scripts given]') - cmd_parser.add_argument('files', nargs='*', help='input files') + + cmd_parser = argparse.ArgumentParser(description="Run scripts on the pyboard.") + cmd_parser.add_argument( + "--device", + default="/dev/ttyACM0", + help="the serial device or the IP address of the pyboard", + ) + cmd_parser.add_argument( + "-b", "--baudrate", default=115200, help="the baud rate of the serial device" + ) + cmd_parser.add_argument( + "-u", "--user", default="micro", help="the telnet login username" + ) + cmd_parser.add_argument( + "-p", "--password", default="python", help="the telnet login password" + ) + cmd_parser.add_argument("-c", "--command", help="program passed in as string") + cmd_parser.add_argument( + "-w", + "--wait", + default=0, + type=int, + help="seconds to wait for USB connected board to become available", + ) + cmd_parser.add_argument( + "--follow", + action="store_true", + help="follow the output after running the scripts [default if no scripts given]", + ) + cmd_parser.add_argument("files", nargs="*", help="input files") args = cmd_parser.parse_args() # open the connection to the pyboard @@ -423,7 +487,9 @@ def main(): def execbuffer(buf): try: - ret, ret_err = pyb.exec_raw(buf, timeout=None, data_consumer=stdout_write_bytes) + ret, ret_err = pyb.exec_raw( + buf, timeout=None, data_consumer=stdout_write_bytes + ) except PyboardError as er: print(er) pyb.close() @@ -438,11 +504,11 @@ def main(): # run the command, if given if args.command is not None: - execbuffer(args.command.encode('utf-8')) + execbuffer(args.command.encode("utf-8")) # run any files for filename in args.files: - with open(filename, 'rb') as f: + with open(filename, "rb") as f: pyfile = f.read() execbuffer(pyfile) @@ -466,5 +532,6 @@ def main(): # close the connection to the pyboard pyb.close() + if __name__ == "__main__": main() diff --git a/pycardium/tools/pycard10.py b/pycardium/tools/pycard10.py index 64477245..00e35a9e 100755 --- a/pycardium/tools/pycard10.py +++ b/pycardium/tools/pycard10.py @@ -59,29 +59,39 @@ Or: """ -__author__ = "Alexander Böhm" +__author__ = "Alexander Böhm" __copyright__ = "Copyright 2019, Alexander Böhm" -__license__ = "MIT" -__email__ = "alexander.boehm@malbolge.net" +__license__ = "MIT" +__email__ = "alexander.boehm@malbolge.net" import sys import time import os -from pyboard import stdout_write_bytes, PyboardError, Pyboard, ProcessPtyToTerminal, ProcessToSerial, TelnetToSerial +from pyboard import ( + stdout_write_bytes, + PyboardError, + Pyboard, + ProcessPtyToTerminal, + ProcessToSerial, + TelnetToSerial, +) + class PyCard10(Pyboard): - ''' + """ Python card10 connector. - ''' + """ def __init__(self, device, wait=0): - ''' + """ Open a connection to the card10 over the serial device *device*. - ''' - Pyboard.__init__(self, device=device, baudrate=115200, user=None, password=None, wait=wait) + """ + Pyboard.__init__( + self, device=device, baudrate=115200, user=None, password=None, wait=wait + ) def exec_raw_no_follow(self, command): - ''' + """ Execute a the command 'command' on the card10. Parameters: @@ -89,37 +99,37 @@ class PyCard10(Pyboard): Returns: None - ''' + """ if isinstance(command, bytes): command_bytes = command else: - command_bytes = bytes(command, encoding='utf8') + command_bytes = bytes(command, encoding="utf8") - data = self.read_until(1, b'>') - if not data.endswith(b'>'): - raise PyboardError('card10 not in raw repl mode: (response: %r)' % (data)) + data = self.read_until(1, b">") + if not data.endswith(b">"): + raise PyboardError("card10 not in raw repl mode: (response: %r)" % (data)) # write command for i in range(0, len(command_bytes), 256): - self.serial.write(command_bytes[i:min(i + 256, len(command_bytes))]) + self.serial.write(command_bytes[i : min(i + 256, len(command_bytes))]) time.sleep(0.01) - self.serial.write(b'\x04') + self.serial.write(b"\x04") # check if we could exec command data = self.serial.read(2) - if data != b'OK': - raise PyboardError('could not exec command (response: %r)' % data) + if data != b"OK": + raise PyboardError("could not exec command (response: %r)" % data) def enter_raw_repl(self): - ''' + """ Enter the RAW repl mode. After the prompt character ('>') left in the buffer of the serial line. Returns: None - ''' - self.serial.write(b'\x03\x03') # ctrl-C twice: interrupt any running program + """ + self.serial.write(b"\x03\x03") # ctrl-C twice: interrupt any running program # flush input (without relying on serial.flushInput()) n = self.serial.inWaiting() @@ -127,18 +137,18 @@ class PyCard10(Pyboard): self.serial.read(n) n = self.serial.inWaiting() - self.serial.write(b'\x01') # ctrl-A: enter raw REPL - data = self.read_until(1, b'raw REPL; CTRL-B to exit\r\n>') - if not data.endswith(b'raw REPL; CTRL-B to exit\r\n>'): - raise PyboardError('could not enter raw repl') + self.serial.write(b"\x01") # ctrl-A: enter raw REPL + data = self.read_until(1, b"raw REPL; CTRL-B to exit\r\n>") + if not data.endswith(b"raw REPL; CTRL-B to exit\r\n>"): + raise PyboardError("could not enter raw repl") - self.serial.write(b'\r\x04') # execute nothing + self.serial.write(b"\r\x04") # execute nothing data = self.serial.read(2) - if data != b'OK': - raise PyboardError('could not enter raw repl') + if data != b"OK": + raise PyboardError("could not enter raw repl") def exec(self, command): - ''' + """ Execute a command on the card10 and return the output of the card10. Parameters: @@ -146,30 +156,31 @@ class PyCard10(Pyboard): Returns: data(bytes), data_err(bytes): data is standard out, data_err is standard error - ''' + """ return self.exec_(command) def soft_reset(self): - ''' + """ Doing a soft reset on the board and going to menu. Returns: None - ''' + """ - self.serial.write(b'\x03\x03') # ctrl-C twice: interrupt any running program + self.serial.write(b"\x03\x03") # ctrl-C twice: interrupt any running program - self.serial.write(b'\x01') # ctrl-B: ensue it's the normal mode + self.serial.write(b"\x01") # ctrl-B: ensue it's the normal mode - self.serial.write(b'\x04') # ctrl-D: do the reset + self.serial.write(b"\x04") # ctrl-D: do the reset n = self.serial.inWaiting() while n > 0: self.serial.read(n) n = self.serial.inWaiting() -def execfile(filename, device='/dev/ttyACM0'): - ''' + +def execfile(filename, device="/dev/ttyACM0"): + """ Execute python source from *filename* via the RAW repl mode on the card10 connected via serial line *device*. Parameters: @@ -178,7 +189,7 @@ def execfile(filename, device='/dev/ttyACM0'): Returns: None - ''' + """ c = PyCard10(device) c.enter_raw_repl() output = c.execfile(filename) @@ -186,22 +197,38 @@ def execfile(filename, device='/dev/ttyACM0'): c.exit_raw_repl() c.close() + def main(): - ''' + """ The main method. Returns: None - ''' + """ import argparse - cmd_parser = argparse.ArgumentParser(description='Run scripts on the card10.') - cmd_parser.add_argument('--device', default='/dev/ttyACM0', help='the serial device of the card10') - cmd_parser.add_argument('-c', '--command', help='program passed in as string') - cmd_parser.add_argument('-w', '--wait', default=0, type=int, help='seconds to wait for USB connected board to become available') - cmd_parser.add_argument('--follow', action='store_true', help='follow the output after running the scripts [default if no scripts given]') - cmd_parser.add_argument('--reset', action='store_true', help='Soft reseting the card10') - cmd_parser.add_argument('files', nargs='*', help='input files') + + cmd_parser = argparse.ArgumentParser(description="Run scripts on the card10.") + cmd_parser.add_argument( + "--device", default="/dev/ttyACM0", help="the serial device of the card10" + ) + cmd_parser.add_argument("-c", "--command", help="program passed in as string") + cmd_parser.add_argument( + "-w", + "--wait", + default=0, + type=int, + help="seconds to wait for USB connected board to become available", + ) + cmd_parser.add_argument( + "--follow", + action="store_true", + help="follow the output after running the scripts [default if no scripts given]", + ) + cmd_parser.add_argument( + "--reset", action="store_true", help="Soft reseting the card10" + ) + cmd_parser.add_argument("files", nargs="*", help="input files") args = cmd_parser.parse_args() # open the connection to the card10 @@ -226,7 +253,9 @@ def main(): def execbuffer(buf): try: - ret, ret_err = card10.exec_raw(buf, timeout=None, data_consumer=stdout_write_bytes) + ret, ret_err = card10.exec_raw( + buf, timeout=None, data_consumer=stdout_write_bytes + ) except PyboardError as er: print(er) card10.close() @@ -241,11 +270,11 @@ def main(): # run the command, if given if args.command is not None: - execbuffer(args.command.encode('utf-8')) + execbuffer(args.command.encode("utf-8")) # run any files for filename in args.files: - with open(filename, 'rb') as f: + with open(filename, "rb") as f: pyfile = f.read() execbuffer(pyfile) @@ -269,5 +298,6 @@ def main(): # close the connection to the card10 card10.close() + if __name__ == "__main__": main() -- GitLab From 1e7762c7ae158936970d11aba05620b58faffe33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20B=C3=B6hm?= Date: Tue, 27 Aug 2019 15:09:57 +0200 Subject: [PATCH 6/9] Moved changed into another branch --- pycardium/modules/bhi160-sys.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/pycardium/modules/bhi160-sys.c b/pycardium/modules/bhi160-sys.c index 7320885c..db0a5c9a 100644 --- a/pycardium/modules/bhi160-sys.c +++ b/pycardium/modules/bhi160-sys.c @@ -66,17 +66,6 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1( mp_bhi160_disable_sensor_obj, mp_bhi160_disable_sensor ); -STATIC mp_obj_t mp_bhi160_disable_all_sensors() -{ - epic_bhi160_disable_all_sensors(); - - return mp_const_none; -} - -STATIC MP_DEFINE_CONST_FUN_OBJ_0( - mp_bhi160_disable_all_sensors_obj, mp_bhi160_disable_all_sensors -); - STATIC const mp_rom_map_elem_t bhi160_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sys_bhi160) }, { MP_ROM_QSTR(MP_QSTR_enable_sensor), @@ -85,8 +74,6 @@ STATIC const mp_rom_map_elem_t bhi160_module_globals_table[] = { MP_ROM_PTR(&mp_bhi160_read_sensor_obj) }, { MP_ROM_QSTR(MP_QSTR_disable_sensor), MP_ROM_PTR(&mp_bhi160_disable_sensor_obj) }, - { MP_ROM_QSTR(MP_QSTR_disable_all_sensors), - MP_ROM_PTR(&mp_bhi160_disable_all_sensors_obj) }, }; STATIC MP_DEFINE_CONST_DICT(bhi160_module_globals, bhi160_module_globals_table); -- GitLab From 548ebbf2806b18edececa6bfeabcaf98dbe3f549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20B=C3=B6hm?= Date: Tue, 27 Aug 2019 15:11:49 +0200 Subject: [PATCH 7/9] Moved pycard10 and pyboard to tools --- Documentation/pycardium/overview.rst | 4 ++-- {pycardium/tools => tools}/pyboard.py | 0 {pycardium/tools => tools}/pycard10.py | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename {pycardium/tools => tools}/pyboard.py (100%) rename {pycardium/tools => tools}/pycard10.py (100%) diff --git a/Documentation/pycardium/overview.rst b/Documentation/pycardium/overview.rst index 085d19b4..49370ada 100644 --- a/Documentation/pycardium/overview.rst +++ b/Documentation/pycardium/overview.rst @@ -75,7 +75,7 @@ CTRL-D the whole entered code will be evaluated and executed. The board will reply with **OK** and print after that the output (print commands) of the code or give you tracebacks if an error occured. -You can use **pycard10** (pycardium/tools/pycard10.py) to execute python files -from your PC directly on the card10. +You can use **pycard10** (tools/pycard10.py) to execute python files from your +PC directly on the card10. diff --git a/pycardium/tools/pyboard.py b/tools/pyboard.py similarity index 100% rename from pycardium/tools/pyboard.py rename to tools/pyboard.py diff --git a/pycardium/tools/pycard10.py b/tools/pycard10.py similarity index 100% rename from pycardium/tools/pycard10.py rename to tools/pycard10.py -- GitLab From 293efcb198e7574384a4251fa2f120bd19627c3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20B=C3=B6hm?= Date: Tue, 27 Aug 2019 15:19:14 +0200 Subject: [PATCH 8/9] Removed added qstr label --- pycardium/modules/qstrdefs.h | 1 - 1 file changed, 1 deletion(-) diff --git a/pycardium/modules/qstrdefs.h b/pycardium/modules/qstrdefs.h index 75c8876e..c5813602 100644 --- a/pycardium/modules/qstrdefs.h +++ b/pycardium/modules/qstrdefs.h @@ -67,7 +67,6 @@ Q(RTC_ALARM) Q(sys_bhi160) Q(enable_sensor) Q(disable_sensor) -Q(disable_all_sensors) Q(read_sensor) Q(x) Q(y) -- GitLab From 1f1612122e0b7ce30ac8f2bdfcd12449c82d9dbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20B=C3=B6hm?= Date: Tue, 27 Aug 2019 15:43:47 +0200 Subject: [PATCH 9/9] Updated path references --- tools/pycard10.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/pycard10.py b/tools/pycard10.py index 00e35a9e..d5b51e7e 100755 --- a/tools/pycard10.py +++ b/tools/pycard10.py @@ -52,10 +52,10 @@ Then: This script can also be run directly. To execute a local script, use: - pycardium/tools/pycard10.py test.py + tools/pycard10.py test.py Or: - python3 pycardium/tools/pycard10.py test.py + python3 tools/pycard10.py test.py """ -- GitLab