├── ev3 ├── __init__.py ├── mindsensors.py ├── lego.py └── ev3dev.py ├── run_all_tests.sh ├── run_all_tests_python3.sh ├── test ├── util.py ├── test_ev3_battery_percentage.py ├── test_ev3_i2cs.py ├── test_ev3_tone.py ├── test_lego_touch_sensor.py ├── test_lego_large_motor.py ├── test_lego_medium_motor.py ├── test_mindsensors_pspnxv4.py ├── test_mindsensors_absolute_IMU.py ├── test_lego_gyro_sensor.py ├── test_lego_color_sensor.py ├── test_lego_infrared_sensor_sensor.py ├── test_ev3_msensor.py ├── test_lego_ultrasonic_sensor.py ├── test_ev3_lcd.py ├── test_ev3_led.py ├── test_ev3_key.py └── test_ev3_motor.py ├── smbus-build ├── py-smbus-python3 │ ├── build-smbus.sh │ └── python3.patch ├── multistrap.conf └── build.sh ├── setup.py ├── .gitignore ├── README.md └── LICENSE /ev3/__init__.py: -------------------------------------------------------------------------------- 1 | __all__ = ['ev3dev', 'lego','mindsensors'] -------------------------------------------------------------------------------- /run_all_tests.sh: -------------------------------------------------------------------------------- 1 | python -m unittest discover test 'test_*.py' -------------------------------------------------------------------------------- /run_all_tests_python3.sh: -------------------------------------------------------------------------------- 1 | python3 -m unittest discover test 'test_*.py' 2 | -------------------------------------------------------------------------------- /test/util.py: -------------------------------------------------------------------------------- 1 | import sys 2 | def get_input(prompt): 3 | if sys.hexversion > 0x03000000: 4 | return input(prompt) 5 | else: 6 | return raw_input(prompt) -------------------------------------------------------------------------------- /smbus-build/py-smbus-python3/build-smbus.sh: -------------------------------------------------------------------------------- 1 | SCRIPT=$(readlink -f "$0") 2 | SCRIPTPATH=$(dirname "$SCRIPT") 3 | cd $SCRIPTPATH 4 | export LANG=C 5 | rm -r i2c-tools-3.1.1 6 | rm *.deb 7 | apt-get source i2c-tools 8 | cd i2c-tools-3.1.1 9 | patch -p1<../python3.patch 10 | dpkg-buildpackage -uc -B 11 | -------------------------------------------------------------------------------- /test/test_ev3_battery_percentage.py: -------------------------------------------------------------------------------- 1 | import ev3.ev3dev 2 | import unittest 3 | from util import get_input 4 | 5 | 6 | class TestI2CS(unittest.TestCase): 7 | 8 | def test_battery(self): 9 | print(ev3.ev3dev.get_battery_percentage()) 10 | if __name__ == '__main__': 11 | unittest.main() 12 | -------------------------------------------------------------------------------- /test/test_ev3_i2cs.py: -------------------------------------------------------------------------------- 1 | from ev3.ev3dev import I2CS 2 | import unittest 3 | from util import get_input 4 | 5 | 6 | class TestI2CS(unittest.TestCase): 7 | 8 | def test_i2cs(self): 9 | get_input('Attach an I2CS sensor on port 3 then continue') 10 | d = I2CS(port=3,addr=0x01) 11 | print(d.read_byte()) 12 | print(d.read_byte_data(0x00)) 13 | if __name__ == '__main__': 14 | unittest.main() 15 | -------------------------------------------------------------------------------- /test/test_ev3_tone.py: -------------------------------------------------------------------------------- 1 | from ev3.ev3dev import Tone 2 | import unittest 3 | from util import get_input 4 | import time 5 | 6 | class TestTone(unittest.TestCase): 7 | 8 | def test_tone(self): 9 | get_input('Test beep') 10 | d= Tone() 11 | d.play(1000, 3000) 12 | time.sleep(3) 13 | d.play(1000) 14 | time.sleep(3) 15 | d.stop() 16 | 17 | if __name__ == '__main__': 18 | unittest.main() 19 | -------------------------------------------------------------------------------- /test/test_lego_touch_sensor.py: -------------------------------------------------------------------------------- 1 | from ev3.lego import TouchSensor 2 | import unittest 3 | from util import get_input 4 | 5 | class TestTouchSensor(unittest.TestCase): 6 | def test_touch_sensor(self): 7 | get_input('Attach a TouchSensor then continue') 8 | d = TouchSensor() 9 | get_input('test pushed') 10 | print(d.is_pushed) 11 | print(d.mode) 12 | 13 | 14 | if __name__ == '__main__': 15 | unittest.main() 16 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from setuptools import setup 4 | 5 | setup(name='python-ev3', 6 | version='0.0.2.4', 7 | license='Apache License 2.0', 8 | description='Python library of ev3dev firmware', 9 | author='Gong Yi', 10 | author_email='topikachu@163.com', 11 | url='https://github.com/topikachu/python-ev3', 12 | packages=['ev3'], 13 | download_url="https://github.com/topikachu/python-ev3/releases/tag/0.0.2.4" 14 | ) 15 | -------------------------------------------------------------------------------- /test/test_lego_large_motor.py: -------------------------------------------------------------------------------- 1 | from ev3.lego import LargeMotor 2 | import unittest 3 | import time 4 | from util import get_input 5 | 6 | 7 | class TestLargeMotor(unittest.TestCase): 8 | def test_large_motor(self): 9 | get_input('Attach a LargeMotor then continue') 10 | d = LargeMotor() 11 | print(d.type) 12 | d.run_forever(100, regulation_mode=False) 13 | print(d.run) 14 | time.sleep(5) 15 | d.stop() 16 | if __name__ == '__main__': 17 | unittest.main() 18 | -------------------------------------------------------------------------------- /test/test_lego_medium_motor.py: -------------------------------------------------------------------------------- 1 | from ev3.lego import MediumMotor 2 | import unittest 3 | import time 4 | from util import get_input 5 | 6 | 7 | class TesMediumMotor(unittest.TestCase): 8 | def test_medium_motor(self): 9 | get_input('Attach a MediumMotor then continue') 10 | d = MediumMotor() 11 | print(d.type) 12 | d.run_forever(100, regulation_mode=False) 13 | print(d.run) 14 | time.sleep(5) 15 | d.stop() 16 | if __name__ == '__main__': 17 | unittest.main() 18 | -------------------------------------------------------------------------------- /test/test_mindsensors_pspnxv4.py: -------------------------------------------------------------------------------- 1 | from ev3.mindsensors import PSPNxV4 2 | import unittest 3 | from util import get_input 4 | 5 | 6 | class TestPSPNxV4(unittest.TestCase): 7 | def test_PSPNxV4(self): 8 | get_input('Attach a PSPNxV4 at port 3 then continue') 9 | d = PSPNxV4(3) 10 | print(d.version) 11 | print(d.vendor_id) 12 | print(d.device_id) 13 | get_input('test button_set_1') 14 | print(d.button_set_1) 15 | 16 | 17 | 18 | if __name__ == '__main__': 19 | unittest.main() 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | 21 | # Installer logs 22 | pip-log.txt 23 | 24 | # Unit test / coverage reports 25 | .coverage 26 | .tox 27 | nosetests.xml 28 | 29 | # Translations 30 | *.mo 31 | 32 | # Mr Developer 33 | .mr.developer.cfg 34 | .project 35 | .pydevproject 36 | /.settings 37 | 38 | .nfs* 39 | 40 | python-ev3.sublime-project 41 | python-ev3.sublime-workspace 42 | 43 | ev3-rootfs 44 | -------------------------------------------------------------------------------- /smbus-build/multistrap.conf: -------------------------------------------------------------------------------- 1 | [General] 2 | arch=armel 3 | directory=ev3-rootfs 4 | noauth=false 5 | unpack=true 6 | debootstrap=Emdebian Languages 7 | aptsources=Emdebian 8 | 9 | [Emdebian] 10 | packages=apt 11 | source=http://ftp.cn.debian.org/debian/ 12 | keyring=debian-archive-keyring 13 | suite=jessie 14 | 15 | 16 | 17 | [Languages] 18 | #General purpose languages 19 | packages=gcc python3 python3-dev python3-setuptools dpkg-dev devscripts fakeroot build-essential python-all-dev debhelper 20 | source=http://ftp.cn.debian.org/debian/ 21 | keyring=debian-archive-keyring 22 | suite=jessie 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /test/test_mindsensors_absolute_IMU.py: -------------------------------------------------------------------------------- 1 | from ev3.mindsensors import AbsoluteIMU 2 | import unittest 3 | from util import get_input 4 | 5 | class TestAbsoluteIMU(unittest.TestCase): 6 | 7 | def test_absolute_IMU(self): 8 | get_input('Attach a AbsoluteIMU at port 2 then continue') 9 | d = AbsoluteIMU(2) 10 | print(d.version) 11 | print(d.vendor_id) 12 | print(d.device_id) 13 | get_input('test compass') 14 | print(d.compass) 15 | get_input('test x_gyro') 16 | print(d.x_gyro) 17 | 18 | if __name__ == '__main__': 19 | unittest.main() 20 | -------------------------------------------------------------------------------- /test/test_lego_gyro_sensor.py: -------------------------------------------------------------------------------- 1 | from ev3.lego import GyroSensor 2 | import unittest 3 | from util import get_input 4 | 5 | class TestGyroSensor(unittest.TestCase): 6 | def test_gyro_sensor(self): 7 | get_input('Attach a GyroSensor then continue') 8 | d = GyroSensor() 9 | get_input('test ang') 10 | print(d.ang) 11 | print(d.mode) 12 | get_input('test rate') 13 | print(d.rate) 14 | print(d.mode) 15 | get_input('test ang_and_rate') 16 | print(d.ang_and_rate) 17 | print(d.mode) 18 | 19 | 20 | if __name__ == '__main__': 21 | unittest.main() 22 | -------------------------------------------------------------------------------- /test/test_lego_color_sensor.py: -------------------------------------------------------------------------------- 1 | from ev3.lego import ColorSensor 2 | import unittest 3 | from util import get_input 4 | 5 | class TestColorSensor(unittest.TestCase): 6 | def test_color_sensor(self): 7 | get_input('Attach a ColorSensor then continue') 8 | d = ColorSensor() 9 | get_input('test rgb') 10 | print(d.rgb) 11 | print(d.mode) 12 | get_input('test color') 13 | print(d.color) 14 | print(d.mode) 15 | get_input('test reflect') 16 | print(d.reflect) 17 | print(d.mode) 18 | get_input('test ambient') 19 | print(d.ambient) 20 | print(d.mode) 21 | 22 | 23 | if __name__ == '__main__': 24 | unittest.main() 25 | -------------------------------------------------------------------------------- /test/test_lego_infrared_sensor_sensor.py: -------------------------------------------------------------------------------- 1 | from ev3.lego import InfraredSensor 2 | import unittest 3 | from util import get_input 4 | 5 | class TestInfraredSensor(unittest.TestCase): 6 | def test_infrared_sensor(self): 7 | get_input('Attach a InfraredSensor then continue') 8 | d= InfraredSensor() 9 | print(d.mode) 10 | get_input('test proxy') 11 | print(d.prox) 12 | print(d.mode) 13 | get_input('test remote') 14 | print(d.remote) 15 | print(d.mode) 16 | get_input('test remote_bin') 17 | print(d.remote_bin) 18 | print(d.mode) 19 | get_input('test seek') 20 | print(d.seek) 21 | print(d.mode) 22 | 23 | 24 | if __name__ == '__main__': 25 | unittest.main() -------------------------------------------------------------------------------- /test/test_ev3_msensor.py: -------------------------------------------------------------------------------- 1 | from ev3.ev3dev import Msensor 2 | import unittest 3 | from util import get_input 4 | import glob 5 | 6 | class TestMsensor(unittest.TestCase): 7 | 8 | def test_msensor(self): 9 | get_input('Attach a Msensor on port 1 then continue') 10 | d = Msensor(port=1) 11 | print(d.mode) 12 | print(d.port) 13 | if (len(glob.glob('/sys/class/msensor/sensor*/type_id')) >0): 14 | type_id = d.type_id 15 | print(type_id) 16 | d = Msensor(type_id=type_id) 17 | if (len(glob.glob('/sys/class/msensor/sensor*/name')) >0): 18 | name = d.name 19 | print(name) 20 | d = Msensor(name=name) 21 | print(d.mode) 22 | print(d.port) 23 | 24 | if __name__ == '__main__': 25 | unittest.main() 26 | -------------------------------------------------------------------------------- /test/test_lego_ultrasonic_sensor.py: -------------------------------------------------------------------------------- 1 | from ev3.lego import UltrasonicSensor 2 | import unittest 3 | from util import get_input 4 | 5 | class TestUltrasonicSensor(unittest.TestCase): 6 | def test_ultrasonic_sensor(self): 7 | get_input('Attach a UltrasonicSensor then continue') 8 | d = UltrasonicSensor() 9 | get_input('test dist_cm') 10 | print(d.dist_cm) 11 | print(d.mode) 12 | get_input('test dist_in') 13 | print(d.dist_in) 14 | print(d.mode) 15 | get_input('test listen') 16 | print(d.listen) 17 | print(d.mode) 18 | get_input('test si_cm') 19 | print(d.si_cm) 20 | print(d.mode) 21 | get_input('test si_in') 22 | print(d.si_in) 23 | print(d.mode) 24 | 25 | 26 | 27 | if __name__ == '__main__': 28 | unittest.main() 29 | -------------------------------------------------------------------------------- /test/test_ev3_lcd.py: -------------------------------------------------------------------------------- 1 | from ev3.ev3dev import Lcd 2 | # -*- coding: utf-8 -*- 3 | import unittest 4 | from util import get_input 5 | import time 6 | from PIL import Image,ImageDraw,ImageFont 7 | class TestLcd(unittest.TestCase): 8 | 9 | def test_lcd(self): 10 | get_input('Test lcd') 11 | d= Lcd() 12 | d.draw.ellipse((20, 20, 60, 60)) 13 | d.update() 14 | time.sleep(2) 15 | d.reset() 16 | font = ImageFont.load_default() 17 | d.draw.text((10, 10), "hello", font=font) 18 | try: 19 | font = ImageFont.truetype('/usr/share/fonts/truetype/arphic/uming.ttc',15) 20 | d.draw.text((20, 20), u'你好,世界', font=font) 21 | except IOError: 22 | print('No uming.ttc found. Skip the CJK test') 23 | d.update() 24 | 25 | 26 | 27 | 28 | if __name__ == '__main__': 29 | unittest.main() 30 | -------------------------------------------------------------------------------- /test/test_ev3_led.py: -------------------------------------------------------------------------------- 1 | from ev3.ev3dev import LED 2 | import unittest 3 | import time 4 | from util import get_input 5 | 6 | class TestLED(unittest.TestCase): 7 | 8 | def test_led(self): 9 | get_input('Test LED') 10 | led = LED() 11 | get_input('Test left red') 12 | led.left.color=LED.COLOR.RED 13 | get_input('Test left green') 14 | led.left.color=LED.COLOR.GREEN 15 | get_input('Test left amber') 16 | led.left.color=LED.COLOR.AMBER 17 | get_input('Test right blink') 18 | led.right.blink(color=LED.COLOR.GREEN,delay_on=1000, delay_off=2000 ) 19 | time.sleep(10) 20 | get_input('Test left and right on') 21 | led.left.on() 22 | led.right.on() 23 | time.sleep(5) 24 | get_input('Test left and right off') 25 | led.left.off() 26 | led.right.off() 27 | 28 | 29 | if __name__ == '__main__': 30 | unittest.main() 31 | -------------------------------------------------------------------------------- /test/test_ev3_key.py: -------------------------------------------------------------------------------- 1 | from ev3.ev3dev import Key 2 | import unittest 3 | from util import get_input 4 | 5 | 6 | class TestKey(unittest.TestCase): 7 | 8 | def test_key(self): 9 | d = Key() 10 | get_input('Test keyboard. Hold Up key') 11 | print(d.up) 12 | get_input('Test keyboard. Release Up key') 13 | print(d.up) 14 | get_input('Test keyboard. Hold Down key') 15 | print(d.down) 16 | get_input('Test keyboard. Release Down key') 17 | print(d.down) 18 | get_input('Test keyboard. Hold Left key') 19 | print(d.left) 20 | get_input('Test keyboard. Release Left key') 21 | print(d.left) 22 | get_input('Test keyboard. Hold Right key') 23 | print(d.right) 24 | get_input('Test keyboard. Release Right key') 25 | print(d.right) 26 | get_input('Test keyboard. Hold Backspace key') 27 | print(d.backspace) 28 | get_input('Test keyboard. Release Backspace key') 29 | print(d.backspace) 30 | get_input('Test keyboard. Hold Enter key') 31 | print(d.enter) 32 | get_input('Test keyboard. Release Enter key') 33 | print(d.enter) 34 | 35 | if __name__ == '__main__': 36 | unittest.main() 37 | -------------------------------------------------------------------------------- /smbus-build/build.sh: -------------------------------------------------------------------------------- 1 | if [ "$(whoami)" != "root" ] 2 | then 3 | echo "Sorry, you are not root!" 4 | exit 1 5 | fi 6 | export DEBIAN_FRONTEND=noninteractive 7 | export DEBCONF_NONINTERACTIVE_SEEN=true 8 | export LC_ALL=C LANGUAGE=C LANG=C 9 | 10 | apt-get install qemu-user-static multistrap 11 | export TARGET_ROOTFS_DIR=ev3-rootfs 12 | multistrap -f multistrap.conf -d ${TARGET_ROOTFS_DIR} 13 | cp /usr/bin/qemu-arm-static ${TARGET_ROOTFS_DIR}/usr/bin 14 | cp /etc/resolv.conf ev3-rootfs/etc/ 15 | mkdir -p ${TARGET_ROOTFS_DIR}/dev 16 | mount --bind /dev ${TARGET_ROOTFS_DIR}/dev 17 | chroot ${TARGET_ROOTFS_DIR} var/lib/dpkg/info/dash.preinst install > /dev/null 18 | chroot ${TARGET_ROOTFS_DIR} dpkg --configure -a 19 | cp -r py-smbus-python3 ${TARGET_ROOTFS_DIR}/home/py-smbus-python3 20 | chroot ${TARGET_ROOTFS_DIR} /home/py-smbus-python3/build-smbus.sh 21 | mkdir -p py-smbus-python3/build 22 | cp -r ${TARGET_ROOTFS_DIR}/home/py-smbus-python3/*.deb py-smbus-python3/build 23 | umount ${TARGET_ROOTFS_DIR}/dev 24 | ls ${TARGET_ROOTFS_DIR}/dev 25 | echo "''''''''''''''''''''''''''" 26 | echo "Warning!!" 27 | echo "/dev may still mount at ${TARGET_ROOTFS_DIR}/dev" 28 | echo "Do NOT remove ${TARGET_ROOTFS_DIR} until you check ${TARGET_ROOTFS_DIR}/dev" 29 | echo "''''''''''''''''''''''''''" 30 | 31 | -------------------------------------------------------------------------------- /test/test_ev3_motor.py: -------------------------------------------------------------------------------- 1 | from ev3.ev3dev import Motor 2 | import unittest 3 | import time 4 | from util import get_input 5 | 6 | class TestMotor(unittest.TestCase): 7 | get_input('Attach a motor on port A then continue') 8 | def __init__(self,*args, **kwargs): 9 | super(TestMotor, self).__init__(*args, **kwargs) 10 | self.d=Motor(port=Motor.PORT.A) 11 | 12 | def setUp(self): 13 | self.d.reset() 14 | 15 | def test_run(self): 16 | self.d.run_mode = 'forever' 17 | self.d.regulation_mode = True 18 | self.d.pulses_per_second_sp = 200 19 | self.d.start() 20 | time.sleep(5) 21 | self.d.stop() 22 | 23 | def test_run_forever(self): 24 | self.d.run_forever(50, regulation_mode=False) 25 | time.sleep(5) 26 | self.d.stop() 27 | self.d.run_forever(200, regulation_mode=True) 28 | time.sleep(5) 29 | self.d.stop() 30 | 31 | def test_run_time_limited(self): 32 | self.d.run_time_limited(time_sp=10000, speed_sp=80, regulation_mode=False, 33 | stop_mode=Motor.STOP_MODE.COAST, ramp_up_sp=1000, ramp_down_sp=1000) 34 | time.sleep(12) 35 | def test_run_position_limited(self): 36 | self.d.position=0 37 | self.d.run_position_limited(position_sp=360, speed_sp=800, 38 | stop_mode=Motor.STOP_MODE.BRAKE , ramp_up_sp=1000, ramp_down_sp=1000) 39 | time.sleep(5) 40 | 41 | def test_run_position_limited_relative (self): 42 | self.d.position_mode=Motor.POSITION_MODE.RELATIVE 43 | self.d.run_position_limited(position_sp=160, speed_sp=800, 44 | stop_mode=Motor.STOP_MODE.BRAKE , ramp_up_sp=1000, ramp_down_sp=1000) 45 | time.sleep(5) 46 | 47 | if __name__ == '__main__': 48 | unittest.main() 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | python-ev3 2 | ========== 3 | 4 | Program Lego Mindstorms EV3 using Python on ev3dev 5 | 6 | ## What you need 7 | 8 | 9 | You need a working [ev3dev](https://github.com/mindboards/ev3dev) on your ev3 and have a ssh session. Please reference the [ev3dev wiki](https://github.com/mindboards/ev3dev/wiki/Getting-started-v2) to burn such system. 10 | Current python-ev3 is developed on [ev3dev-jessie-2014-10-07](https://github.com/mindboards/ev3dev/releases/tag/ev3dev-jessie-2014-10-07) 11 | 12 | ## Both python 2.7 and python 3.4 are supported 13 | python-ev3 is tested on the ev3-dev in python2.7 and python3.4. 14 | 15 | 16 | ## Install the python-ev3 on EV3 17 | ### Python 2.7 18 | * ```apt-get update``` 19 | * ```apt-get install virtualenv virtualenvwrapper python-setuptools python-smbus python-pil``` 20 | * ```source /etc/bash_completion.d/virtualenvwrapper``` 21 | * ```mkvirtualenv ev3_py27 --python=/usr/bin/python2.7 --system-site-packages``` 22 | * ```workon ev3_py27``` 23 | * ```easy_install python-ev3``` 24 | * type ```deactive``` to exit 25 | 26 | ### Python 3.4 27 | * ```apt-get update``` 28 | * ```apt-get install virtualenv virtualenvwrapper python3-setuptools python3-smbus python3-pil``` 29 | * ```source /etc/bash_completion.d/virtualenvwrapper``` 30 | * ```mkvirtualenv ev3_py34 --python=/usr/bin/python3.4 --system-site-packages``` 31 | * ```workon ev3_py34``` 32 | * ```easy_install python-ev3``` 33 | * type ```deactive``` to exit 34 | 35 | ## Example 36 | ```python 37 | (ev3_py27)root@ev3dev:~# python 38 | Python 2.7.8 (default, Jul 4 2014, 16:59:40) 39 | [GCC 4.9.0] on linux2 40 | Type "help", "copyright", "credits" or "license" for more information. 41 | >>> from ev3.lego import MediumMotor 42 | >>> d = MediumMotor() 43 | >>> d.reset() 44 | >>> d.run_forever(50, regulation_mode=False) 45 | >>> d.stop() 46 | >>> exit() 47 | ``` 48 | To exit the virtual env, type ```deactivate``` 49 | 50 | ## More devices 51 | Plese see [```test```](https://github.com/topikachu/python-ev3/tree/master/test) to know how to use other devices. 52 | To create new sensor class please see [How to create a new sensor class ](https://github.com/topikachu/python-ev3/wiki/How-to-create-a-new-sensor-class) 53 | 54 | ## Python3 vs Python2 performance 55 | [@fuzzycow](https://github.com/fuzzycow) found there's some performance problem when using Python3. Please see https://github.com/topikachu/python-ev3/issues/22 56 | 57 | 58 | ## Reference 59 | * ev3 opensource project: https://github.com/mindboards/ev3sources 60 | * ev3-dev: https://github.com/mindboards/ev3dev 61 | -------------------------------------------------------------------------------- /ev3/mindsensors.py: -------------------------------------------------------------------------------- 1 | from .ev3dev import I2CS 2 | 3 | 4 | class MindSensorI2CS(I2CS): 5 | 6 | @property 7 | def version(self): 8 | return self.read_byte_array_as_string(0x00, 8) 9 | 10 | @property 11 | def vendor_id(self): 12 | return self.read_byte_array_as_string(0x08, 8) 13 | 14 | @property 15 | def device_id(self): 16 | return self.read_byte_array_as_string(0x10, 8) 17 | 18 | 19 | @I2CS.create_i2c_property( 20 | command=(0x41, {'read_only': False}), 21 | button_set_1 = 0x42, 22 | button_set_2= 0x43, 23 | x_left= 0x44, 24 | y_left= 0x45, 25 | x_right= 0x46, 26 | y_right= 0x47, 27 | up= 0x4A, 28 | right= 0x4B, 29 | down= 0x4C, 30 | left= 0x4D, 31 | l2= 0x4E, 32 | r2= 0x4F, 33 | l1= 0x50, 34 | r1= 0x51, 35 | triangle= 0x52, 36 | circle= 0x53, 37 | cross= 0x54, 38 | square= 0x55) 39 | class PSPNxV4(MindSensorI2CS): 40 | 41 | def __init__(self, port, addr=0x01): 42 | I2CS.__init__(self, port, addr) 43 | self.command = 0x49 44 | 45 | 46 | def absoluteIMU_property(cls): 47 | for prop in ['x_acc', 'y_acc', 'z_acc', 48 | 'x_raw_magnetic', 'y_raw_magnetic', 'z_raw_magnetic', 49 | 'x_gyro', 'y_gyro', 'z_gyro', 50 | 'compass']: 51 | def fget(self): 52 | return (getattr(self, prop + '_msb') << 8) + getattr(self, prop + '_lsb') 53 | setattr(cls, prop, property(fget)) 54 | return cls 55 | 56 | @I2CS.create_i2c_property( 57 | command=(0x41, {'read_only':False}), 58 | x_tilt= 0x42, 59 | y_tilt= 0x43, 60 | z_tilt= 0x44, 61 | x_acc_lsb= 0x45, 62 | x_acc_msb= 0x46, 63 | y_acc_lsb= 0x47, 64 | y_acc_msb= 0x48, 65 | z_acc_lsb= 0x49, 66 | z_acc_msb= 0x4A, 67 | compass_lsb= 0x4B, 68 | compass_msb= 0x4C, 69 | x_raw_magnetic_lsb= 0x4D, 70 | x_raw_magnetic_msb= 0x4E, 71 | y_raw_magnetic_lsb= 0x4F, 72 | y_raw_magnetic_msb= 0x50, 73 | z_raw_magnetic_lsb= 0x51, 74 | z_raw_magnetic_msb= 0x52, 75 | x_gyro_lsb= 0x53, 76 | x_gyro_msb= 0x54, 77 | y_gyro_lsb= 0x55, 78 | y_gyro_msb= 0x56, 79 | z_gyro_lsb= 0x57, 80 | z_gyro_msb= 0x58, 81 | gyro_filter= 0x5A) 82 | @absoluteIMU_property 83 | class AbsoluteIMU(MindSensorI2CS): 84 | # Is this too tricky to create property? 85 | 86 | def __init__(self, port, addr=0x11): 87 | I2CS.__init__(self, port, addr) 88 | 89 | def compass_cal_start(self): 90 | self.command = 0x43 91 | 92 | def compass_cal_end(self): 93 | self.command = 0x63 94 | 95 | def acc_2g(self): 96 | self.command = 0x31 97 | 98 | def acc_4g(self): 99 | self.command = 0x32 100 | 101 | def acc_8g(self): 102 | self.command = 0x33 103 | 104 | def acc_16g(self): 105 | self.command = 0x34 106 | -------------------------------------------------------------------------------- /ev3/lego.py: -------------------------------------------------------------------------------- 1 | from .ev3dev import Msensor, Motor 2 | 3 | 4 | class TouchSensor(Msensor): 5 | 6 | def __init__(self, port=-1): 7 | Msensor.__init__(self, port, type_id=16, name='lego-ev3-touch') 8 | 9 | @property 10 | def is_pushed(self): 11 | self.mode = 'TOUCH' 12 | return bool(self.value0) 13 | 14 | 15 | class ColorSensor(Msensor): 16 | colors = (None, 'black', 'blue', 'green', 17 | 'yellow', 'red', 'white', 'brown') 18 | 19 | def __init__(self, port=-1): 20 | Msensor.__init__(self, port, type_id=29, name='ev3-uart-29') 21 | 22 | @property 23 | def rgb(self): 24 | self.mode = 'RGB-RAW' 25 | return self.value0, self.value1, self.value2 26 | 27 | @property 28 | def color(self): 29 | self.mode = 'COL-COLOR' 30 | return self.value0 31 | 32 | @property 33 | def reflect(self): 34 | self.mode = 'COL-REFLECT' 35 | return self.value0 36 | 37 | @property 38 | def ambient(self): 39 | self.mode = 'COL-AMBIENT' 40 | return self.value0 41 | 42 | @property 43 | def ref_raw(self): 44 | self.mode = 'REF-RAW' 45 | return self.value0, self.value1 46 | 47 | 48 | class InfraredSensor(Msensor): 49 | 50 | def __init__(self, port=-1): 51 | Msensor.__init__(self, port, type_id=33, name='ev3-uart-33') 52 | 53 | @property 54 | def remote(self): 55 | self.mode = 'IR-REMOTE' 56 | return self.value0, self.value1, self.value2, self.value3 57 | 58 | @property 59 | def remote_bin(self): 60 | self.mode = 'IR-REM-A' 61 | return self.value0 62 | 63 | @property 64 | def prox(self): 65 | self.mode = 'IR-PROX' 66 | return self.value0 67 | 68 | @property 69 | def seek(self): 70 | self.mode = 'IR-SEEK' 71 | return [(self.value0, self.value1), 72 | (self.value2, self.value3), 73 | (self.value4, self.value5), 74 | (self.value6, self.value7)] 75 | 76 | 77 | class GyroSensor(Msensor): 78 | 79 | def __init__(self, port=-1): 80 | Msensor.__init__(self, port, type_id=32, name='ev3-uart-32') 81 | 82 | @property 83 | def ang(self): 84 | self.mode = 'GYRO-ANG' 85 | return self.value0 86 | 87 | @property 88 | def rate(self): 89 | self.mode = 'GYRO-RATE' 90 | return self.value0 91 | 92 | @property 93 | def ang_and_rate(self): 94 | self.mode = 'GYRO-G&A' 95 | return self.value0, self.value1 96 | 97 | 98 | class UltrasonicSensor(Msensor): 99 | 100 | def __init__(self, port=-1): 101 | Msensor.__init__(self, port, type_id=30, name='ev3-uart-30') 102 | 103 | @property 104 | def dist_cm(self): 105 | self.mode = 'US-DIST-CM' 106 | return self.value0 107 | 108 | @property 109 | def dist_in(self): 110 | self.mode = 'US-DIST-IN' 111 | return self.value0 112 | 113 | @property 114 | def listen(self): 115 | self.mode = 'US-LISTEN' 116 | return bool(self.value0) 117 | 118 | @property 119 | def si_cm(self): 120 | self.mode_force_flush('US-SI-CM') 121 | return self.value0 122 | 123 | @property 124 | def si_in(self): 125 | self.mode_force_flush('US-SI-IN') 126 | return self.value0 127 | 128 | 129 | class LargeMotor(Motor): 130 | 131 | def __init__(self, port=''): 132 | Motor.__init__(self, port, _type='tacho') 133 | 134 | 135 | class MediumMotor(Motor): 136 | 137 | def __init__(self, port=''): 138 | Motor.__init__(self, port, _type='minitacho') 139 | -------------------------------------------------------------------------------- /smbus-build/py-smbus-python3/python3.patch: -------------------------------------------------------------------------------- 1 | diff -cr i2c-tools-3.1.1.orig/debian/control i2c-tools-3.1.1/debian/control 2 | *** i2c-tools-3.1.1.orig/debian/control 2014-07-26 20:13:39.305699703 +0800 3 | --- i2c-tools-3.1.1/debian/control 2014-07-26 20:11:21.889018290 +0800 4 | *************** 5 | *** 1,11 **** 6 | Source: i2c-tools 7 | Section: utils 8 | Priority: extra 9 | ! Build-Depends: debhelper (>= 5), python-all-dev (>= 2.6.6-3~) 10 | Maintainer: Aurelien Jarno 11 | Standards-Version: 3.9.5 12 | Homepage: http://www.lm-sensors.org 13 | X-Python-Version: >= 2.2 14 | 15 | Package: i2c-tools 16 | Architecture: any 17 | --- 1,12 ---- 18 | Source: i2c-tools 19 | Section: utils 20 | Priority: extra 21 | ! Build-Depends: debhelper (>= 5), python-all-dev (>= 2.6.6-3~), python3-dev (>= 3.2) 22 | Maintainer: Aurelien Jarno 23 | Standards-Version: 3.9.5 24 | Homepage: http://www.lm-sensors.org 25 | X-Python-Version: >= 2.2 26 | + X-Python3-Version: >=3.2 27 | 28 | Package: i2c-tools 29 | Architecture: any 30 | *************** 31 | *** 39,41 **** 32 | --- 40,53 ---- 33 | This Python module allows SMBus access through the I2C /dev interface on 34 | Linux hosts. The host kernel must have I2C support, I2C device interface 35 | support, and a bus adapter driver. 36 | + 37 | + Package: python3-smbus 38 | + Architecture: any 39 | + Section: python 40 | + Depends: ${shlibs:Depends}, ${python3:Depends}, ${misc:Depends} 41 | + Provides: ${python3:Provides} 42 | + Recommends: i2c-tools 43 | + Description: Python3 bindings for Linux SMBus access through i2c-dev 44 | + This Python3 module allows SMBus access through the I2C /dev interface on 45 | + Linux hosts. The host kernel must have I2C support, I2C device interface 46 | + support, and a bus adapter driver. 47 | diff -cr i2c-tools-3.1.1.orig/debian/rules i2c-tools-3.1.1/debian/rules 48 | *** i2c-tools-3.1.1.orig/debian/rules 2014-07-26 20:13:39.305699703 +0800 49 | --- i2c-tools-3.1.1/debian/rules 2014-07-26 20:11:21.885018270 +0800 50 | *************** 51 | *** 22,28 **** 52 | export CFLAGS CPPFLAGS LDFLAGS 53 | 54 | PYVERS = $(shell pyversions -v -r debian/control) 55 | ! 56 | clean: 57 | dh_testdir 58 | dh_testroot 59 | --- 22,28 ---- 60 | export CFLAGS CPPFLAGS LDFLAGS 61 | 62 | PYVERS = $(shell pyversions -v -r debian/control) 63 | ! PY3VERS = $(shell py3versions -v -r debian/control) 64 | clean: 65 | dh_testdir 66 | dh_testroot 67 | *************** 68 | *** 37,43 **** 69 | # Build everything that goes into the Debian package. Use recursive make 70 | # invocations to build all of the interesting components. 71 | build: build-arch build-indep 72 | ! build-arch: build-stamp-binaries $(PYVERS:%=build-stamp-python-%) 73 | build-indep: build-stamp-binaries 74 | 75 | build-stamp-binaries: 76 | --- 37,43 ---- 77 | # Build everything that goes into the Debian package. Use recursive make 78 | # invocations to build all of the interesting components. 79 | build: build-arch build-indep 80 | ! build-arch: build-stamp-binaries $(PYVERS:%=build-stamp-python-%) $(PY3VERS:%=build-stamp-python3-%) 81 | build-indep: build-stamp-binaries 82 | 83 | build-stamp-binaries: 84 | *************** 85 | *** 51,57 **** 86 | CFLAGS="$(CFLAGS) -I../include" python$* setup.py build 87 | touch $@ 88 | 89 | ! install: install-stamp-binaries $(PYVERS:%=install-stamp-python-%) 90 | 91 | install-stamp-binaries: build-stamp-binaries 92 | dh_testdir 93 | --- 51,63 ---- 94 | CFLAGS="$(CFLAGS) -I../include" python$* setup.py build 95 | touch $@ 96 | 97 | ! build-stamp-python3-%: 98 | ! dh_testdir 99 | ! cd py-smbus && \ 100 | ! CFLAGS="$(CFLAGS) -I../include -DPYTHON_3" python$* setup.py build 101 | ! touch $@ 102 | ! 103 | ! install: install-stamp-binaries $(PYVERS:%=install-stamp-python-%) $(PY3VERS:%=install-stamp-python3-%) 104 | 105 | install-stamp-binaries: build-stamp-binaries 106 | dh_testdir 107 | *************** 108 | *** 75,81 **** 109 | --- 81,96 ---- 110 | CFLAGS="$(CFLAGS) -I../include" python$* setup.py install --install-layout=deb --root=$(CURDIR)/debian/python-smbus 111 | 112 | touch $@ 113 | + install-stamp-python3-%: build-stamp-python3-% 114 | + dh_testdir 115 | + dh_testroot 116 | + dh_installdirs 117 | 118 | + $(MAKE) install DESTDIR=$(CURDIR)/debian/tmp prefix=/usr 119 | + cd py-smbus && \ 120 | + CFLAGS="$(CFLAGS) -I../include -DPYTHON_3" python$* setup.py install --install-layout=deb --root=$(CURDIR)/debian/python3-smbus 121 | + 122 | + touch $@ 123 | 124 | # Build architecture-independent files here. 125 | binary-indep: build install 126 | *************** 127 | *** 108,113 **** 128 | --- 123,129 ---- 129 | dh_perl -a 130 | dh_makeshlibs -a 131 | dh_python2 -a 132 | + dh_python3 -a 133 | dh_installdeb -a 134 | dh_shlibdeps -a 135 | dh_gencontrol -a 136 | diff -cr i2c-tools-3.1.1.orig/py-smbus/smbusmodule.c i2c-tools-3.1.1/py-smbus/smbusmodule.c 137 | *** i2c-tools-3.1.1.orig/py-smbus/smbusmodule.c 2014-07-26 20:13:39.313699742 +0800 138 | --- i2c-tools-3.1.1/py-smbus/smbusmodule.c 2014-07-26 20:11:21.893018310 +0800 139 | *************** 140 | *** 33,38 **** 141 | --- 33,39 ---- 142 | #define I2C_SMBUS_I2C_BLOCK_DATA 8 143 | #endif 144 | 145 | + #ifndef PYTHON_3 146 | PyDoc_STRVAR(SMBus_module_doc, 147 | "This module defines an object type that allows SMBus transactions\n" 148 | "on hosts running the Linux kernel. The host kernel must have I2C\n" 149 | *************** 150 | *** 42,47 **** 151 | --- 43,49 ---- 152 | "\n" 153 | "Because the I2C device interface is opened R/W, users of this\n" 154 | "module usually must have root permissions.\n"); 155 | + #endif 156 | 157 | typedef struct { 158 | PyObject_HEAD 159 | *************** 160 | *** 92,98 **** 161 | --- 94,104 ---- 162 | PyObject *ref = SMBus_close(self); 163 | Py_XDECREF(ref); 164 | 165 | + #ifndef PYTHON_3 166 | self->ob_type->tp_free((PyObject *)self); 167 | + #else 168 | + Py_TYPE(self)->tp_free((PyObject*)self); 169 | + #endif 170 | } 171 | 172 | #define MAXPATH 16 173 | *************** 174 | *** 432,442 **** 175 | --- 438,456 ---- 176 | 177 | for (ii = 0; ii < len; ii++) { 178 | PyObject *val = PyList_GET_ITEM(list, ii); 179 | + #ifndef PYTHON_3 180 | if (!PyInt_Check(val)) { 181 | + #else 182 | + if (!PyLong_Check(val)) { 183 | + #endif 184 | PyErr_SetString(PyExc_TypeError, msg); 185 | return 0; /* fail */ 186 | } 187 | + #ifndef PYTHON_3 188 | data->block[ii+1] = (__u8)PyInt_AS_LONG(val); 189 | + #else 190 | + data->block[ii+1] = (__u8)PyLong_AS_LONG(val); 191 | + #endif 192 | } 193 | 194 | return 1; /* success */ 195 | *************** 196 | *** 635,642 **** 197 | --- 649,661 ---- 198 | }; 199 | 200 | static PyTypeObject SMBus_type = { 201 | + #ifndef PYTHON_3 202 | PyObject_HEAD_INIT(NULL) 203 | 0, /* ob_size */ 204 | + #else 205 | + PyVarObject_HEAD_INIT(NULL, 0) 206 | + #endif 207 | + 208 | "smbus.SMBus", /* tp_name */ 209 | sizeof(SMBus), /* tp_basicsize */ 210 | 0, /* tp_itemsize */ 211 | *************** 212 | *** 675,689 **** 213 | 0, /* tp_alloc */ 214 | SMBus_new, /* tp_new */ 215 | }; 216 | ! 217 | static PyMethodDef SMBus_module_methods[] = { 218 | {NULL} 219 | }; 220 | 221 | #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ 222 | #define PyMODINIT_FUNC void 223 | #endif 224 | PyMODINIT_FUNC 225 | initsmbus(void) 226 | { 227 | PyObject* m; 228 | --- 694,729 ---- 229 | 0, /* tp_alloc */ 230 | SMBus_new, /* tp_new */ 231 | }; 232 | ! #ifndef PYTHON_3 233 | static PyMethodDef SMBus_module_methods[] = { 234 | {NULL} 235 | + #else 236 | + static struct PyModuleDef SMBusModule = { 237 | + PyModuleDef_HEAD_INIT, 238 | + "SMBus", /* m_name */ 239 | + "This module defines an object type that allows SMBus transactions\n" 240 | + "on hosts running the Linux kernel. The host kernel must have I2C\n" 241 | + "support, I2C device interface support, and a bus adapter driver.\n" 242 | + "All of these can be either built-in to the kernel, or loaded from\n" 243 | + "modules.\n" 244 | + "\n" 245 | + "Because the I2C device interface is opened R/W, users of this\n" 246 | + "module usually must have root permissions.\n", /* m_doc */ 247 | + -1, /* m_size */ 248 | + NULL, /* m_methods */ 249 | + NULL, /* m_reload */ 250 | + NULL, /* m_traverse */ 251 | + NULL, /* m_clear */ 252 | + NULL, /* m_free */ 253 | + #endif 254 | + 255 | }; 256 | 257 | #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ 258 | #define PyMODINIT_FUNC void 259 | #endif 260 | PyMODINIT_FUNC 261 | + #ifndef PYTHON_3 262 | initsmbus(void) 263 | { 264 | PyObject* m; 265 | *************** 266 | *** 696,699 **** 267 | --- 736,759 ---- 268 | Py_INCREF(&SMBus_type); 269 | PyModule_AddObject(m, "SMBus", (PyObject *)&SMBus_type); 270 | } 271 | + #else 272 | + PyInit_smbus(void) 273 | + { 274 | + PyObject* m; 275 | + 276 | + if (PyType_Ready(&SMBus_type) < 0) 277 | + return NULL; 278 | + 279 | + m = PyModule_Create(&SMBusModule); 280 | + 281 | + if (m == NULL) 282 | + return NULL; 283 | + 284 | + Py_INCREF(&SMBus_type); 285 | + PyModule_AddObject(m, "SMBus", (PyObject *)&SMBus_type); 286 | + 287 | + return m; 288 | + } 289 | + #endif 290 | + 291 | 292 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, and 10 | distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 13 | owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities 16 | that control, are controlled by, or are under common control with that entity. 17 | For the purposes of this definition, "control" means (i) the power, direct or 18 | indirect, to cause the direction or management of such entity, whether by 19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 20 | outstanding shares, or (iii) beneficial ownership of such entity. 21 | 22 | "You" (or "Your") shall mean an individual or Legal Entity exercising 23 | permissions granted by this License. 24 | 25 | "Source" form shall mean the preferred form for making modifications, including 26 | but not limited to software source code, documentation source, and configuration 27 | files. 28 | 29 | "Object" form shall mean any form resulting from mechanical transformation or 30 | translation of a Source form, including but not limited to compiled object code, 31 | generated documentation, and conversions to other media types. 32 | 33 | "Work" shall mean the work of authorship, whether in Source or Object form, made 34 | available under the License, as indicated by a copyright notice that is included 35 | in or attached to the work (an example is provided in the Appendix below). 36 | 37 | "Derivative Works" shall mean any work, whether in Source or Object form, that 38 | is based on (or derived from) the Work and for which the editorial revisions, 39 | annotations, elaborations, or other modifications represent, as a whole, an 40 | original work of authorship. For the purposes of this License, Derivative Works 41 | shall not include works that remain separable from, or merely link (or bind by 42 | name) to the interfaces of, the Work and Derivative Works thereof. 43 | 44 | "Contribution" shall mean any work of authorship, including the original version 45 | of the Work and any modifications or additions to that Work or Derivative Works 46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 47 | by the copyright owner or by an individual or Legal Entity authorized to submit 48 | on behalf of the copyright owner. For the purposes of this definition, 49 | "submitted" means any form of electronic, verbal, or written communication sent 50 | to the Licensor or its representatives, including but not limited to 51 | communication on electronic mailing lists, source code control systems, and 52 | issue tracking systems that are managed by, or on behalf of, the Licensor for 53 | the purpose of discussing and improving the Work, but excluding communication 54 | that is conspicuously marked or otherwise designated in writing by the copyright 55 | owner as "Not a Contribution." 56 | 57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 58 | of whom a Contribution has been received by Licensor and subsequently 59 | incorporated within the Work. 60 | 61 | 2. Grant of Copyright License. 62 | 63 | Subject to the terms and conditions of this License, each Contributor hereby 64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 65 | irrevocable copyright license to reproduce, prepare Derivative Works of, 66 | publicly display, publicly perform, sublicense, and distribute the Work and such 67 | Derivative Works in Source or Object form. 68 | 69 | 3. Grant of Patent License. 70 | 71 | Subject to the terms and conditions of this License, each Contributor hereby 72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 73 | irrevocable (except as stated in this section) patent license to make, have 74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 75 | such license applies only to those patent claims licensable by such Contributor 76 | that are necessarily infringed by their Contribution(s) alone or by combination 77 | of their Contribution(s) with the Work to which such Contribution(s) was 78 | submitted. If You institute patent litigation against any entity (including a 79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 80 | Contribution incorporated within the Work constitutes direct or contributory 81 | patent infringement, then any patent licenses granted to You under this License 82 | for that Work shall terminate as of the date such litigation is filed. 83 | 84 | 4. Redistribution. 85 | 86 | You may reproduce and distribute copies of the Work or Derivative Works thereof 87 | in any medium, with or without modifications, and in Source or Object form, 88 | provided that You meet the following conditions: 89 | 90 | You must give any other recipients of the Work or Derivative Works a copy of 91 | this License; and 92 | You must cause any modified files to carry prominent notices stating that You 93 | changed the files; and 94 | You must retain, in the Source form of any Derivative Works that You distribute, 95 | all copyright, patent, trademark, and attribution notices from the Source form 96 | of the Work, excluding those notices that do not pertain to any part of the 97 | Derivative Works; and 98 | If the Work includes a "NOTICE" text file as part of its distribution, then any 99 | Derivative Works that You distribute must include a readable copy of the 100 | attribution notices contained within such NOTICE file, excluding those notices 101 | that do not pertain to any part of the Derivative Works, in at least one of the 102 | following places: within a NOTICE text file distributed as part of the 103 | Derivative Works; within the Source form or documentation, if provided along 104 | with the Derivative Works; or, within a display generated by the Derivative 105 | Works, if and wherever such third-party notices normally appear. The contents of 106 | the NOTICE file are for informational purposes only and do not modify the 107 | License. You may add Your own attribution notices within Derivative Works that 108 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 109 | provided that such additional attribution notices cannot be construed as 110 | modifying the License. 111 | You may add Your own copyright statement to Your modifications and may provide 112 | additional or different license terms and conditions for use, reproduction, or 113 | distribution of Your modifications, or for any such Derivative Works as a whole, 114 | provided Your use, reproduction, and distribution of the Work otherwise complies 115 | with the conditions stated in this License. 116 | 117 | 5. Submission of Contributions. 118 | 119 | Unless You explicitly state otherwise, any Contribution intentionally submitted 120 | for inclusion in the Work by You to the Licensor shall be under the terms and 121 | conditions of this License, without any additional terms or conditions. 122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of 123 | any separate license agreement you may have executed with Licensor regarding 124 | such Contributions. 125 | 126 | 6. Trademarks. 127 | 128 | This License does not grant permission to use the trade names, trademarks, 129 | service marks, or product names of the Licensor, except as required for 130 | reasonable and customary use in describing the origin of the Work and 131 | reproducing the content of the NOTICE file. 132 | 133 | 7. Disclaimer of Warranty. 134 | 135 | Unless required by applicable law or agreed to in writing, Licensor provides the 136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, 137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 138 | including, without limitation, any warranties or conditions of TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are 140 | solely responsible for determining the appropriateness of using or 141 | redistributing the Work and assume any risks associated with Your exercise of 142 | permissions under this License. 143 | 144 | 8. Limitation of Liability. 145 | 146 | In no event and under no legal theory, whether in tort (including negligence), 147 | contract, or otherwise, unless required by applicable law (such as deliberate 148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 149 | liable to You for damages, including any direct, indirect, special, incidental, 150 | or consequential damages of any character arising as a result of this License or 151 | out of the use or inability to use the Work (including but not limited to 152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or 153 | any and all other commercial damages or losses), even if such Contributor has 154 | been advised of the possibility of such damages. 155 | 156 | 9. Accepting Warranty or Additional Liability. 157 | 158 | While redistributing the Work or Derivative Works thereof, You may choose to 159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 160 | other liability obligations and/or rights consistent with this License. However, 161 | in accepting such obligations, You may act only on Your own behalf and on Your 162 | sole responsibility, not on behalf of any other Contributor, and only if You 163 | agree to indemnify, defend, and hold each Contributor harmless for any liability 164 | incurred by, or claims asserted against, such Contributor by reason of your 165 | accepting any such warranty or additional liability. 166 | 167 | END OF TERMS AND CONDITIONS 168 | 169 | APPENDIX: How to apply the Apache License to your work 170 | 171 | To apply the Apache License to your work, attach the following boilerplate 172 | notice, with the fields enclosed by brackets "[]" replaced with your own 173 | identifying information. (Don't include the brackets!) The text should be 174 | enclosed in the appropriate comment syntax for the file format. We also 175 | recommend that a file or class name and description of purpose be included on 176 | the same "printed page" as the copyright notice for easier identification within 177 | third-party archives. 178 | 179 | Copyright [yyyy] [name of copyright owner] 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. 192 | -------------------------------------------------------------------------------- /ev3/ev3dev.py: -------------------------------------------------------------------------------- 1 | import os 2 | import glob 3 | import sys 4 | import warnings 5 | import logging 6 | import re 7 | import subprocess 8 | 9 | logger = logging.getLogger(__name__) 10 | 11 | class NoSuchSensorError(Exception): 12 | 13 | def __init__(self, port, type_id=None, name=None): 14 | self.port = port 15 | self.type_id = type_id 16 | self.name =name 17 | 18 | def __str__(self): 19 | return "No such sensor port=%d type_id=%d name=%s" % (self.port, self.type_id, self.name) 20 | 21 | 22 | class NoSuchMotorError(Exception): 23 | 24 | def __init__(self, port, _type): 25 | self.port = port 26 | self._type = _type 27 | 28 | def __str__(self): 29 | return "No such sensor port=%s type=%s" % (self.port, self._type) 30 | 31 | 32 | class NoSuchLibraryError(Exception): 33 | 34 | def __init__(self, lib=""): 35 | self.lib = lib 36 | 37 | def __str__(self): 38 | return "No such library %s" % self.lib 39 | 40 | 41 | class Ev3StringType(object): 42 | 43 | @staticmethod 44 | def post_read(value): 45 | return value 46 | 47 | @staticmethod 48 | def pre_write(value): 49 | return value 50 | 51 | 52 | class Ev3IntType(object): 53 | 54 | @staticmethod 55 | def post_read(value): 56 | return int(value) 57 | 58 | @staticmethod 59 | def pre_write(value): 60 | return str(value) 61 | 62 | 63 | class Ev3BoolType(object): 64 | 65 | @staticmethod 66 | def post_read(value): 67 | return bool(value) 68 | 69 | @staticmethod 70 | def pre_write(value): 71 | return '1' if value else '0' 72 | 73 | 74 | class Ev3OnOffType(object): 75 | 76 | @staticmethod 77 | def post_read(value): 78 | return True if value == 'on' else False 79 | 80 | @staticmethod 81 | def pre_write(value): 82 | if (value == 'on' or value == 'off'): 83 | return value 84 | else: 85 | return 'on' if bool(value) else 'off' 86 | 87 | 88 | class create_ev3_property(object): 89 | 90 | def __init__(self, **kwargs): 91 | self.kwargs = kwargs 92 | 93 | def __call__(self, cls): 94 | for name, args in self.kwargs.items(): 95 | def ev3_property(name, read_only=False, property_type=Ev3StringType): 96 | def fget(self): 97 | return property_type.post_read(self.read_value(name)) 98 | 99 | def fset(self, value): 100 | self.write_value( 101 | name, property_type.pre_write(value)) 102 | return property(fget, None if read_only else fset) 103 | 104 | setattr(cls, name, ev3_property(name, **args)) 105 | 106 | return cls 107 | 108 | def get_battery_percentage(): 109 | """ 110 | Return an int() of the percentage of battery life remaining 111 | """ 112 | voltage_max = None 113 | voltage_min = None 114 | voltage_now = None 115 | 116 | with open('/sys/devices/platform/legoev3-battery/power_supply/legoev3-battery/uevent', 'r') as fh: 117 | for line in fh: 118 | 119 | if not voltage_max: 120 | re_voltage_max = re.search('POWER_SUPPLY_VOLTAGE_MAX_DESIGN=(\d+)', line) 121 | 122 | if re_voltage_max: 123 | voltage_max = int(re_voltage_max.group(1)) 124 | continue 125 | 126 | if not voltage_min: 127 | re_voltage_min = re.search('POWER_SUPPLY_VOLTAGE_MIN_DESIGN=(\d+)', line) 128 | 129 | if re_voltage_min: 130 | voltage_min = int(re_voltage_min.group(1)) 131 | continue 132 | 133 | if not voltage_now: 134 | re_voltage_now = re.search('POWER_SUPPLY_VOLTAGE_NOW=(\d+)', line) 135 | 136 | if re_voltage_now: 137 | voltage_now = int(re_voltage_now.group(1)) 138 | 139 | if re_voltage_max and re_voltage_min and re_voltage_now: 140 | break 141 | 142 | if voltage_max and voltage_min and voltage_now: 143 | 144 | # This happens with the EV3 rechargeable battery if it is fully charge 145 | if voltage_now >= voltage_max: 146 | return 100 147 | 148 | # Haven't seen this scenario but it can't hurt to check for it 149 | elif voltage_now <= voltage_min: 150 | return 0 151 | 152 | # voltage_now is between the min and max 153 | else: 154 | voltage_max -= voltage_min 155 | voltage_now -= voltage_min 156 | return int(voltage_now/float(voltage_max) * 100) 157 | else: 158 | logger.error('voltage_max %s, voltage_min %s, voltage_now %s' %\ 159 | (voltage_max, voltage_min, voltage_now)) 160 | return 0 161 | 162 | class Ev3Dev(object): 163 | 164 | def __init__(self): 165 | self.sys_path = "" 166 | 167 | def read_value(self, name): 168 | attr_file = os.path.join(self.sys_path, name) 169 | if os.path.isfile(attr_file): 170 | with open(attr_file) as f: 171 | value = f.read().strip() 172 | return value 173 | else: 174 | return None 175 | 176 | def write_value(self, name, value): 177 | attr_file = os.path.join(self.sys_path, name) 178 | if os.path.isfile(attr_file): 179 | with open(attr_file, 'w') as f: 180 | f.write(str(value)) 181 | else: 182 | return 183 | 184 | 185 | 186 | @create_ev3_property( 187 | bin_data={'read_only': True}, 188 | bin_data_format={'read_only': True}, 189 | dp={'read_only': True}, 190 | #mode={ 'read_only': False}, 191 | modes={'read_only': True}, 192 | name={'read_only': True}, 193 | port_name={'read_only': True}, 194 | type_id={'read_only': True, 'property_type': Ev3IntType}, 195 | uevent={'read_only': True}, 196 | units={'read_only': True}, 197 | value0={'read_only': True, 'property_type': Ev3IntType}, 198 | value1={'read_only': True, 'property_type': Ev3IntType}, 199 | value2={'read_only': True, 'property_type': Ev3IntType}, 200 | value3={'read_only': True, 'property_type': Ev3IntType}, 201 | value4={'read_only': True, 'property_type': Ev3IntType}, 202 | value5={'read_only': True, 'property_type': Ev3IntType}, 203 | value6={'read_only': True, 'property_type': Ev3IntType}, 204 | value7={'read_only': True, 'property_type': Ev3IntType} 205 | ) 206 | class Msensor(Ev3Dev): 207 | 208 | def __init__(self, port=-1, type_id=-1, name=None): 209 | Ev3Dev.__init__(self) 210 | type_id = int(type_id) 211 | sensor_existing = False 212 | if (port > 0): 213 | self.port = port 214 | for p in glob.glob('/sys/class/msensor/sensor*/port_name'): 215 | with open(p) as f: 216 | value = f.read().strip() 217 | if (value == 'in' + str(port)): 218 | self.sys_path = os.path.dirname(p) 219 | sensor_existing = True 220 | break 221 | if (len(glob.glob('/sys/class/msensor/sensor*/type_id')) >0 and type_id > 0 and port == -1): 222 | for p in glob.glob('/sys/class/msensor/sensor*/type_id'): 223 | with open(p) as f: 224 | value = int(f.read().strip()) 225 | if (value == type_id): 226 | self.sys_path = os.path.dirname(p) 227 | self.port = int(self.port_name[2:]) 228 | sensor_existing = True 229 | break 230 | if (len(glob.glob('/sys/class/msensor/sensor*/name')) >0 and name !=None and port == -1): 231 | for p in glob.glob('/sys/class/msensor/sensor*/name'): 232 | with open(p) as f: 233 | value = f.read().strip() 234 | if (name in value): 235 | self.sys_path = os.path.dirname(p) 236 | self.port = int(self.port_name[2:]) 237 | sensor_existing = True 238 | break 239 | if (not sensor_existing): 240 | raise NoSuchSensorError(port, type_id, name) 241 | self._mode = self.read_value('mode') 242 | 243 | @property 244 | def mode(self): 245 | return self._mode 246 | 247 | @mode.setter 248 | def mode(self, value): 249 | if (self._mode != value): 250 | self._mode = value 251 | self.write_value('mode', value) 252 | 253 | def mode_force_flush(self, value): 254 | self._mode = value 255 | self.write_value('mode', value) 256 | 257 | 258 | class Enum(object): 259 | 260 | def __init__(self, *args, **kwargs): 261 | for arg in args: 262 | kwargs[arg] = arg 263 | self.enum_dict = kwargs 264 | 265 | def __getattr__(self, name): 266 | if (name in self.enum_dict.keys()): 267 | return self.enum_dict[name] 268 | else: 269 | raise NameError("no such item %s" % name) 270 | 271 | 272 | @create_ev3_property( 273 | duty_cycle={'read_only': True, 'property_type': Ev3IntType}, 274 | duty_cycle_sp={'read_only': False, 'property_type': Ev3IntType}, 275 | estop={'read_only': False, 'property_type': Ev3IntType}, 276 | polarity_mode={'read_only': False}, 277 | port_name={'read_only': True}, 278 | position={'read_only': False, 'property_type': Ev3IntType}, 279 | position_mode={'read_only': False}, 280 | position_sp={'read_only': False, 'property_type': Ev3IntType}, 281 | pulses_per_second={'read_only': True, 'property_type': Ev3IntType}, 282 | pulses_per_second_sp={'read_only': False, 'property_type': Ev3IntType}, 283 | ramp_down_sp={'read_only': False, 'property_type': Ev3IntType}, 284 | ramp_up_sp={'read_only': False, 'property_type': Ev3IntType}, 285 | regulation_mode={'read_only': False, 'property_type': Ev3OnOffType}, 286 | #reset={ 'read_only': False}, 287 | run={'read_only': False, 'property_type': Ev3BoolType}, 288 | run_mode={'read_only': False}, 289 | speed_regulation_D={'read_only': False, 'property_type': Ev3IntType}, 290 | speed_regulation_I={'read_only': False, 'property_type': Ev3IntType}, 291 | speed_regulation_K={'read_only': False, 'property_type': Ev3IntType}, 292 | speed_regulation_P={'read_only': False, 'property_type': Ev3IntType}, 293 | state={'read_only': True}, 294 | stop_mode={'read_only': False}, 295 | stop_modes={'read_only': False}, 296 | time_sp={'read_only': False, 'property_type': Ev3IntType}, 297 | type={'read_only': False}, 298 | uevent={'read_only': True} 299 | ) 300 | class Motor(Ev3Dev): 301 | STOP_MODE = Enum(COAST='coast', BRAKE='brake', HOLD='hold') 302 | POSITION_MODE = Enum(RELATIVE='relative', ABSOLUTE='absolute') 303 | PORT = Enum('A', 'B', 'C', 'D') 304 | 305 | def __init__(self, port='', _type=''): 306 | Ev3Dev.__init__(self) 307 | motor_existing = False 308 | searchpath='/sys/class/tacho-motor/motor*/' 309 | if (len(glob.glob(searchpath + "*"))==0): 310 | searchpath='/sys/class/tacho-motor/tacho-motor*/' 311 | if (port != ''): 312 | self.port = port 313 | for p in glob.glob(searchpath + 'port_name'): 314 | with open(p) as f: 315 | value = f.read().strip() 316 | if (value.lower() == ('out' + port).lower()): 317 | self.sys_path = os.path.dirname(p) 318 | motor_existing = True 319 | break 320 | if (_type != '' and port == ''): 321 | for p in glob.glob(searchpath + 'type'): 322 | with open(p) as f: 323 | value = f.read().strip() 324 | if (value.lower() == _type.lower()): 325 | self.sys_path = os.path.dirname(p) 326 | self.port = self.port_name[3:] 327 | motor_existing = True 328 | break 329 | if (not motor_existing): 330 | raise NoSuchMotorError(port, _type) 331 | 332 | def stop(self): 333 | self.run = False 334 | 335 | def start(self): 336 | self.run = True 337 | 338 | def reset(self): 339 | self.write_value('reset', 1) 340 | 341 | def run_forever(self, speed_sp, **kwargs): 342 | self.run_mode = 'forever' 343 | for k in kwargs: 344 | v = kwargs[k] 345 | if (v != None): 346 | setattr(self, k, v) 347 | regulation_mode = self.regulation_mode 348 | if (regulation_mode): 349 | self.pulses_per_second_sp = speed_sp 350 | else: 351 | self.duty_cycle_sp = speed_sp 352 | self.start() 353 | 354 | def run_time_limited(self, time_sp, speed_sp, **kwargs): 355 | self.run_mode = 'time' 356 | for k in kwargs: 357 | v = kwargs[k] 358 | if (v != None): 359 | setattr(self, k, v) 360 | regulation_mode = self.regulation_mode 361 | if (regulation_mode): 362 | self.pulses_per_second_sp = speed_sp 363 | else: 364 | self.duty_cycle_sp = speed_sp 365 | self.time_sp = time_sp 366 | self.start() 367 | 368 | def run_position_limited(self, position_sp, speed_sp, **kwargs): 369 | self.run_mode = 'position' 370 | kwargs['regulation_mode'] = True 371 | for k in kwargs: 372 | v = kwargs[k] 373 | if (v != None): 374 | setattr(self, k, v) 375 | self.pulses_per_second_sp = speed_sp 376 | self.position_sp = position_sp 377 | self.start() 378 | 379 | 380 | def I2CSMBusProxy(cls): 381 | try: 382 | from smbus import SMBus 383 | smbus_proxied_methods = [ 384 | m for m in dir(SMBus) if (m.startswith('read') or m.startswith('write'))] 385 | for m in smbus_proxied_methods: 386 | def create_proxied_smb_method(method): 387 | def proxied_smb_method(self, *args, **kwargs): 388 | return getattr(self.b, method)(self.addr, *args, **kwargs) 389 | return proxied_smb_method 390 | setattr(cls, m, create_proxied_smb_method(m)) 391 | return cls 392 | except ImportError: 393 | warnings.warn('python-smbus binding not found!') 394 | return cls 395 | 396 | 397 | @I2CSMBusProxy 398 | class I2CS(object): 399 | 400 | def __init__(self, port, addr): 401 | self.port = port 402 | self.i2c_port = port + 2 403 | self.sys_path = '/dev/i2c-%s' % self.i2c_port 404 | if (not os.path.exists(self.sys_path)): 405 | raise NoSuchSensorError(port) 406 | try: 407 | from smbus import SMBus 408 | self.b = SMBus(self.i2c_port) 409 | self.addr = addr 410 | except ImportError: 411 | raise NoSuchLibraryError('smbus') 412 | 413 | def read_byte_array(self, reg, _len): 414 | return [self.read_byte_data(reg + r) for r in range(_len)] 415 | 416 | def read_byte_array_as_string(self, reg, _len): 417 | return ''.join(chr(r) for r in self.read_byte_array(reg, _len)) 418 | 419 | class create_i2c_property(object): 420 | 421 | def __init__(self, **kwargs): 422 | self.kwargs = kwargs 423 | 424 | def __call__(self, cls): 425 | for name, reg_address_and_read_only in self.kwargs.items(): 426 | def i2c_property(reg, read_only=True): 427 | def fget(self): 428 | return self.read_byte_data(reg) 429 | 430 | def fset(self, value): 431 | return self.write_byte_data(reg, value) 432 | return property(fget, None if read_only else fset) 433 | if (type(reg_address_and_read_only) == int): 434 | prop = i2c_property(reg_address_and_read_only) 435 | else: 436 | prop = i2c_property( 437 | reg_address_and_read_only[0], **reg_address_and_read_only[1]) 438 | setattr(cls, name, prop) 439 | return cls 440 | 441 | 442 | @create_ev3_property( 443 | brightness={'read_only': False, 'property_type': Ev3IntType}, 444 | trigger={'read_only': False}, 445 | delay_on={'read_only': False, 'property_type': Ev3IntType}, 446 | delay_off={'read_only': False, 'property_type': Ev3IntType} 447 | ) 448 | class LEDLight(Ev3Dev): 449 | 450 | def __init__(self, light_path): 451 | super(Ev3Dev, self).__init__() 452 | self.sys_path = '/sys/class/leds/' + light_path 453 | 454 | 455 | class LEDSide (object): 456 | 457 | def __init__(self, left_or_right): 458 | self.green = LEDLight('ev3:green:%s' % left_or_right) 459 | self.red = LEDLight('ev3:red:%s' % left_or_right) 460 | self._color = 0 461 | 462 | @property 463 | def color(self): 464 | return self._color 465 | 466 | @color.setter 467 | def color(self, value): 468 | self.red.brightness = value & 0x01 469 | self.green.brightness = (value >> 1) & 0x01 470 | self._color = value 471 | 472 | def get_operation_lights(self): 473 | lights = [] 474 | if (self._color & 0x01): 475 | lights.append(self.red) 476 | if ((self._color >> 1) & 0x01): 477 | lights.append(self.green) 478 | return lights 479 | 480 | def blink(self, color=0, **kwargs): 481 | if (color != 0): 482 | self.color = color 483 | lights = self.get_operation_lights() 484 | for light in lights: 485 | light.trigger = 'timer' 486 | for p, v in kwargs.items(): 487 | setattr(light, p, v) 488 | 489 | def on(self): 490 | lights = self.get_operation_lights() 491 | for light in lights: 492 | light.trigger = 'none' 493 | light.brightness = 1 494 | 495 | def off(self): 496 | lights = self.get_operation_lights() 497 | for light in lights: 498 | light.trigger = 'none' 499 | light.brightness = 0 500 | 501 | 502 | class LED(object): 503 | 504 | class COLOR: 505 | RED = 1 506 | GREEN = 2 507 | AMBER = 3 508 | 509 | left = LEDSide('left') 510 | right = LEDSide('right') 511 | 512 | 513 | @create_ev3_property( 514 | tone={'read_only': False}, 515 | mode={'read_only': True}, 516 | volume={'read_only': False, 'property_type': Ev3IntType} 517 | ) 518 | class Tone(Ev3Dev): 519 | 520 | def __init__(self): 521 | super(Ev3Dev, self).__init__() 522 | self.sys_path = '/sys/devices/platform/snd-legoev3' 523 | 524 | def play(self, frequency, milliseconds=1000): 525 | self.tone = '%d %d' % (frequency, milliseconds) 526 | 527 | def stop(self): 528 | self.tone = '0' 529 | 530 | import os 531 | 532 | 533 | class Lcd(object): 534 | 535 | def __init__(self): 536 | try: 537 | from PIL import Image, ImageDraw 538 | 539 | SCREEN_WIDTH = 178 540 | SCREEN_HEIGHT = 128 541 | HW_MEM_WIDTH = int((SCREEN_WIDTH + 31) / 32) * 4 542 | SCREEN_MEM_WIDTH = int((SCREEN_WIDTH + 7) / 8) 543 | LCD_BUFFER_LENGTH = SCREEN_MEM_WIDTH * SCREEN_HEIGHT 544 | LCD_HW_BUFFER_LENGTH = HW_MEM_WIDTH * SCREEN_HEIGHT 545 | self._buffer = Image.new( 546 | "1", (HW_MEM_WIDTH * 8, SCREEN_HEIGHT), "white") 547 | self._draw = ImageDraw.Draw(self._buffer) 548 | except ImportError: 549 | raise NoSuchLibraryError('PIL') 550 | 551 | def update(self): 552 | f = os.open('/dev/fb0', os.O_RDWR) 553 | os.write(f, self._buffer.tobytes("raw", "1;IR")) 554 | os.close(f) 555 | 556 | @property 557 | def buffer(self): 558 | return self._buffer 559 | 560 | @property 561 | def draw(self): 562 | return self._draw 563 | 564 | def reset(self): 565 | self._draw.rectangle( 566 | (0, 0) + self._buffer.size, outline='white', fill='white') 567 | 568 | 569 | class attach_ev3_keys(object): 570 | 571 | def __init__(self, **kwargs): 572 | self.kwargs = kwargs 573 | 574 | def __call__(self, cls): 575 | key_const = {} 576 | for key_name, key_code in self.kwargs.items(): 577 | def attach_key(key_name, key_code): 578 | def fget(self): 579 | buf = self.polling() 580 | return self.test_bit(key_code, buf) 581 | return property(fget) 582 | 583 | setattr(cls, key_name, attach_key(key_name, key_code)) 584 | key_const[key_name.upper()] = key_code 585 | setattr(cls, 'CODE', Enum(**key_const)) 586 | return cls 587 | 588 | 589 | import array 590 | import fcntl 591 | 592 | 593 | @attach_ev3_keys( 594 | up=103, 595 | down=108, 596 | left=105, 597 | right=106, 598 | enter=28, 599 | backspace=14 600 | ) 601 | class Key(object): 602 | 603 | def __init__(self): 604 | pass 605 | 606 | def EVIOCGKEY(self, length): 607 | return 2 << (14 + 8 + 8) | length << (8 + 8) | ord('E') << 8 | 0x18 608 | 609 | def test_bit(self, bit, bytes): 610 | # bit in bytes is 1 when released and 0 when pressed 611 | return not bool(bytes[int(bit / 8)] & 1 << bit % 8) 612 | 613 | def polling(self): 614 | KEY_MAX = 0x2ff 615 | BUF_LEN = int((KEY_MAX + 7) / 8) 616 | buf = array.array('B', [0] * BUF_LEN) 617 | with open('/dev/input/by-path/platform-gpio-keys.0-event', 'r') as fd: 618 | ret = fcntl.ioctl(fd, self.EVIOCGKEY(len(buf)), buf) 619 | if (ret < 0): 620 | return None 621 | else: 622 | return buf 623 | --------------------------------------------------------------------------------