diff --git a/.gitmodules b/.gitmodules index ba05cc2..f43ab5e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,4 +3,4 @@ url = https://github.com/ev3dev/ev3dev-lang.git [submodule "tests/fake-sys"] path = tests/fake-sys - url = https://github.com/rhempel/ev3dev-lang-fake-sys.git + url = https://github.com/ev3dev/ev3dev-lang-fake-sys.git diff --git a/.travis.yml b/.travis.yml index 1be1447..45b526c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,8 @@ deploy: provider: pypi user: Denis.Demidov password: - secure: W13pXc2pp2A9gBUDz2QW/K3OfDT/cn/iapkW6NMdkwRQ+CL7MWXsY2qayfxs6QPfR5W7838pmMXogLOie70n8Hov5/XIrou+dEz/xh6If2VFG41KUz/tP9cy1yUhDYeADn5I+mr+8Yrh59OkHkaeZH1EFndbsxY4pyXV0DT+FNVg4eLc8sE5ZCfa/itVdsqp8M4xrMe8NO/ldnFEIHFymyEMTZHR5qoJD+Uk+AGMR4cSeSuOrZixnSLpQViZDFHpwej6F1LrreLltbT9ChjSEmdPAr1Jp1ReQfuD+vBzUMkhVfZyEo+fY1x8FZVPrTAEtbJGDhvTAsrV1KTgPqanMyyrCIG+OWfCSYYPyg7MYbAJVfB+P0BRp1cm2D5oFrpAZE129oVtATOykQarFzzRFhH4Tyc04WriyY/greEAe58MqYoJAZXUIe/JLf9+GLdjBKD07+q7QMZvyEEdsDCPYkqKQeuwrWZ/JlpWO5rmy12L23aYzvJqhcKo9LZQOY8LkmFmxuxt7k5eb/3iZ1trhj/lwoLLSu8l29B8cK3dax38URie0x9bMMhQRxaO59JQuhGuu0sNHiuFQHs6wLs/V8ff1IZIlRSlzztqIP3KW514TEdsFIuF0Gpn5wvagAXFbSnyxAUUTy81eQjY/ExTiUHKfU3zjluaAYGt9zjF0Bc= + secure: cTDN4WMJEjxZ0zwFwkLPpOIOSG/1JlHbQsHzd/tV9LfjBMR0nJR5HrmyiIO1TE5Wo6AFSFd7S3JmKdEnFO1ByvN/EFtsKGRUR9L6Yst4KfFi4nHwdZ7vgPTV0nNdvgC1YM/3bx8W8PcjFSm/k8awx7aicwXj09yDA8ENTf5XedaX22Z+9OKhb1mKola1cqEoc0GwaYzd8UX0Ruwh9/6RRbvTt7zn8BCZc9vIVqNd6mZgBWY9zAU40ZZsjYORbiZmDNCygEI+RViZ51M58WYkPPhoivzcG9em8DMRS7SC3CjWGapiOaXUHa3Fhnn+IQ+Q8Xv9YU5+qmj65MWQy3SSnMnvuxmrLf4aLoOlSJUMhDarjni4tdBOTX5PdOkdmhskyQt1DqDrw0+WhPItYfGe5zQfQwqW+YOpGbOipAeU+844arPo5jvZG/IOBX2qVUwdSxo8Y/98ocjqoZOq8b5xkWtJef0Kh1RCkp1bR2XELQVe76qeWqQxWz3OPqq+wK3xeNvj5kMQmytl3dCEB//D6UcES7Qr8YxD+LWoaIf32JIj/4LaCXXuxMVH+PJ68Oc72/ox0qLmXK0qhbea2QvaqXyGDrO+a2X6VbMdH32D2xHzH4Mg75xLnXnSaFvGovhYl1zEVcYUDioxrXZEuDmymGf9nH2mivJ24Fon6u+C3QQ= on: tags: true - repo: rhempel/ev3dev-lang-python + condition: $TRAVIS_TAG != ev3dev-stretch/* + repo: ev3dev/ev3dev-lang-python diff --git a/debian/changelog b/debian/changelog index 6f80429..d9ccd9f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,31 @@ +python-ev3dev (1.2.0) stable; urgency=medium + + [ Denis Demidov ] + * Return handle to aplay in Sound.espeak + + [ David Lechner ] + * PiStorms support + * Update BrickPi port names for 4.14 kernel + * Fix brickpi led names + * brickpi3 support + * Fix EV3 framebuffer for 4.14 kernel + + -- David Lechner Sat, 05 May 2018 10:37:02 -0500 + +python-ev3dev (1.1.1) testing; urgency=medium + + [Denis Demidov] + * Fixed Screen class on Debian Stretch + + -- Denis Demidov Sun, 29 Oct 2017 11:29:00 +0300 + +python-ev3dev (1.1.0) testing; urgency=medium + + [Daniel Walton] + * Fixes to make the library work on Debian Stretch. + + -- Denis Demidov Fri, 20 Oct 2017 09:29:00 +0300 + python-ev3dev (1.0.0) stable; urgency=medium [Denis Demidov] diff --git a/debian/control b/debian/control index e80bae1..b2a7c16 100644 --- a/debian/control +++ b/debian/control @@ -3,7 +3,7 @@ Maintainer: Ralph Hempel Section: python Priority: optional Standards-Version: 3.9.5 -Build-Depends: python3-setuptools (>= 0.6b3), python3-all (>= 3.4), debhelper (>= 9), dh-python +Build-Depends: python3-setuptools (>= 0.6b3), python3-all (>= 3.4), debhelper (>= 9), dh-python, python3-pillow VCS-Git: git://github.com/rhempel/ev3dev-lang-python.git VCS-Browser: https://github.com/rhempel/ev3dev-lang-python diff --git a/debian/gbp.conf b/debian/gbp.conf new file mode 100644 index 0000000..e723f5a --- /dev/null +++ b/debian/gbp.conf @@ -0,0 +1,3 @@ +[DEFAULT] +debian-branch=ev3dev-stretch +debian-tag=ev3dev-stretch/%(version)s diff --git a/debian/release.sh b/debian/release.sh new file mode 100755 index 0000000..79d44e1 --- /dev/null +++ b/debian/release.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# +# Maintainer script for publishing releases. + +set -e + +source=$(dpkg-parsechangelog -S Source) +version=$(dpkg-parsechangelog -S Version) +distribution=$(dpkg-parsechangelog -S Distribution) +codename=$(debian-distro-info --codename --${distribution}) + +OS=debian DIST=${codename} ARCH=amd64 pbuilder-ev3dev build +OS=raspbian DIST=${codename} ARCH=armhf pbuilder-ev3dev build + +debsign ~/pbuilder-ev3dev/debian/${codename}-amd64/${source}_${version}_amd64.changes +debsign ~/pbuilder-ev3dev/raspbian/${codename}-armhf/${source}_${version}_armhf.changes + +dput ev3dev-debian ~/pbuilder-ev3dev/debian/${codename}-amd64/${source}_${version}_amd64.changes +dput ev3dev-raspbian ~/pbuilder-ev3dev/raspbian/${codename}-armhf/${source}_${version}_armhf.changes + +gbp buildpackage --git-tag-only diff --git a/ev3dev-lang b/ev3dev-lang index 008804f..0169750 160000 --- a/ev3dev-lang +++ b/ev3dev-lang @@ -1 +1 @@ -Subproject commit 008804f5c28acd225f1b43057be2060d9965b2c1 +Subproject commit 0169750bf9aeefac423c38929287b4ad9ef7abd4 diff --git a/ev3dev/brickpi.py b/ev3dev/brickpi.py index 1df0483..6cfec2f 100644 --- a/ev3dev/brickpi.py +++ b/ev3dev/brickpi.py @@ -29,15 +29,15 @@ from .core import * -OUTPUT_A = 'ttyAMA0:MA' -OUTPUT_B = 'ttyAMA0:MB' -OUTPUT_C = 'ttyAMA0:MC' -OUTPUT_D = 'ttyAMA0:MD' +OUTPUT_A = 'serial0-0:MA' +OUTPUT_B = 'serial0-0:MB' +OUTPUT_C = 'serial0-0:MC' +OUTPUT_D = 'serial0-0:MD' -INPUT_1 = 'ttyAMA0:S1' -INPUT_2 = 'ttyAMA0:S2' -INPUT_3 = 'ttyAMA0:S3' -INPUT_4 = 'ttyAMA0:S4' +INPUT_1 = 'serial0-0:S1' +INPUT_2 = 'serial0-0:S2' +INPUT_3 = 'serial0-0:S3' +INPUT_4 = 'serial0-0:S4' class Leds(object): @@ -45,16 +45,14 @@ class Leds(object): The BrickPi LEDs. """ -# ~autogen led-colors platforms.brickpi.led>currentClass + blue_led1 = Led(name_pattern='led1:blue:brick-status') + blue_led2 = Led(name_pattern='led2:blue:brick-status') - blue_led1 = Led(name_pattern='brickpi:led1:blue:ev3dev') - blue_led2 = Led(name_pattern='brickpi:led2:blue:ev3dev') + LED1 = (blue_led1,) + LED2 = (blue_led2,) - LED1 = ( blue_led1, ) - LED2 = ( blue_led2, ) - - BLACK = ( 0, ) - BLUE = ( 1, ) + BLACK = (0,) + BLUE = (1,) @staticmethod def set_color(group, color, pct=1): @@ -90,6 +88,3 @@ def all_off(): """ Leds.blue_led1.brightness = 0 Leds.blue_led2.brightness = 0 - - -# ~autogen diff --git a/ev3dev/brickpi3.py b/ev3dev/brickpi3.py new file mode 100644 index 0000000..3524a71 --- /dev/null +++ b/ev3dev/brickpi3.py @@ -0,0 +1,85 @@ +# ------------------------------------------------------------------------------ +# Copyright (c) 2013 David Lechner +# +# 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 NON INFRINGEMENT. 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. +# ----------------------------------------------------------------------------- + +""" +An assortment of classes modeling specific features of the BrickPi. +""" + +from .core import * + + +OUTPUT_A = 'spi0.1:MA' +OUTPUT_B = 'spi0.1:MB' +OUTPUT_C = 'spi0.1:MC' +OUTPUT_D = 'spi0.1:MD' + +INPUT_1 = 'spi0.1:S1' +INPUT_2 = 'spi0.1:S2' +INPUT_3 = 'spi0.1:S3' +INPUT_4 = 'spi0.1:S4' + + +class Leds(object): + """ + The BrickPi3 LEDs. + """ + + amber_led = Led(name_pattern='led0:blue:brick-status') + + LED = (amber_led,) + + BLACK = (0,) + AMBER = (1,) + + @staticmethod + def set_color(group, color, pct=1): + """ + Sets brigthness of leds in the given group to the values specified in + color tuple. When percentage is specified, brightness of each led is + reduced proportionally. + + Example:: + + Leds.set_color(LEFT, AMBER) + """ + for l, v in zip(group, color): + l.brightness_pct = v * pct + + @staticmethod + def set(group, **kwargs): + """ + Set attributes for each led in group. + + Example:: + + Leds.set(LEFT, brightness_pct=0.5, trigger='timer') + """ + for led in group: + for k in kwargs: + setattr(led, k, kwargs[k]) + + @staticmethod + def all_off(): + """ + Turn all leds off + """ + Leds.amber_led.brightness = 0 diff --git a/ev3dev/core.py b/ev3dev/core.py index 8747923..29b3250 100644 --- a/ev3dev/core.py +++ b/ev3dev/core.py @@ -42,6 +42,7 @@ import fnmatch import numbers import array +import logging import mmap import ctypes import re @@ -53,6 +54,8 @@ from struct import pack, unpack from subprocess import Popen, check_output, PIPE +log = logging.getLogger(__name__) + try: # This is a linux-specific module. # It is required by the Button() class, but failure to import it may be @@ -195,7 +198,8 @@ def _get_attribute(self, attribute, name): attribute.seek(0) return attribute, attribute.read().strip().decode() else: - raise Exception('Device is not connected') + log.info("%s: path %s, attribute %s" % (self, self._path, name)) + raise Exception("%s is not connected" % self) def _set_attribute(self, attribute, name, value): """Device attribute setter""" @@ -208,7 +212,8 @@ def _set_attribute(self, attribute, name, value): attribute.flush() return attribute else: - raise Exception('Device is not connected') + log.info("%s: path %s, attribute %s" % (self, self._path, name)) + raise Exception("%s is not connected" % self) def get_attr_int(self, attribute, name): attribute, value = self._get_attribute(attribute, name) @@ -3239,8 +3244,19 @@ def __init__(self): from PIL import Image, ImageDraw FbMem.__init__(self) - self._img = Image.new( - self.var_info.bits_per_pixel == 1 and "1" or "RGB", + if self.var_info.bits_per_pixel == 1: + im_type = "1" + elif self.var_info.bits_per_pixel == 16: + im_type = "RGB" + elif self.var_info.bits_per_pixel == 32: + # EV3 LCD is currently only screen that identifies as 32bpp, but + # it is really 2bpp grayscal. "L" is 8bpp grayscale, which is the + # closest match in PIL + im_type = "L" + else: + raise RuntimeError("Screen color depth not supported") + + self._img = Image.new(im_type, (self.fix_info.line_length * 8 // self.var_info.bits_per_pixel, self.yres), "white") @@ -3306,15 +3322,23 @@ def _img_to_rgb565_bytes(self): pixels = [self._color565(r, g, b) for (r, g, b) in self._img.getdata()] return pack('H' * len(pixels), *pixels) + def _img_to_xrgb_bytes(self): + # convert grayscale to XRGB + pixels = [0x010101 * v for v in self._img.getdata()] + return pack('I' * len(pixels), *pixels) + def update(self): """ Applies pending changes to the screen. Nothing will be drawn on the screen until this function is called. """ if self.var_info.bits_per_pixel == 1: - self.mmap[:] = self._img.tobytes("raw", "1;IR") + b = self._img.tobytes("raw", "1;R") + self.mmap[:len(b)] = b elif self.var_info.bits_per_pixel == 16: self.mmap[:] = self._img_to_rgb565_bytes() + elif self.var_info.bits_per_pixel == 32: + self.mmap[:] = self._img_to_xrgb_bytes() else: raise Exception("Not supported") @@ -3445,7 +3469,7 @@ def speak(text, espeak_opts='-a 200 -s 130'): cmd_line = '/usr/bin/espeak --stdout {0} "{1}"'.format(espeak_opts, text) espeak = Popen(shlex.split(cmd_line), stdout=PIPE) play = Popen(['/usr/bin/aplay', '-q'], stdin=espeak.stdout, stdout=n) - return espeak + return play @staticmethod def _get_channel(): diff --git a/ev3dev/ev3.py b/ev3dev/ev3.py index 725f713..c9ec23f 100644 --- a/ev3dev/ev3.py +++ b/ev3dev/ev3.py @@ -47,10 +47,10 @@ class Leds(object): # ~autogen led-colors platforms.ev3.led>currentClass - red_left = Led(name_pattern='ev3:left:red:ev3dev') - red_right = Led(name_pattern='ev3:right:red:ev3dev') - green_left = Led(name_pattern='ev3:left:green:ev3dev') - green_right = Led(name_pattern='ev3:right:green:ev3dev') + red_left = Led(name_pattern='led0:red:brick-status') + red_right = Led(name_pattern='led1:red:brick-status') + green_left = Led(name_pattern='led0:green:brick-status') + green_right = Led(name_pattern='led1:green:brick-status') LEFT = ( red_left, green_left, ) RIGHT = ( red_right, green_right, ) @@ -165,12 +165,12 @@ def on_backspace(state): _buttons = { - 'up': {'name': '/dev/input/by-path/platform-gpio-keys.0-event', 'value': 103}, - 'down': {'name': '/dev/input/by-path/platform-gpio-keys.0-event', 'value': 108}, - 'left': {'name': '/dev/input/by-path/platform-gpio-keys.0-event', 'value': 105}, - 'right': {'name': '/dev/input/by-path/platform-gpio-keys.0-event', 'value': 106}, - 'enter': {'name': '/dev/input/by-path/platform-gpio-keys.0-event', 'value': 28}, - 'backspace': {'name': '/dev/input/by-path/platform-gpio-keys.0-event', 'value': 14}, + 'up': {'name': '/dev/input/by-path/platform-gpio_keys-event', 'value': 103}, + 'down': {'name': '/dev/input/by-path/platform-gpio_keys-event', 'value': 108}, + 'left': {'name': '/dev/input/by-path/platform-gpio_keys-event', 'value': 105}, + 'right': {'name': '/dev/input/by-path/platform-gpio_keys-event', 'value': 106}, + 'enter': {'name': '/dev/input/by-path/platform-gpio_keys-event', 'value': 28}, + 'backspace': {'name': '/dev/input/by-path/platform-gpio_keys-event', 'value': 14}, } @property diff --git a/ev3dev/pistorms.py b/ev3dev/pistorms.py new file mode 100644 index 0000000..21e89e9 --- /dev/null +++ b/ev3dev/pistorms.py @@ -0,0 +1,130 @@ +# ------------------------------------------------------------------------------ +# Copyright (c) 2018 David Lechner +# +# 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 NON INFRINGEMENT. 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. +# ----------------------------------------------------------------------------- + +""" +An assortment of classes modeling specific features of the EV3 brick. +""" + +from .core import * + + +OUTPUT_A = 'pistorms:BAM1' +OUTPUT_B = 'pistorms:BAM2' +OUTPUT_C = 'pistorms:BBM1' +OUTPUT_D = 'pistorms:BBM2' + +INPUT_1 = 'pistorms:BAS1' +INPUT_2 = 'pistorms:BAS2' +INPUT_3 = 'pistorms:BBS1' +INPUT_4 = 'pistorms:BBS2' + + +class Leds(object): + """ + The PiStorms LEDs. + """ + + red_left = Led(name_pattern='pistorms:BB:red:brick-status') + red_right = Led(name_pattern='pistorms:BA:red:brick-status') + green_left = Led(name_pattern='pistorms:BB:green:brick-status') + green_right = Led(name_pattern='pistorms:BA:green:brick-status') + blue_left = Led(name_pattern='pistorms:BB:blue:brick-status') + blue_right = Led(name_pattern='pistorms:BA:blue:brick-status') + + LEFT = (red_left, green_left, blue_left) + RIGHT = (red_right, green_right, blue_right) + + BLACK = (0, 0, 0) + RED = (1, 0, 0) + GREEN = (0, 1, 0) + BLUE = (0, 0, 1) + YELLOW = (1, 1, 0) + CYAN = (0, 1, 1) + MAGENTA = (1, 0, 1) + + @staticmethod + def set_color(group, color, pct=1): + """ + Sets brightness of leds in the given group to the values specified in + color tuple. When percentage is specified, brightness of each led is + reduced proportionally. + + Example:: + + Leds.set_color(LEFT, MAGENTA) + """ + for l, v in zip(group, color): + l.brightness_pct = v * pct + + @staticmethod + def set(group, **kwargs): + """ + Set attributes for each led in group. + + Example:: + + Leds.set(LEFT, brightness_pct=0.5, trigger='timer') + """ + for led in group: + for k in kwargs: + setattr(led, k, kwargs[k]) + + @staticmethod + def all_off(): + """ + Turn all leds off + """ + Leds.red_left.brightness = 0 + Leds.red_right.brightness = 0 + Leds.green_left.brightness = 0 + Leds.green_right.brightness = 0 + Leds.blue_left.brightness = 0 + Leds.blue_right.brightness = 0 + + +class Button(ButtonEVIO): + """ + PiStorms Buttons + """ + + @staticmethod + def on_go(state): + """ + This handler is called by `process()` whenever state of 'enter' button + has changed since last `process()` call. `state` parameter is the new + state of the button. + """ + pass + + _buttons = { + 'go': { + 'name': '/dev/input/by-path/platform-3f804000.i2c-event', + 'value': 103, + }, + } + + @property + def go(self): + """ + Check if 'go' button is pressed. + """ + return 'go' in self.buttons_pressed diff --git a/tests/README.md b/tests/README.md index b8f0017..0c7adf4 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,8 +1,27 @@ +# fake-sys directory +The tests require the fake-sys directory which comes from +https://github.com/ddemidov/ev3dev-lang-fake-sys + +If you have already cloned the ev3dev-lang-python repo but do not have the +`fake-sys` directory use `git submodule init` to get it. If you have not +already cloned the ev3dev-lang-python repo you can use the `--recursive` option +when you git clone. Example: + +``` +$ git clone --recursive https://github.com/rhempel/ev3dev-lang-python.git +``` + +# Running Tests +To run the tests do: +``` +$ ./api_tests.py +``` + +# Misc Commands used to copy the /sys/class node: -```sh -node=lego-sensor/sensor0 -mkdir -p ./${node} -# Copy contents of special files, do not follow symlinks: -cp -P --copy-contents -r /sys/class/${node}/* ./${node}/ +``` +$ node=lego-sensor/sensor0 +$ mkdir -p ./${node} +$ cp -P --copy-contents -r /sys/class/${node}/* ./${node}/ ``` diff --git a/tests/api_tests.py b/tests/api_tests.py index 6edb48e..7476352 100755 --- a/tests/api_tests.py +++ b/tests/api_tests.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import unittest, sys, os FAKE_SYS = os.path.join(os.path.dirname(__file__), 'fake-sys')