├── .gitignore ├── AUTHORS ├── LICENSE ├── README.md ├── platform └── brickpi3-motor-and-sensor.py └── robots ├── BALANC3R ├── README.md ├── drive.py └── remote.py ├── DINOR3X ├── README.md ├── T-rex roar.wav └── dinor3x.py ├── EDUCATOR ├── README.md ├── color.py ├── square-gyro.py ├── square.py ├── touch.py └── ultrasonic.py ├── EL3CTRIC_GUITAR ├── README.md └── el3ctric_guitar.py ├── EV3D4 ├── EV3D4RemoteControl.py ├── EV3D4WebControl.py ├── README.md ├── desktop.html ├── favicon.ico ├── include │ ├── ArrowClockwise.png │ ├── ArrowDown.png │ ├── ArrowLeft.png │ ├── ArrowRight.png │ ├── ArrowUp.png │ ├── desktop.png │ ├── gear.png │ ├── jquery.ui.touch-punch.min.js │ ├── mobile.png │ ├── tank-desktop.js │ ├── tank-mobile.js │ ├── tank.css │ └── tank.png ├── index.html └── mobile.html ├── EV3RSTORM ├── README.md └── ev3rstorm.py ├── EXPLOR3R ├── README.rst ├── auto-drive.py └── remote-control.py ├── GRIPP3R └── GRIPP3R.py ├── MINDCUB3R ├── .gitignore ├── README.md ├── __init__.py ├── calibrate_white.py └── mindcuber.py ├── R3PTAR ├── README.md ├── r3ptar.py ├── rattle-snake.wav └── snake-hiss.wav ├── SPIK3R ├── Blip 3.wav ├── README.md └── spik3r.py ├── TRACK3R ├── TRACK3R.py ├── TRACK3RWithBallShooter ├── TRACK3RWithClaw └── TRACK3RWithSpinner └── misc ├── console_menu.py ├── leds.py ├── snd └── r2d2.wav └── sound.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.pyc 3 | *.swp 4 | 5 | # Visual Studio Code files 6 | .vscode/ 7 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Bert 2 | Daniel Walton 3 | Denis Demidov 4 | Greg Cowell 5 | Kaelin Laundry 6 | Peter Bittner 7 | WallK 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 ev3dev.org 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ev3dev demo programs 2 | 3 | This folder contains several demo programs that you can use to help you in 4 | developing your own code. Brief descriptions of each demo are provided below; 5 | you can access the full source code and some more detailed information on each 6 | by opening the respective folders above. 7 | 8 | To install these on your EV3, use git to clone the ev3dev-lang-python-demo repository 9 | from github. Your EV3 will need Internet connectivty in order to clone the 10 | repository from github. 11 | ``` 12 | $ sudo apt-get install git 13 | $ git clone https://github.com/ev3dev/ev3dev-lang-python-demo.git 14 | ``` 15 | 16 | ## Running A Program 17 | There are two ways to run a program. You can run a program from the command line 18 | or from the brickman interface. 19 | 20 | Note that for both running from the command line and running from Brickman the 21 | program **must be marked as an executable** and the **first line of the program 22 | must be** `#!/usr/bin/env python3`. To mark a program as executable run 23 | `chmod +x PROGRAM_NAME.py`. All of the demo programs are already marked as 24 | executable and already have `#!/usr/bin/env python3` so you should be fine, we 25 | only mention it so you know to do these things when writing your own programs. 26 | 27 | ## Command Line 28 | To run one of the demo programs from the command line, cd to the directory and 29 | run the program via `./PROGRAM_NAME.py`. Example: 30 | ``` 31 | $ cd ~/ev3dev-lang-python-demo/robots/R3PTAR/ 32 | $ ./r3ptar.py 33 | ``` 34 | ## Brickman 35 | To run one of the demo programs from Brickman, select the program in the 36 | File Browser. 37 | 38 | ## Demo Programs 39 | ### BALANC3R 40 | 41 | Laurens Valk's BALANC3R - This robot uses the gyro sensor to balance on two 42 | wheels. Use the IR remote to control BALANC3R 43 | 44 | * http://robotsquare.com/2014/07/01/tutorial-ev3-self-balancing-robot/ 45 | 46 | ### EV3D4 47 | 48 | * http://www.lego.com/en-us/mindstorms/build-a-robot/ev3d4 49 | * EV3D4RemoteControl - Use the IR remote to control EV3D4 50 | * EV3D4WebControl - Use a web interface to control EV3D4. There is a desktop version and a mobile version, both support touchscreen so you can drive via your smartphone. The web server will listen on port 8000 so go to http://x.x.x.x:8000/ 51 | 52 | ### EXPLOR3R 53 | 54 | Lauren Valk's EXPLOR3R 55 | 56 | * http://robotsquare.com/2015/10/06/explor3r-building-instructions/ 57 | 58 | ### MINDCUB3R 59 | 60 | David Gilday's MINDCUB3R 61 | 62 | * http://mindcuber.com/ 63 | 64 | ### TRACK3R 65 | 66 | A basic example of Object Oriented programming where there is a base TRACK3R 67 | class with child classes for the various permutations of TRACK3R 68 | 69 | * http://www.lego.com/en-us/mindstorms/build-a-robot/track3r 70 | * TRACK3R.py 71 | * TRACK3RWithBallShooter 72 | * TRACK3RWithClaw 73 | * TRACK3RWithSpinner 74 | 75 | ## More robot programs 76 | 77 | The [LEGO Mindstorms EV3 Comparison 78 | project](https://github.com/bittner/lego-mindstorms-ev3-comparison) sports a 79 | few other robot programs in addition. Its [comparison 80 | table](https://github.com/bittner/lego-mindstorms-ev3-comparison#inspiration-for-lego-ev3-robots) 81 | also lists programs created for other platforms than ev3dev. 82 | -------------------------------------------------------------------------------- /platform/brickpi3-motor-and-sensor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | The Brickpi3 doesn't support auto-detecting motors and sensors. To use devices 5 | connected to the input ports, you must specify what type of device it is. 6 | Output ports are pre-configured as NXT Large motors and do not need to be 7 | configured manually. 8 | """ 9 | 10 | from time import sleep 11 | from ev3dev2 import list_devices 12 | from ev3dev2.port import LegoPort 13 | from ev3dev2.motor import OUTPUT_A, LargeMotor, SpeedPercent 14 | from ev3dev2.sensor import INPUT_1 15 | from ev3dev2.sensor.lego import UltrasonicSensor 16 | 17 | p1 = LegoPort(INPUT_1) 18 | # http://docs.ev3dev.org/projects/lego-linux-drivers/en/ev3dev-stretch/brickpi3.html#brickpi3-in-port-modes 19 | p1.mode = 'ev3-uart' 20 | # http://docs.ev3dev.org/projects/lego-linux-drivers/en/ev3dev-stretch/sensors.html#supported-sensors 21 | p1.set_device = 'lego-ev3-us' 22 | 23 | # allow for some time to load the new drivers 24 | sleep(0.5) 25 | 26 | s = UltrasonicSensor(INPUT_1) 27 | m = LargeMotor(OUTPUT_A) 28 | 29 | print("Running motor...") 30 | 31 | while True: 32 | dist = s.distance_centimeters 33 | if dist < 50: 34 | m.on(SpeedPercent(30)) 35 | else: 36 | m.on(SpeedPercent(-30)) 37 | 38 | sleep(0.05) 39 | -------------------------------------------------------------------------------- /robots/BALANC3R/README.md: -------------------------------------------------------------------------------- 1 | # BALANC3R 2 | This demonstration script is for the BALANC3R robot. 3 | 4 | **Building Instructions**: http://robotsquare.com/2014/07/01/tutorial-ev3-self-balancing-robot/ 5 | 6 | **Note**: You will also need to add a touch sensor to port 1. Add it just 7 | like the Gyro, but on the other side of the brick. It will act as the program's 8 | safe stop button. 9 | -------------------------------------------------------------------------------- /robots/BALANC3R/drive.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Make BALANC3R robot stay upright and move in a pattern.""" 3 | 4 | import logging 5 | import time 6 | from ev3dev2.control.GyroBalancer import GyroBalancer, GracefulShutdown 7 | from ev3dev2.sensor.lego import InfraredSensor 8 | 9 | # Logging 10 | logging.basicConfig(level=logging.DEBUG, 11 | format='%(asctime)s %(levelname)5s: %(message)s') 12 | log = logging.getLogger(__name__) 13 | log.info("Starting BALANC3R") 14 | 15 | # Infrared remote 16 | remote = InfraredSensor() 17 | remote.mode = remote.MODE_IR_REMOTE 18 | 19 | # Balance robot 20 | robot = GyroBalancer() 21 | robot.balance() 22 | 23 | try: 24 | # Wait for top left button on remote to be pressed 25 | button_code = remote.value() 26 | while button_code != remote.TOP_LEFT: 27 | button_code = remote.value() 28 | time.sleep(0.5) # Give CPU a rest 29 | 30 | # Move robot in a simple pattern 31 | robot.rotate_left(seconds=3) 32 | robot.rotate_right(seconds=3) 33 | robot.move_forward(seconds=3) 34 | robot.move_backward(seconds=3) 35 | 36 | # Wait for user to terminate program 37 | while True: 38 | time.sleep(0.5) 39 | 40 | except (GracefulShutdown, Exception) as e: 41 | log.exception(e) 42 | # Exit cleanly so that all motors are stopped 43 | robot.shutdown() 44 | 45 | log.info("Exiting BALANC3R") 46 | -------------------------------------------------------------------------------- /robots/BALANC3R/remote.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Make BALANC3R robot stay upright and move in response to the remote.""" 3 | 4 | import logging 5 | import time 6 | from ev3dev2.control.GyroBalancer import GyroBalancer, GracefulShutdown 7 | from ev3dev2.sensor.lego import InfraredSensor 8 | 9 | # Logging 10 | logging.basicConfig(level=logging.DEBUG, 11 | format='%(asctime)s %(levelname)5s: %(message)s') 12 | log = logging.getLogger(__name__) 13 | log.info("Starting BALANC3R") 14 | 15 | # Infrared remote 16 | remote = InfraredSensor() 17 | remote.mode = remote.MODE_IR_REMOTE 18 | 19 | # Balance robot 20 | robot = GyroBalancer() 21 | robot.balance() 22 | 23 | try: 24 | while True: 25 | # Read remote 26 | button_code = remote.value() 27 | 28 | # Move robot in response to infrared remote 29 | if button_code == remote.TOP_LEFT_TOP_RIGHT: 30 | robot.move_forward() 31 | elif button_code == remote.TOP_LEFT_BOTTOM_RIGHT: 32 | robot.rotate_right() 33 | elif button_code == remote.BOTTOM_LEFT_TOP_RIGHT: 34 | robot.rotate_left() 35 | elif button_code == remote.BOTTOM_LEFT_BOTTOM_RIGHT: 36 | robot.move_backward() 37 | else: 38 | robot.stop() 39 | 40 | time.sleep(0.5) # Give CPU a rest 41 | 42 | except (GracefulShutdown, Exception) as e: 43 | log.exception(e) 44 | # Exit cleanly so that all motors are stopped 45 | robot.shutdown() 46 | 47 | log.info("Exiting BALANC3R") 48 | -------------------------------------------------------------------------------- /robots/DINOR3X/README.md: -------------------------------------------------------------------------------- 1 | # DINOR3X 2 | 3 | > Designed by Lasse Stenbæk Lauesen 4 | > 5 | > This charming robotic triceratops dinosaur is capable of walking and turning on all fours. 6 | 7 | The build instructions may be found at the official LEGO MINDSTROMS site [here](https://www.lego.com/cdn/cs/set/assets/bltad10c02978864265/DINOR3X.pdf). 8 | 9 | Please add a Color Sensor to Dinor3x's back or tail. Control DINOR3X as follows: 10 | 11 | - Make DINOR3X roar by pressing the IR Remote Control's Beacon button 12 | 13 | - Make DINOR3X walk or turn according to instructions from the IR Beacon 14 | - 2 top/up buttons together: walk forward 15 | - 2 bottom/down buttons together: walk backward 16 | - Top Left / Red Up: turn left on the spot 17 | - Top Right / Blue Up: turn right on the spot 18 | - Bottom Left / Red Down: stop 19 | - Bottom Right / Blue Down: calibrate to make the legs straight 20 | 21 | - Make DINOR3X change its speed when detecting some colors 22 | - Red: walk fast 23 | - Green: walk normally 24 | - White: walk slowly 25 | -------------------------------------------------------------------------------- /robots/DINOR3X/T-rex roar.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ev3dev/ev3dev-lang-python-demo/99eb25392ff350a2f9a3c180c3892c29d1fa5a21/robots/DINOR3X/T-rex roar.wav -------------------------------------------------------------------------------- /robots/DINOR3X/dinor3x.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env micropython 2 | 3 | 4 | from ev3dev2.motor import ( 5 | LargeMotor, MediumMotor, MoveTank, MoveSteering, 6 | OUTPUT_A, OUTPUT_B, OUTPUT_C 7 | ) 8 | from ev3dev2.sensor import INPUT_1, INPUT_3, INPUT_4 9 | from ev3dev2.sensor.lego import TouchSensor, ColorSensor, InfraredSensor 10 | from ev3dev2.sound import Sound 11 | 12 | 13 | class Dinor3x: 14 | FAST_WALK_SPEED = 80 15 | NORMAL_WALK_SPEED = 40 16 | SLOW_WALK_SPEED = 20 17 | 18 | def __init__( 19 | self, 20 | jaw_motor_port: str = OUTPUT_A, 21 | left_motor_port: str = OUTPUT_B, right_motor_port: str = OUTPUT_C, 22 | touch_sensor_port: str = INPUT_1, color_sensor_port: str = INPUT_3, 23 | ir_sensor_port: str = INPUT_4, ir_beacon_channel: int = 1): 24 | self.jaw_motor = MediumMotor(address=jaw_motor_port) 25 | 26 | self.left_motor = LargeMotor(address=left_motor_port) 27 | self.right_motor = LargeMotor(address=right_motor_port) 28 | self.tank_driver = MoveTank(left_motor_port=left_motor_port, 29 | right_motor_port=right_motor_port, 30 | motor_class=LargeMotor) 31 | self.steer_driver = MoveSteering(left_motor_port=left_motor_port, 32 | right_motor_port=right_motor_port, 33 | motor_class=LargeMotor) 34 | 35 | self.touch_sensor = TouchSensor(address=touch_sensor_port) 36 | self.color_sensor = ColorSensor(address=color_sensor_port) 37 | 38 | self.ir_sensor = InfraredSensor(address=ir_sensor_port) 39 | self.ir_beacon_channel = ir_beacon_channel 40 | 41 | self.speaker = Sound() 42 | 43 | self.roaring = False 44 | self.walk_speed = self.NORMAL_WALK_SPEED 45 | 46 | def roar_by_ir_beacon(self): 47 | """ 48 | Dinor3x roars when the Beacon button is pressed 49 | """ 50 | if self.ir_sensor.beacon(channel=self.ir_beacon_channel): 51 | self.roaring = True 52 | self.open_mouth() 53 | self.roar() 54 | 55 | elif self.roaring: 56 | self.roaring = False 57 | self.close_mouth() 58 | 59 | def change_speed_by_color(self): 60 | """ 61 | Dinor3x changes its speed when detecting some colors 62 | - Red: walk fast 63 | - Green: walk normally 64 | - White: walk slowly 65 | """ 66 | if self.color_sensor.color == ColorSensor.COLOR_RED: 67 | self.speaker.speak( 68 | text='RUN!', 69 | volume=100, 70 | play_type=Sound.PLAY_NO_WAIT_FOR_COMPLETE) 71 | self.walk_speed = self.FAST_WALK_SPEED 72 | self.walk(speed=self.walk_speed) 73 | 74 | elif self.color_sensor.color == ColorSensor.COLOR_GREEN: 75 | self.speaker.speak( 76 | text='Normal', 77 | volume=100, 78 | play_type=Sound.PLAY_NO_WAIT_FOR_COMPLETE) 79 | self.walk_speed = self.NORMAL_WALK_SPEED 80 | self.walk(speed=self.walk_speed) 81 | 82 | elif self.color_sensor.color == ColorSensor.COLOR_WHITE: 83 | self.speaker.speak( 84 | text='slow...', 85 | volume=100, 86 | play_type=Sound.PLAY_NO_WAIT_FOR_COMPLETE) 87 | self.walk_speed = self.SLOW_WALK_SPEED 88 | self.walk(speed=self.walk_speed) 89 | 90 | def walk_by_ir_beacon(self): 91 | """ 92 | Dinor3x walks or turns according to instructions from the IR Beacon 93 | - 2 top/up buttons together: walk forward 94 | - 2 bottom/down buttons together: walk backward 95 | - Top Left / Red Up: turn left on the spot 96 | - Top Right / Blue Up: turn right on the spot 97 | - Bottom Left / Red Down: stop 98 | - Bottom Right / Blue Down: calibrate to make the legs straight 99 | """ 100 | 101 | # forward 102 | if self.ir_sensor.top_left(channel=self.ir_beacon_channel) and \ 103 | self.ir_sensor.top_right: 104 | self.walk(speed=self.walk_speed) 105 | 106 | # backward 107 | elif self.ir_sensor.bottom_left(channel=self.ir_beacon_channel) and \ 108 | self.ir_sensor.bottom_right(channel=self.ir_beacon_channel): 109 | self.walk(speed=-self.walk_speed) 110 | 111 | # turn left on the spot 112 | elif self.ir_sensor.top_left(channel=self.ir_beacon_channel): 113 | self.turn(speed=self.walk_speed) 114 | 115 | # turn right on the spot 116 | elif self.ir_sensor.top_right(channel=self.ir_beacon_channel): 117 | self.turn(speed=-self.walk_speed) 118 | 119 | # stop 120 | elif self.ir_sensor.bottom_left(channel=self.ir_beacon_channel): 121 | self.tank_driver.off(brake=True) 122 | 123 | # calibrate legs 124 | elif self.ir_sensor.bottom_right(channel=self.ir_beacon_channel): 125 | self.calibrate_legs() 126 | 127 | def calibrate_legs(self): 128 | self.tank_driver.on( 129 | left_speed=10, 130 | right_speed=20) 131 | 132 | self.touch_sensor.wait_for_released() 133 | 134 | self.tank_driver.off(brake=True) 135 | 136 | self.left_motor.on(speed=40) 137 | 138 | self.touch_sensor.wait_for_pressed() 139 | 140 | self.left_motor.off(brake=True) 141 | 142 | self.left_motor.on_for_rotations( 143 | rotations=-0.2, 144 | speed=50, 145 | brake=True, 146 | block=True) 147 | 148 | self.right_motor.on(speed=40) 149 | 150 | self.touch_sensor.wait_for_pressed() 151 | 152 | self.right_motor.off(brake=True) 153 | 154 | self.right_motor.on_for_rotations( 155 | rotations=-0.2, 156 | speed=50, 157 | brake=True, 158 | block=True) 159 | 160 | self.left_motor.reset() 161 | self.right_motor.reset() 162 | 163 | def walk(self, speed: float = 100): 164 | self.calibrate_legs() 165 | 166 | self.steer_driver.on( 167 | steering=0, 168 | speed=-speed) 169 | 170 | def turn(self, speed: float = 100): 171 | self.calibrate_legs() 172 | 173 | if speed >= 0: 174 | self.left_motor.on_for_degrees( 175 | degrees=180, 176 | speed=speed, 177 | brake=True, 178 | block=True) 179 | 180 | else: 181 | self.right_motor.on_for_degrees( 182 | degrees=180, 183 | speed=-speed, 184 | brake=True, 185 | block=True) 186 | 187 | self.tank_driver.on( 188 | left_speed=speed, 189 | right_speed=-speed) 190 | 191 | def close_mouth(self): 192 | self.jaw_motor.on_for_seconds( 193 | speed=-20, 194 | seconds=1, 195 | brake=False, 196 | block=False) 197 | 198 | def open_mouth(self): 199 | self.jaw_motor.on_for_seconds( 200 | speed=20, 201 | seconds=1, 202 | block=False, 203 | brake=False) 204 | 205 | def roar(self): 206 | self.speaker.play_file( 207 | wav_file='T-rex roar.wav', 208 | volume=100, 209 | play_type=Sound.PLAY_NO_WAIT_FOR_COMPLETE) 210 | 211 | self.jaw_motor.on_for_degrees( 212 | speed=40, 213 | degrees=-60, 214 | block=True, 215 | brake=True) 216 | 217 | for i in range(12): 218 | self.jaw_motor.on_for_seconds( 219 | speed=-40, 220 | seconds=0.05, 221 | block=True, 222 | brake=True) 223 | 224 | self.jaw_motor.on_for_seconds( 225 | speed=40, 226 | seconds=0.05, 227 | block=True, 228 | brake=True) 229 | 230 | self.jaw_motor.on_for_seconds( 231 | speed=20, 232 | seconds=0.5, 233 | brake=False, 234 | block=True) 235 | 236 | def main(self): 237 | self.close_mouth() 238 | 239 | while True: 240 | self.roar_by_ir_beacon() 241 | self.change_speed_by_color() 242 | self.walk_by_ir_beacon() 243 | 244 | 245 | if __name__ == '__main__': 246 | DINOR3X = Dinor3x() 247 | DINOR3X.main() 248 | -------------------------------------------------------------------------------- /robots/EDUCATOR/README.md: -------------------------------------------------------------------------------- 1 | # EDUCATOR 2 | These demonstration scripts are for the EDUCATOR robot that can be built using 3 | the Education kit (45544). You can also build a close approximation using the 4 | Home kit (31313) if you purchase some additional parts such as a gyro sensor and 5 | ultrasonic sensor. You can substitute the ball and socket from the Education 6 | kit with a single rear wheel. 7 | 8 | **Building Instructions Using Education Kit (45544)**: https://education.lego.com/en-au/support/mindstorms-ev3/building-instructions 9 | 10 | **Building Instructions Using Home Kit (31313)**: 11 | https://sites.google.com/site/gask3t/lego-ev3/building-plans/educator-vehicle-retail-kit-version 12 | -------------------------------------------------------------------------------- /robots/EDUCATOR/color.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Make robot say whatever color it observes with the color sensor.""" 3 | 4 | from ev3dev2.sensor.lego import ColorSensor 5 | from time import sleep 6 | from ev3dev2.sound import Sound 7 | 8 | color_sensor = ColorSensor() 9 | sound = Sound() 10 | 11 | while True: 12 | color = color_sensor.color 13 | text = ColorSensor.COLORS[color] 14 | sound.speak(text) 15 | sleep(2) 16 | -------------------------------------------------------------------------------- /robots/EDUCATOR/square-gyro.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Move robot in a square path using the Gyro sensor. 4 | 5 | This script is a simple demonstration of turning using the Gyro sensor. 6 | """ 7 | 8 | from ev3dev2.motor import MoveSteering, OUTPUT_B, OUTPUT_C 9 | from ev3dev2.sensor.lego import GyroSensor 10 | 11 | 12 | motor_pair = MoveSteering(OUTPUT_B, OUTPUT_C) 13 | gyro = GyroSensor() 14 | gyro.mode = GyroSensor.MODE_GYRO_ANG 15 | 16 | for i in range(4): 17 | 18 | # Move robot forward for 3 seconds 19 | motor_pair.on_for_seconds(steering=0, speed=50, seconds=3) 20 | 21 | # Spin robot to the left 22 | motor_pair.on(steering=-100, speed=5) 23 | 24 | # Wait until angle changed by 90 degrees 25 | gyro.wait_until_angle_changed_by(90) 26 | 27 | # Stop motors 28 | motor_pair.off() 29 | -------------------------------------------------------------------------------- /robots/EDUCATOR/square.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Move robot in a square path without using the Gyro sensor. 4 | 5 | This script is a simple demonstration of moving forward and turning. 6 | """ 7 | 8 | from ev3dev2.motor import MoveSteering, OUTPUT_B, OUTPUT_C 9 | 10 | motor_pair = MoveSteering(OUTPUT_B, OUTPUT_C) 11 | 12 | for i in range(4): 13 | 14 | # Move robot forward for 3 seconds 15 | motor_pair.on_for_seconds(steering=0, speed=50, seconds=3) 16 | 17 | # Turn robot left 90 degrees (adjust rotations for your particular robot) 18 | motor_pair.on_for_rotations(steering=-100, speed=5, rotations=0.5) 19 | -------------------------------------------------------------------------------- /robots/EDUCATOR/touch.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Reverse robot if bumps into wall. 4 | 5 | This script is a simple demonstration of the touch sensor. 6 | """ 7 | 8 | from ev3dev2.motor import MoveSteering, OUTPUT_B, OUTPUT_C 9 | from ev3dev2.sensor.lego import TouchSensor 10 | 11 | motor_pair = MoveSteering(OUTPUT_B, OUTPUT_C) 12 | touch_sensor = TouchSensor() 13 | 14 | # Start robot moving forward 15 | motor_pair.on(steering=0, speed=10) 16 | 17 | # Wait until robot touches wall 18 | touch_sensor.wait_for_pressed() 19 | 20 | # Stop moving forward 21 | motor_pair.off() 22 | 23 | # Reverse away from wall 24 | motor_pair.on_for_seconds(steering=0, speed=-10, seconds=2) 25 | -------------------------------------------------------------------------------- /robots/EDUCATOR/ultrasonic.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Use robot to reposition cuboid. 4 | 5 | This script is a simple demonstration of the ultrasonic sensor and medium 6 | motor. 7 | """ 8 | 9 | from ev3dev2.motor import ( 10 | MoveSteering, MediumMotor, OUTPUT_A, OUTPUT_B, OUTPUT_C) 11 | from ev3dev2.sensor.lego import UltrasonicSensor 12 | from time import sleep 13 | 14 | motor_pair = MoveSteering(OUTPUT_B, OUTPUT_C) 15 | medium_motor = MediumMotor(OUTPUT_A) 16 | ultrasonic_sensor = UltrasonicSensor() 17 | 18 | # Start robot moving forward 19 | motor_pair.on(steering=0, speed=10) 20 | 21 | # Wait until robot less than 3.5cm from cuboid 22 | while ultrasonic_sensor.distance_centimeters > 3.5: 23 | sleep(0.01) 24 | 25 | # Stop moving forward 26 | motor_pair.off() 27 | 28 | # Lower robot arm over cuboid 29 | medium_motor.on_for_degrees(speed=-10, degrees=90) 30 | 31 | # Drag cuboid backwards for 2 seconds 32 | motor_pair.on_for_seconds(steering=0, speed=-20, seconds=2) 33 | 34 | # Raise robot arm 35 | medium_motor.on_for_degrees(speed=10, degrees=90) 36 | 37 | # Move robot away from cuboid 38 | motor_pair.on_for_seconds(steering=0, speed=-20, seconds=2) 39 | -------------------------------------------------------------------------------- /robots/EL3CTRIC_GUITAR/README.md: -------------------------------------------------------------------------------- 1 | # El3ctric Guitar 2 | 3 | > Designed by Daniele Benedettelli 4 | > 5 | > Ready to to rock’n roll? This LEGO Guitar can be played almost like a real guitar. Stroke the string, slide your fingers across its fretless neck, and bend the notes using the tremolo bar to produce the most amazing guitar solos! 6 | 7 | The build instructions may be found at the official LEGO MINDSTROMS site [here](https://www.lego.com/cdn/cs/set/assets/blt4e3bb67a2139cfef/EL3CTRIC_GUITAR.pdf) 8 | -------------------------------------------------------------------------------- /robots/EL3CTRIC_GUITAR/el3ctric_guitar.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env micropython 2 | 3 | 4 | from ev3dev2.motor import MediumMotor, OUTPUT_D 5 | from ev3dev2.sensor import INPUT_1, INPUT_4 6 | from ev3dev2.sensor.lego import TouchSensor, InfraredSensor 7 | from ev3dev2.led import Leds 8 | from ev3dev2.sound import Sound 9 | 10 | from time import sleep 11 | 12 | 13 | class El3ctricGuitar: 14 | NOTES = [1318, 1174, 987, 880, 783, 659, 587, 493, 440, 392, 329, 293] 15 | N_NOTES = len(NOTES) 16 | 17 | def __init__( 18 | self, lever_motor_port: str = OUTPUT_D, 19 | touch_sensor_port: str = INPUT_1, ir_sensor_port: str = INPUT_4): 20 | self.lever_motor = MediumMotor(address=lever_motor_port) 21 | 22 | self.touch_sensor = TouchSensor(address=touch_sensor_port) 23 | 24 | self.ir_sensor = InfraredSensor(address=ir_sensor_port) 25 | 26 | self.leds = Leds() 27 | 28 | self.speaker = Sound() 29 | 30 | def start_up(self): 31 | self.leds.animate_flash( 32 | color='ORANGE', 33 | groups=('LEFT', 'RIGHT'), 34 | sleeptime=0.5, 35 | duration=3, 36 | block=True) 37 | 38 | self.lever_motor.on_for_seconds( 39 | speed=5, 40 | seconds=1, 41 | brake=False, 42 | block=True) 43 | 44 | self.lever_motor.on_for_degrees( 45 | speed=-5, 46 | degrees=30, 47 | brake=True, 48 | block=True) 49 | 50 | sleep(0.1) 51 | 52 | self.lever_motor.reset() 53 | 54 | def play_music(self): 55 | if self.touch_sensor.is_released: 56 | raw = sum(self.ir_sensor.proximity for _ in range(4)) / 4 57 | 58 | self.speaker.tone( 59 | self.NOTES[min(round(raw / 5), self.N_NOTES - 1)] 60 | - 11 * self.lever_motor.position, 61 | 100, 62 | play_type=Sound.PLAY_WAIT_FOR_COMPLETE) 63 | 64 | def main(self): 65 | self.start_up() 66 | while True: 67 | self.play_music() 68 | 69 | 70 | if __name__ == '__main__': 71 | EL3CTRIC_GUITAR = El3ctricGuitar() 72 | EL3CTRIC_GUITAR.main() 73 | -------------------------------------------------------------------------------- /robots/EV3D4/EV3D4RemoteControl.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import logging 4 | import sys 5 | from ev3dev2.motor import OUTPUT_A, OUTPUT_B, OUTPUT_C, MediumMotor 6 | from ev3dev2.control.rc_tank import RemoteControlledTank 7 | 8 | 9 | class EV3D4RemoteControlled(RemoteControlledTank): 10 | 11 | def __init__(self, medium_motor=OUTPUT_A, left_motor=OUTPUT_C, right_motor=OUTPUT_B): 12 | RemoteControlledTank.__init__(self, left_motor, right_motor) 13 | self.medium_motor = MediumMotor(medium_motor) 14 | self.medium_motor.reset() 15 | 16 | 17 | if __name__ == '__main__': 18 | 19 | # Change level to logging.INFO to make less chatty 20 | logging.basicConfig(level=logging.DEBUG, 21 | format="%(asctime)s %(levelname)5s %(filename)s:%(lineno)5s - %(funcName)25s(): %(message)s") 22 | log = logging.getLogger(__name__) 23 | 24 | # Color the errors and warnings in red 25 | logging.addLevelName(logging.ERROR, "\033[91m %s\033[0m" % logging.getLevelName(logging.ERROR)) 26 | logging.addLevelName(logging.WARNING, "\033[91m%s\033[0m" % logging.getLevelName(logging.WARNING)) 27 | 28 | log.info("Starting EV3D4") 29 | ev3d4 = EV3D4RemoteControlled() 30 | ev3d4.main() 31 | log.info("Exiting EV3D4") 32 | -------------------------------------------------------------------------------- /robots/EV3D4/EV3D4WebControl.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import logging 4 | import sys 5 | from ev3dev2.motor import OUTPUT_A, OUTPUT_B, OUTPUT_C, MediumMotor 6 | from ev3dev2.control.webserver import WebControlledTank 7 | 8 | 9 | class EV3D4WebControlled(WebControlledTank): 10 | 11 | def __init__(self, medium_motor=OUTPUT_A, left_motor=OUTPUT_C, right_motor=OUTPUT_B): 12 | WebControlledTank.__init__(self, left_motor, right_motor) 13 | self.medium_motor = MediumMotor(medium_motor) 14 | self.medium_motor.reset() 15 | 16 | 17 | if __name__ == '__main__': 18 | 19 | # Change level to logging.INFO to make less chatty 20 | logging.basicConfig(level=logging.DEBUG, 21 | format="%(asctime)s %(levelname)5s %(filename)s:%(lineno)5s - %(funcName)25s(): %(message)s") 22 | log = logging.getLogger(__name__) 23 | 24 | # Color the errors and warnings in red 25 | logging.addLevelName(logging.ERROR, "\033[91m %s\033[0m" % logging.getLevelName(logging.ERROR)) 26 | logging.addLevelName(logging.WARNING, "\033[91m%s\033[0m" % logging.getLevelName(logging.WARNING)) 27 | 28 | log.info("Starting EV3D4") 29 | ev3d4 = EV3D4WebControlled() 30 | ev3d4.main() # start the web server 31 | log.info("Exiting EV3D4") 32 | -------------------------------------------------------------------------------- /robots/EV3D4/README.md: -------------------------------------------------------------------------------- 1 | # EV3D4 2 | EV3D4 is designed to look like R2-D2 from Star Wars. There are two options for 3 | controlling EV3D4. The first is to use the IR remote to send commands to the IR 4 | sensor, run EV3D4RemoteControl.py to use this method. The second means of 5 | controlling EV3D4 is via a web browser, run EV3D4WebControl.py to use this method. 6 | You can run both of these from the Brickman interface or if logged in via ssh 7 | you can run them via ./EV3D4RemoteControl.py or ./EV3D4WebControl.py. 8 | 9 | **Building instructions**: https://www.lego.com/en-us/mindstorms/build-a-robot/ev3d4 10 | 11 | ### EV3D4RemoteControl 12 | EV3D4RemoteControl.py creates a child class of ev3dev/helper.py's 13 | RemoteControlledTank. 14 | 15 | 16 | ### EV3D4WebControl 17 | EV3D4WebControl creates a child class of ev3dev/webserver.py's WebControlledTank. 18 | The WebControlledTank class runs a web server that serves the web pages, 19 | images, etc but it also services the AJAX calls made via the client. The user 20 | loads the initial web page at which point they choose the "Desktop interface" 21 | or the "Mobile Interface". 22 | 23 | Desktop Interface - The user is presented with four arrows for moving forwards, 24 | backwards, spinning clockwise or spinning counter-clockwise. Two additional 25 | buttons are provided for controlling the medium motor. There are two sliders, 26 | one to control the speed of the tank and the other to control the speed of the 27 | medium motor. 28 | 29 | Mobile Interface - The user is presented with a virtual joystick that is used 30 | to control the movements of the robot. Slide your thumb forward and the robot 31 | moves forward, slide it to the right and the robot spins clockwise, etc. The 32 | further you move the joystick towards the edge of the circle the faster the 33 | robot moves. Buttons and a speed slider for the medium motor are also provided. 34 | 35 | Both interfaces have touch support so you can use either Desktop or Mobile from 36 | your smartphone. When the user clicks/touches a button some jQuery code will 37 | fire off an AJAX call to let the EV3D4 web server know what the user clicked or 38 | where the joystick is if using the Mobile Interface. The web server in 39 | WebControlledTank services this AJAX call and adjust motor speed/power 40 | accordingly. 41 | 42 | You can see a demo of the web interface below. Note that the demo is on a 43 | simple Tank robot, not EV3D4, but that doesn't really matter as EV3D4 is also 44 | just a Tank robot. 45 | 46 | **Demo Video**: https://www.youtube.com/watch?v=x5VauXr7W4A 47 | -------------------------------------------------------------------------------- /robots/EV3D4/desktop.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Lego Tank 13 | 14 | 15 |
16 | 18 | 19 |
20 | 21 |
22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 |
30 |
31 | 32 |
33 | 34 | 35 |
36 |
37 | 38 |
39 |
40 | 41 |
42 | 43 |
44 | 45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /robots/EV3D4/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ev3dev/ev3dev-lang-python-demo/99eb25392ff350a2f9a3c180c3892c29d1fa5a21/robots/EV3D4/favicon.ico -------------------------------------------------------------------------------- /robots/EV3D4/include/ArrowClockwise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ev3dev/ev3dev-lang-python-demo/99eb25392ff350a2f9a3c180c3892c29d1fa5a21/robots/EV3D4/include/ArrowClockwise.png -------------------------------------------------------------------------------- /robots/EV3D4/include/ArrowDown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ev3dev/ev3dev-lang-python-demo/99eb25392ff350a2f9a3c180c3892c29d1fa5a21/robots/EV3D4/include/ArrowDown.png -------------------------------------------------------------------------------- /robots/EV3D4/include/ArrowLeft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ev3dev/ev3dev-lang-python-demo/99eb25392ff350a2f9a3c180c3892c29d1fa5a21/robots/EV3D4/include/ArrowLeft.png -------------------------------------------------------------------------------- /robots/EV3D4/include/ArrowRight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ev3dev/ev3dev-lang-python-demo/99eb25392ff350a2f9a3c180c3892c29d1fa5a21/robots/EV3D4/include/ArrowRight.png -------------------------------------------------------------------------------- /robots/EV3D4/include/ArrowUp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ev3dev/ev3dev-lang-python-demo/99eb25392ff350a2f9a3c180c3892c29d1fa5a21/robots/EV3D4/include/ArrowUp.png -------------------------------------------------------------------------------- /robots/EV3D4/include/desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ev3dev/ev3dev-lang-python-demo/99eb25392ff350a2f9a3c180c3892c29d1fa5a21/robots/EV3D4/include/desktop.png -------------------------------------------------------------------------------- /robots/EV3D4/include/gear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ev3dev/ev3dev-lang-python-demo/99eb25392ff350a2f9a3c180c3892c29d1fa5a21/robots/EV3D4/include/gear.png -------------------------------------------------------------------------------- /robots/EV3D4/include/jquery.ui.touch-punch.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery UI Touch Punch 0.2.3 3 | * 4 | * Copyright 2011-2014, Dave Furfero 5 | * Dual licensed under the MIT or GPL Version 2 licenses. 6 | * 7 | * Depends: 8 | * jquery.ui.widget.js 9 | * jquery.ui.mouse.js 10 | */ 11 | !function(a){function f(a,b){if(!(a.originalEvent.touches.length>1)){a.preventDefault();var c=a.originalEvent.changedTouches[0],d=document.createEvent("MouseEvents");d.initMouseEvent(b,!0,!0,window,1,c.screenX,c.screenY,c.clientX,c.clientY,!1,!1,!1,!1,0,null),a.target.dispatchEvent(d)}}if(a.support.touch="ontouchend"in document,a.support.touch){var e,b=a.ui.mouse.prototype,c=b._mouseInit,d=b._mouseDestroy;b._touchStart=function(a){var b=this;!e&&b._mouseCapture(a.originalEvent.changedTouches[0])&&(e=!0,b._touchMoved=!1,f(a,"mouseover"),f(a,"mousemove"),f(a,"mousedown"))},b._touchMove=function(a){e&&(this._touchMoved=!0,f(a,"mousemove"))},b._touchEnd=function(a){e&&(f(a,"mouseup"),f(a,"mouseout"),this._touchMoved||f(a,"click"),e=!1)},b._mouseInit=function(){var b=this;b.element.bind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),c.call(b)},b._mouseDestroy=function(){var b=this;b.element.unbind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),d.call(b)}}}(jQuery); 12 | -------------------------------------------------------------------------------- /robots/EV3D4/include/mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ev3dev/ev3dev-lang-python-demo/99eb25392ff350a2f9a3c180c3892c29d1fa5a21/robots/EV3D4/include/mobile.png -------------------------------------------------------------------------------- /robots/EV3D4/include/tank-desktop.js: -------------------------------------------------------------------------------- 1 | 2 | var moving = 0; 3 | var ip = 0; 4 | var seq = 0; 5 | 6 | function stop_motors() { 7 | var ajax_url = "/" + seq + "/move-stop/" 8 | seq++; 9 | 10 | $.ajax({ 11 | type: "GET", 12 | cache: false, 13 | dataType: 'text', 14 | url: ajax_url 15 | }); 16 | 17 | moving = 0 18 | } 19 | 20 | function stop_medium_motor() { 21 | var ajax_url = "/" + seq + "/motor-stop/medium/" 22 | seq++; 23 | 24 | $.ajax({ 25 | type: "GET", 26 | cache: false, 27 | dataType: 'text', 28 | url: ajax_url 29 | }); 30 | return false; 31 | } 32 | 33 | // Prevent the page from scrolling on an iphone 34 | // http://stackoverflow.com/questions/7768269/ipad-safari-disable-scrolling-and-bounce-effect 35 | $(document).bind( 36 | 'touchmove', 37 | function(e) { 38 | e.preventDefault(); 39 | } 40 | ); 41 | 42 | $(document).ready(function() { 43 | 44 | $("#medium-motor-speed").slider({ 45 | min: 0, 46 | max: 100, 47 | step: 5, 48 | value: 50 49 | }); 50 | 51 | $("#tank-speed").slider({ 52 | min: 0, 53 | max: 100, 54 | step: 5, 55 | value: 25 56 | }); 57 | 58 | // Desktop Interface 59 | $('#ArrowUp').bind('touchstart mousedown', function() { 60 | console.log('ArrowUp down') 61 | var power = $('#tank-speed').slider("value") 62 | var ajax_url = "/" + seq + "/move-start/forward/" + power + "/" 63 | seq++; 64 | moving = 1 65 | 66 | $.ajax({ 67 | type: "GET", 68 | cache: false, 69 | dataType: 'text', 70 | url: ajax_url 71 | }); 72 | return false; 73 | }); 74 | 75 | $('#ArrowDown').bind('touchstart mousedown', function() { 76 | console.log('ArrowDown down') 77 | var power = $('#tank-speed').slider("value") 78 | var ajax_url = "/" + seq + "/move-start/backward/" + power + "/" 79 | seq++; 80 | moving = 1 81 | 82 | $.ajax({ 83 | type: "GET", 84 | cache: false, 85 | dataType: 'text', 86 | url: ajax_url 87 | }); 88 | return false; 89 | }); 90 | 91 | $('#ArrowLeft').bind('touchstart mousedown', function() { 92 | console.log('ArrowLeft down') 93 | var power = $('#tank-speed').slider("value") 94 | var ajax_url = "/" + seq + "/move-start/left/" + power + "/" 95 | seq++; 96 | moving = 1 97 | 98 | $.ajax({ 99 | type: "GET", 100 | cache: false, 101 | dataType: 'text', 102 | url: ajax_url 103 | }); 104 | return false; 105 | }); 106 | 107 | $('#ArrowRight').bind('touchstart mousedown', function() { 108 | console.log('ArrowRight down') 109 | var power = $('#tank-speed').slider("value") 110 | var ajax_url = "/" + seq + "/move-start/right/" + power + "/" 111 | seq++; 112 | moving = 1 113 | 114 | $.ajax({ 115 | type: "GET", 116 | cache: false, 117 | dataType: 'text', 118 | url: ajax_url 119 | }); 120 | return false; 121 | }); 122 | 123 | $('#desktop-medium-motor-spin .CounterClockwise').bind('touchstart mousedown', function() { 124 | console.log('CounterClockwise down') 125 | var power = $('#medium-motor-speed').slider("value") 126 | var ajax_url = "/" + seq + "/motor-start/medium/counter-clockwise/" + power + "/" 127 | seq++; 128 | 129 | $.ajax({ 130 | type: "GET", 131 | cache: false, 132 | dataType: 'text', 133 | url: ajax_url 134 | }); 135 | return false; 136 | }); 137 | 138 | $('#desktop-medium-motor-spin .Clockwise').bind('touchstart mousedown', function() { 139 | console.log('Clockwise down') 140 | var power = $('#medium-motor-speed').slider("value") 141 | var ajax_url = "/" + seq + "/motor-start/medium/clockwise/" + power + "/" 142 | seq++; 143 | 144 | $.ajax({ 145 | type: "GET", 146 | cache: false, 147 | dataType: 'text', 148 | url: ajax_url 149 | }); 150 | return false; 151 | }); 152 | 153 | $('.medium').bind('touchend mouseup mouseout', function() { 154 | stop_medium_motor() 155 | return false; 156 | }); 157 | 158 | $('.nav').bind('touchend mouseup mouseout', function() { 159 | if (moving) { 160 | console.log('Mouse no longer over button') 161 | stop_motors() 162 | return false; 163 | } 164 | }); 165 | }); 166 | -------------------------------------------------------------------------------- /robots/EV3D4/include/tank-mobile.js: -------------------------------------------------------------------------------- 1 | 2 | var start_x = 0; 3 | var start_y = 0; 4 | var moving = 0; 5 | var seq = 0; 6 | var prev_x = 0; 7 | var prev_y = 0; 8 | 9 | function stop_motors() { 10 | var ajax_url = "/" + seq + "/move-stop/" 11 | seq++; 12 | 13 | $.ajax({ 14 | type: "GET", 15 | cache: false, 16 | dataType: 'text', 17 | async: true, 18 | url: ajax_url 19 | }); 20 | moving = 0; 21 | } 22 | 23 | function stop_medium_motor() { 24 | var ajax_url = "/" + seq + "/motor-stop/medium/" 25 | seq++; 26 | 27 | $.ajax({ 28 | type: "GET", 29 | cache: false, 30 | dataType: 'text', 31 | async: true, 32 | url: ajax_url 33 | }); 34 | return false; 35 | } 36 | 37 | function ajax_move_xy(x, y) { 38 | // console.log("FIRE ajax call with x,y " + x + "," + y) 39 | var ajax_url = "/" + seq + "/move-xy/" + x + "/" + y + "/" 40 | seq++; 41 | 42 | $.ajax({ 43 | type: "GET", 44 | cache: false, 45 | dataType: 'text', 46 | async: true, 47 | url: ajax_url 48 | }); 49 | moving = 1; 50 | } 51 | 52 | function ajax_log(msg) { 53 | var ajax_url = "/" + seq + "/log/" + msg + "/" 54 | seq++; 55 | 56 | $.ajax({ 57 | type: "GET", 58 | cache: false, 59 | dataType: 'text', 60 | async: true, 61 | url: ajax_url 62 | }); 63 | } 64 | 65 | 66 | // Prevent the page from scrolling on an iphone 67 | // http://stackoverflow.com/questions/7768269/ipad-safari-disable-scrolling-and-bounce-effect 68 | $(document).bind( 69 | 'touchmove', 70 | function(e) { 71 | e.preventDefault(); 72 | } 73 | ); 74 | 75 | $(document).ready(function() { 76 | 77 | // Used the 'Restrict the inside circle to the outside circle' code 78 | var r = $('#joystick-wrapper').width()/2; 79 | var small_r = $('#joystick').width()/2; 80 | var origin_x = r - small_r; 81 | var origin_y = r - small_r; 82 | 83 | $("#medium-motor-speed").slider({ 84 | min: 0, 85 | max: 100, 86 | step: 5, 87 | value: 50 88 | }); 89 | 90 | $('#medium-motor-spin .CounterClockwise').bind('touchstart mousedown', function() { 91 | var power = $('#medium-motor-speed').slider("value") 92 | var ajax_url = "/" + seq + "/motor-start/medium/counter-clockwise/" + power + "/" 93 | seq++; 94 | 95 | $.ajax({ 96 | type: "GET", 97 | cache: false, 98 | dataType: 'text', 99 | url: ajax_url 100 | }); 101 | return false; 102 | }); 103 | 104 | $('#medium-motor-spin .Clockwise').bind('touchstart mousedown', function() { 105 | var power = $('#medium-motor-speed').slider("value") 106 | var ajax_url = "/" + seq + "/motor-start/medium/clockwise/" + power + "/" 107 | seq++; 108 | 109 | $.ajax({ 110 | type: "GET", 111 | cache: false, 112 | dataType: 'text', 113 | url: ajax_url 114 | }); 115 | return false; 116 | }); 117 | 118 | $('.medium').bind('touchend mouseup', function() { 119 | stop_medium_motor() 120 | return false; 121 | }); 122 | 123 | $("div#joystick").draggable({ 124 | revert: true, 125 | containment: "parent", 126 | create: function() { 127 | start_x = parseInt($(this).css("left")); 128 | start_y = parseInt($(this).css("top")); 129 | prev_x = start_x; 130 | prev_y = start_y; 131 | }, 132 | drag: function(event, ui) { 133 | 134 | // Restrict the inside circle to the outside circle 135 | // http://stackoverflow.com/questions/26787996/containing-draggable-circle-to-a-larger-circle 136 | var x = ui.position.left - origin_x, y = ui.position.top - origin_y; 137 | var l = Math.sqrt(x*x + y*y); 138 | var l_in = Math.min(r - small_r, l); 139 | ui.position = {'left': Math.round(x/l*l_in) + origin_x, 'top': Math.round(y/l*l_in) + origin_y}; 140 | 141 | // Get coordinates 142 | var x = ui.position.left - start_x 143 | var y = (ui.position.top - start_y) * -1 144 | var distance = 0; 145 | 146 | // If this is the initial touch then set the distance high so we'll move 147 | if (prev_x == start_x && prev_y == start_y) { 148 | distance = 99; 149 | } else { 150 | distance = Math.round(Math.sqrt(((x - prev_x) * (x - prev_x)) + ((y - prev_y) * (y - prev_y)))); 151 | } 152 | 153 | // When you drag the joystick it can fire off a LOT of drag 154 | // events (one about every 8 ms), it ends up overwhelming the 155 | // web server on the EV3. It takes the EV3 ~55ms to process 156 | // one of these request so don't send one if the x,y coordinates 157 | // have only changed a tiny bit 158 | if (distance >= 10) { 159 | ajax_move_xy(x, y); 160 | prev_x = x; 161 | prev_y = y; 162 | } 163 | }, 164 | stop: function() { 165 | if (moving) { 166 | stop_motors(); 167 | } 168 | prev_x = start_x; 169 | prev_y = start_y; 170 | } 171 | }); 172 | 173 | // This reacts much faster than the draggable stop event 174 | $('#joystick-wrapper').bind('touchend mouseup', function() { 175 | if (moving) { 176 | stop_motors() 177 | } 178 | prev_x = start_x; 179 | prev_y = start_y; 180 | return true; 181 | }); 182 | 183 | $('#joystick-wrapper').bind('touchstart mousedown', function() { 184 | var ajax_url = "/" + seq + "/joystick-engaged/" 185 | seq++; 186 | 187 | $.ajax({ 188 | type: "GET", 189 | cache: false, 190 | dataType: 'text', 191 | async: true, 192 | url: ajax_url 193 | }); 194 | }); 195 | }); 196 | -------------------------------------------------------------------------------- /robots/EV3D4/include/tank.css: -------------------------------------------------------------------------------- 1 | /* Entire Page - layout */ 2 | 3 | body { 4 | margin: 0px; 5 | padding: 0; 6 | background: #FFF; 7 | font-family: 'Armata', sans-serif; 8 | font-size: 12px; 9 | color: #222; 10 | } 11 | 12 | .alignCenter{ 13 | width: 960px; 14 | margin: 0px auto; 15 | } 16 | 17 | div#header { 18 | padding-bottom: 70px; 19 | } 20 | 21 | div#controls { 22 | float: left; 23 | width: 450px; 24 | } 25 | 26 | img.button { 27 | cursor: pointer; 28 | border: 4px solid white; 29 | 30 | /* rounded corners */ 31 | -moz-border-radius: 20px; 32 | -webkit-border-radius: 20px; 33 | -khtml-border-radius: 20px; 34 | border-radius: 20px; 35 | 36 | -webkit-touch-callout: none; 37 | -webkit-user-select: none; 38 | } 39 | 40 | img.button:hover { 41 | border: 4px solid black; 42 | } 43 | 44 | img.button:active { 45 | border: 4px solid red; 46 | } 47 | 48 | img#ArrowUp, 49 | img#ArrowDown { 50 | margin-left: 137px; 51 | } 52 | 53 | img#ArrowLeft { 54 | } 55 | 56 | img.Clockwise { 57 | width: 75px; 58 | height: 75px; 59 | } 60 | 61 | img.CounterClockwise { 62 | -moz-transform: scaleX(-1); 63 | -o-transform: scaleX(-1); 64 | -webkit-transform: scaleX(-1); 65 | transform: scaleX(-1); 66 | filter: FlipH; 67 | -ms-filter: "FlipH"; 68 | width: 75px; 69 | height: 75px; 70 | } 71 | 72 | div#desktop-interface { 73 | width: 520px; 74 | float: left; 75 | padding-top: 100px; 76 | text-align: center; 77 | } 78 | 79 | div#mobile-interface { 80 | width: 300px; 81 | padding-left: 600px; 82 | padding-top: 100px; 83 | text-align: center; 84 | } 85 | 86 | div#medium-motor-spin { 87 | width: 250px; 88 | margin-left: 300px; 89 | text-align: center; 90 | } 91 | 92 | div#desktop-medium-motor-spin { 93 | width: 250px; 94 | margin-left: 450px; 95 | text-align: center; 96 | } 97 | 98 | div#tank-speed, 99 | div#medium-motor-speed { 100 | margin-top: 20px; 101 | margin-bottom: 10px; 102 | } 103 | 104 | div#joystick-wrapper { 105 | float: left; 106 | position: fixed; 107 | top: 5px; 108 | left: 10px; 109 | width: 250px; 110 | height: 250px; 111 | -webkit-border-radius: 125px; 112 | -moz-border-radius: 125px; 113 | border-radius: 125px; 114 | background: #848484; 115 | } 116 | 117 | div#joystick { 118 | width: 50px; 119 | height: 50px; 120 | -webkit-border-radius: 25px; 121 | -moz-border-radius: 25px; 122 | border-radius: 25px; 123 | background: black; 124 | cursor: pointer; 125 | position: relative; 126 | top: 100px; 127 | left: 100px 128 | } 129 | 130 | -------------------------------------------------------------------------------- /robots/EV3D4/include/tank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ev3dev/ev3dev-lang-python-demo/99eb25392ff350a2f9a3c180c3892c29d1fa5a21/robots/EV3D4/include/tank.png -------------------------------------------------------------------------------- /robots/EV3D4/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Lego Tank 8 | 9 | 10 |
11 | 13 | 14 |
15 | Desktop 16 |

Desktop Interface

17 |
18 | 19 |
20 | Mobile 21 |

Mobile Interface

22 |
23 | 24 |
25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /robots/EV3D4/mobile.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Lego Tank 14 | 15 | 16 | 18 | 19 |
20 |
21 |
22 |
23 | 24 |
25 | 26 | 27 |
28 |
29 |
30 |
31 |
32 | 33 | 34 | -------------------------------------------------------------------------------- /robots/EV3RSTORM/README.md: -------------------------------------------------------------------------------- 1 | EV3RSTORM 2 | ========= 3 | 4 | EV3RSTORM is the most advanced of the LEGO(r) MINDSTORMS(r) Robots. 5 | Equipped with a blasting bazooka and a spinning tri-blade, EV3RSTORM is 6 | superior in both intelligence as well as in fighting power. 7 | 8 | Our version, being built with ev3dev, is also vastly more intelligent (one 9 | could say, it has a [brain size of a planet](https://en.wikipedia.org/wiki/Marvin_(character))) 10 | so it may be afflicted with severe depression and boredom at times. 11 | 12 | The build instructions may be found at the official LEGO MINDSTROMS site 13 | [here](http://www.lego.com/en-us/mindstorms/build-a-robot/ev3rstorm). 14 | 15 | -------------------------------------------------------------------------------- /robots/EV3RSTORM/ev3rstorm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import time, random 4 | import ev3dev.ev3 as ev3 5 | 6 | random.seed( time.time() ) 7 | 8 | def quote(topic): 9 | """ 10 | Recite a random Marvin the Paranoid Android quote on the specified topic. 11 | See https://en.wikipedia.org/wiki/Marvin_(character) 12 | """ 13 | 14 | marvin_quotes = { 15 | 'initiating' : ( 16 | "Life? Don't talk to me about life!", 17 | "Now I've got a headache.", 18 | "This will all end in tears.", 19 | ), 20 | 'depressed' : ( 21 | "I think you ought to know I'm feeling very depressed.", 22 | "Incredible... it's even worse than I thought it would be.", 23 | "I'd make a suggestion, but you wouldn't listen.", 24 | ), 25 | } 26 | 27 | ev3.Sound.speak(random.choice(marvin_quotes[topic])).wait() 28 | 29 | def check(condition, message): 30 | """ 31 | Check that condition is true, 32 | loudly complain and throw an exception otherwise. 33 | """ 34 | if not condition: 35 | ev3.Sound.speak(message).wait() 36 | quote('depressed') 37 | raise Exception(message) 38 | 39 | class ev3rstorm: 40 | def __init__(self): 41 | # Connect the required equipement 42 | self.lm = ev3.LargeMotor('outB') 43 | self.rm = ev3.LargeMotor('outC') 44 | self.mm = ev3.MediumMotor() 45 | 46 | self.ir = ev3.InfraredSensor() 47 | self.ts = ev3.TouchSensor() 48 | self.cs = ev3.ColorSensor() 49 | 50 | self.screen = ev3.Screen() 51 | 52 | # Reset the motors 53 | for m in (self.lm, self.rm, self.mm): 54 | m.reset() 55 | m.position = 0 56 | m.stop_action = 'brake' 57 | 58 | self.draw_face() 59 | 60 | quote('initiating') 61 | 62 | def draw_face(self): 63 | w,h = self.screen.shape 64 | y = h // 2 65 | 66 | eye_xrad = 20 67 | eye_yrad = 30 68 | 69 | pup_xrad = 10 70 | pup_yrad = 10 71 | 72 | def draw_eye(x): 73 | self.screen.draw.ellipse((x-eye_xrad, y-eye_yrad, x+eye_xrad, y+eye_yrad)) 74 | self.screen.draw.ellipse((x-pup_xrad, y-pup_yrad, x+pup_xrad, y+pup_yrad), fill='black') 75 | 76 | draw_eye(w//3) 77 | draw_eye(2*w//3) 78 | 79 | self.screen.update() 80 | 81 | def shoot(self, direction='up'): 82 | """ 83 | Shot a ball in the specified direction (valid choices are 'up' and 'down') 84 | """ 85 | self.mm.run_to_rel_pos(speed_sp=900, position_sp=(-1080 if direction == 'up' else 1080)) 86 | while 'running' in self.mm.state: 87 | time.sleep(0.1) 88 | 89 | def rc_loop(self): 90 | """ 91 | Enter the remote control loop. RC buttons on channel 1 control the 92 | robot movement, channel 2 is for shooting things. 93 | The loop ends when the touch sensor is pressed. 94 | """ 95 | 96 | def roll(motor, led_group, speed): 97 | """ 98 | Generate remote control event handler. It rolls given motor into 99 | given direction (1 for forward, -1 for backward). When motor rolls 100 | forward, the given led group flashes green, when backward -- red. 101 | When motor stops, the leds are turned off. 102 | 103 | The on_press function has signature required by RemoteControl 104 | class. It takes boolean state parameter; True when button is 105 | pressed, False otherwise. 106 | """ 107 | def on_press(state): 108 | if state: 109 | # Roll when button is pressed 110 | motor.run_forever(speed_sp=speed) 111 | ev3.Leds.set_color(led_group, 112 | ev3.Leds.GREEN if speed > 0 else ev3.Leds.RED) 113 | else: 114 | # Stop otherwise 115 | motor.stop() 116 | ev3.Leds.set_color(led_group, ev3.Leds.BLACK) 117 | 118 | return on_press 119 | 120 | rc1 = ev3.RemoteControl(self.ir, 1) 121 | rc1.on_red_up = roll(self.lm, ev3.Leds.LEFT, 900) 122 | rc1.on_red_down = roll(self.lm, ev3.Leds.LEFT, -900) 123 | rc1.on_blue_up = roll(self.rm, ev3.Leds.RIGHT, 900) 124 | rc1.on_blue_down = roll(self.rm, ev3.Leds.RIGHT, -900) 125 | 126 | 127 | def shoot(direction): 128 | def on_press(state): 129 | if state: self.shoot(direction) 130 | return on_press 131 | 132 | rc2 = ev3.RemoteControl(self.ir, 2) 133 | rc2.on_red_up = shoot('up') 134 | rc2.on_blue_up = shoot('up') 135 | rc2.on_red_down = shoot('down') 136 | rc2.on_blue_down = shoot('down') 137 | 138 | # Now that the event handlers are assigned, 139 | # lets enter the processing loop: 140 | while not self.ts.is_pressed: 141 | rc1.process() 142 | rc2.process() 143 | time.sleep(0.1) 144 | 145 | 146 | if __name__ == '__main__': 147 | Marvin = ev3rstorm() 148 | Marvin.rc_loop() 149 | -------------------------------------------------------------------------------- /robots/EXPLOR3R/README.rst: -------------------------------------------------------------------------------- 1 | The examples in this folder, unless stated otherwise, are based on Explor3r 2 | robot by Laurens Valk. The assembling instructions for the robot may be found 3 | here: http://robotsquare.com/2015/10/06/explor3r-building-instructions. 4 | -------------------------------------------------------------------------------- /robots/EXPLOR3R/auto-drive.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # ----------------------------------------------------------------------------- 4 | # Copyright (c) 2015 Denis Demidov 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | # THE SOFTWARE. 23 | # ----------------------------------------------------------------------------- 24 | 25 | # In this demo an Explor3r robot with touch sensor attachement drives 26 | # autonomously. It drives forward until an obstacle is bumped (determined by 27 | # the touch sensor), then turns in a random direction and continues. The robot 28 | # slows down when it senses obstacle ahead (with the infrared sensor). 29 | # 30 | # The program may be stopped by pressing any button on the brick. 31 | # 32 | # This demonstrates usage of motors, sound, sensors, buttons, and leds. 33 | 34 | from time import sleep 35 | from random import choice, randint 36 | 37 | from ev3dev2.motor import OUTPUT_B, OUTPUT_C, LargeMotor 38 | from ev3dev2.sensor.lego import InfraredSensor, TouchSensor 39 | from ev3dev2.button import Button 40 | from ev3dev2.led import Leds 41 | from ev3dev2.sound import Sound 42 | 43 | # Connect two large motors on output ports B and C: 44 | motors = [LargeMotor(address) for address in (OUTPUT_B, OUTPUT_C)] 45 | 46 | # Connect infrared and touch sensors. 47 | ir = InfraredSensor() 48 | ts = TouchSensor() 49 | 50 | print('Robot Starting') 51 | 52 | # We will need to check EV3 buttons state. 53 | btn = Button() 54 | 55 | def start(): 56 | """ 57 | Start both motors. `run-direct` command will allow to vary motor 58 | performance on the fly by adjusting `duty_cycle_sp` attribute. 59 | """ 60 | for m in motors: 61 | m.run_direct() 62 | 63 | def backup(): 64 | """ 65 | Back away from an obstacle. 66 | """ 67 | 68 | # Sound backup alarm. 69 | spkr = Sound() 70 | spkr.tone([(1000, 500, 500)] * 3) 71 | 72 | # Turn backup lights on: 73 | leds = Leds() 74 | 75 | for light in ('LEFT', 'RIGHT'): 76 | leds.set_color(light, 'RED') 77 | 78 | # Stop both motors and reverse for 1.5 seconds. 79 | # `run-timed` command will return immediately, so we will have to wait 80 | # until both motors are stopped before continuing. 81 | for m in motors: 82 | m.stop(stop_action='brake') 83 | m.run_timed(speed_sp=-500, time_sp=1500) 84 | 85 | # When motor is stopped, its `state` attribute returns empty list. 86 | # Wait until both motors are stopped: 87 | while any(m.state for m in motors): 88 | sleep(0.1) 89 | 90 | # Turn backup lights off: 91 | for light in ('LEFT', 'RIGHT'): 92 | leds.set_color(light, 'GREEN') 93 | 94 | 95 | def turn(): 96 | """ 97 | Turn the robot in random direction. 98 | """ 99 | 100 | # We want to turn the robot wheels in opposite directions from 1/4 to 3/4 101 | # of a second. Use `random.choice()` to decide which wheel will turn which 102 | # way. 103 | power = choice([(1, -1), (-1, 1)]) 104 | t = randint(250, 750) 105 | 106 | for m, p in zip(motors, power): 107 | m.run_timed(speed_sp = p * 750, time_sp = t) 108 | 109 | # Wait until both motors are stopped: 110 | while any(m.state for m in motors): 111 | sleep(0.1) 112 | 113 | # Run the robot until a button is pressed. 114 | start() 115 | while not btn.any(): 116 | 117 | if ts.is_pressed: 118 | # We bumped an obstacle. 119 | # Back away, turn and go in other direction. 120 | backup() 121 | turn() 122 | start() 123 | 124 | # Infrared sensor in proximity mode will measure distance to the closest 125 | # object in front of it. 126 | distance = ir.proximity 127 | 128 | if distance > 60: 129 | # Path is clear, run at full speed. 130 | dc = 95 131 | else: 132 | # Obstacle ahead, slow down. 133 | dc = 30 134 | 135 | for m in motors: 136 | m.duty_cycle_sp = dc 137 | 138 | sleep(0.1) 139 | 140 | # Stop the motors before exiting. 141 | for m in motors: 142 | m.stop() 143 | -------------------------------------------------------------------------------- /robots/EXPLOR3R/remote-control.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # ----------------------------------------------------------------------------- 4 | # Copyright (c) 2015 Denis Demidov 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | # THE SOFTWARE. 23 | # ----------------------------------------------------------------------------- 24 | 25 | # This demo shows how to remote control an Explor3r robot with touch sensor 26 | # attachment. 27 | # 28 | # Red buttons control left motor, blue buttons control right motor. 29 | # Leds are used to indicate movement direction. 30 | # Whenever an obstacle is bumped, robot backs away and apologises. 31 | 32 | from time import sleep 33 | from ev3dev2.motor import OUTPUT_B, OUTPUT_C, LargeMotor 34 | from ev3dev2.sensor.lego import InfraredSensor, TouchSensor 35 | from ev3dev2.button import Button 36 | from ev3dev2.led import Leds 37 | from ev3dev2.sound import Sound 38 | 39 | # Connect two large motors on output ports B and C 40 | lmotor, rmotor = [LargeMotor(address) for address in (OUTPUT_B, OUTPUT_C)] 41 | 42 | # Connect touch sensor and remote control 43 | ts = TouchSensor() 44 | rc = InfraredSensor() 45 | 46 | # Initialize button handler 47 | button = Button() 48 | 49 | # Turn leds off 50 | leds = Leds() 51 | leds.all_off() 52 | 53 | spkr = Sound() 54 | 55 | def roll(motor, led_group, direction): 56 | """ 57 | Generate remote control event handler. It rolls given motor into given 58 | direction (1 for forward, -1 for backward). When motor rolls forward, the 59 | given led group flashes green, when backward -- red. When motor stops, the 60 | leds are turned off. 61 | 62 | The on_press function has signature required by RemoteControl class. 63 | It takes boolean state parameter; True when button is pressed, False 64 | otherwise. 65 | """ 66 | def on_press(state): 67 | if state: 68 | # Roll when button is pressed 69 | motor.run_forever(speed_sp=600*direction) 70 | leds.set_color(led_group, 'GREEN') 71 | else: 72 | # Stop otherwise 73 | motor.stop(stop_action='brake') 74 | leds.all_off() 75 | 76 | return on_press 77 | 78 | # Assign event handler to each of the remote buttons 79 | rc.on_channel1_top_left = roll(lmotor, 'LEFT', 1) 80 | rc.on_channel1_bottom_left = roll(lmotor, 'LEFT', -1) 81 | rc.on_channel1_top_right = roll(rmotor, 'RIGHT', 1) 82 | rc.on_channel1_bottom_right = roll(rmotor, 'RIGHT', -1) 83 | print("Robot Starting") 84 | 85 | # Enter event processing loop 86 | while not button.any(): 87 | rc.process() 88 | 89 | # Backup when bumped an obstacle 90 | if ts.is_pressed: 91 | spkr.speak('Oops, excuse me!') 92 | 93 | for motor in (lmotor, rmotor): 94 | motor.stop(stop_action='brake') 95 | 96 | # Turn red lights on 97 | for light in ('LEFT', 'RIGHT'): 98 | leds.set_color(light, 'RED') 99 | 100 | # Run both motors backwards for 0.5 seconds 101 | for motor in (lmotor, rmotor): 102 | motor.run_timed(speed_sp=-600, time_sp=500) 103 | 104 | # Wait 0.5 seconds while motors are rolling 105 | sleep(0.5) 106 | 107 | leds.all_off() 108 | 109 | sleep(0.01) 110 | -------------------------------------------------------------------------------- /robots/GRIPP3R/GRIPP3R.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | Implementation of GRIPP3R 5 | 6 | - use the remote control on channel 1 to drive the robot 7 | 8 | - use the remote control on channel 4 to open/close the claw 9 | - press the top left button to close 10 | - press the bottom left button to open 11 | 12 | - If GRIPP3R drives into something solid enough to press the 13 | TouchSensor underneath the claw will close. Once close the 14 | remote control must be used to open it. 15 | """ 16 | 17 | import logging 18 | import signal 19 | import sys 20 | from ev3dev2.motor import OUTPUT_A, OUTPUT_B, OUTPUT_C, MediumMotor 21 | from ev3dev2.control.rc_tank import RemoteControlledTank 22 | from ev3dev2.sensor.lego import TouchSensor 23 | from threading import Thread, Event 24 | from time import sleep 25 | 26 | log = logging.getLogger(__name__) 27 | 28 | 29 | class MonitorTouchSensor(Thread): 30 | """ 31 | A thread to monitor Gripper's TouchSensor and close the gripper when 32 | the TouchSensor is pressed 33 | """ 34 | 35 | def __init__(self, parent): 36 | Thread.__init__(self) 37 | self.parent = parent 38 | self.shutdown_event = Event() 39 | self.monitor_ts = Event() 40 | 41 | def __str__(self): 42 | return "MonitorTouchSensor" 43 | 44 | def run(self): 45 | 46 | while True: 47 | 48 | if self.monitor_ts.is_set() and self.parent.ts.is_released: 49 | 50 | # We only wait for 1000ms so that we can wake up to see if 51 | # our shutdown_event has been set 52 | if self.parent.ts.wait_for_pressed(timeout_ms=1000): 53 | self.parent.claw_close(True) 54 | 55 | if self.shutdown_event.is_set(): 56 | log.info('%s: shutdown_event is set' % self) 57 | break 58 | 59 | 60 | class MonitorRemoteControl(Thread): 61 | """ 62 | A thread to monitor Gripper's InfraredSensor and process signals 63 | from the remote control 64 | """ 65 | 66 | def __init__(self, parent): 67 | Thread.__init__(self) 68 | self.parent = parent 69 | self.shutdown_event = Event() 70 | self.monitor_ts = Event() 71 | 72 | def __str__(self): 73 | return "MonitorRemoteControl" 74 | 75 | def run(self): 76 | 77 | while True: 78 | self.parent.remote.process() 79 | sleep(0.01) 80 | 81 | if self.shutdown_event.is_set(): 82 | log.info('%s: shutdown_event is set' % self) 83 | break 84 | 85 | 86 | class Gripper(RemoteControlledTank): 87 | """ 88 | To enable the medium motor toggle the beacon button on the EV3 remote. 89 | """ 90 | CLAW_DEGREES_OPEN = 225 91 | CLAW_DEGREES_CLOSE = 920 92 | CLAW_SPEED_PCT = 50 93 | 94 | def __init__(self, left_motor_port=OUTPUT_B, right_motor_port=OUTPUT_C, medium_motor_port=OUTPUT_A): 95 | RemoteControlledTank.__init__(self, left_motor_port, right_motor_port) 96 | self.set_polarity(MediumMotor.POLARITY_NORMAL) 97 | self.medium_motor = MediumMotor(medium_motor_port) 98 | self.ts = TouchSensor() 99 | self.mts = MonitorTouchSensor(self) 100 | self.mrc = MonitorRemoteControl(self) 101 | self.shutdown_event = Event() 102 | 103 | # Register our signal_term_handler() to be called if the user sends 104 | # a 'kill' to this process or does a Ctrl-C from the command line 105 | signal.signal(signal.SIGTERM, self.signal_term_handler) 106 | signal.signal(signal.SIGINT, self.signal_int_handler) 107 | 108 | self.claw_open(True) 109 | self.remote.on_channel4_top_left = self.claw_close 110 | self.remote.on_channel4_bottom_left = self.claw_open 111 | 112 | def shutdown_robot(self): 113 | 114 | if self.shutdown_event.is_set(): 115 | return 116 | 117 | self.shutdown_event.set() 118 | log.info('shutting down') 119 | self.mts.shutdown_event.set() 120 | self.mrc.shutdown_event.set() 121 | self.remote.on_channel4_top_left = None 122 | self.remote.on_channel4_bottom_left = None 123 | self.left_motor.off(brake=False) 124 | self.right_motor.off(brake=False) 125 | self.medium_motor.off(brake=False) 126 | self.mts.join() 127 | self.mrc.join() 128 | 129 | def signal_term_handler(self, signal, frame): 130 | log.info('Caught SIGTERM') 131 | self.shutdown_robot() 132 | 133 | def signal_int_handler(self, signal, frame): 134 | log.info('Caught SIGINT') 135 | self.shutdown_robot() 136 | 137 | def claw_open(self, state): 138 | if state: 139 | 140 | # Clear monitor_ts while we are opening the claw. We do this because 141 | # the act of opening the claw presses the TouchSensor so we must 142 | # tell mts to ignore that press. 143 | self.mts.monitor_ts.clear() 144 | self.medium_motor.on(speed=self.CLAW_SPEED_PCT * -1, block=True) 145 | self.medium_motor.off() 146 | self.medium_motor.reset() 147 | self.medium_motor.on_to_position(speed=self.CLAW_SPEED_PCT, 148 | position=self.CLAW_DEGREES_OPEN, 149 | brake=False, block=True) 150 | self.mts.monitor_ts.set() 151 | 152 | def claw_close(self, state): 153 | if state: 154 | self.medium_motor.on_to_position(speed=self.CLAW_SPEED_PCT, 155 | position=self.CLAW_DEGREES_CLOSE) 156 | 157 | def main(self): 158 | self.mts.start() 159 | self.mrc.start() 160 | self.shutdown_event.wait() 161 | 162 | 163 | if __name__ == '__main__': 164 | 165 | # Change level to logging.DEBUG for more details 166 | logging.basicConfig(level=logging.INFO, 167 | format="%(asctime)s %(levelname)5s %(filename)s: %(message)s") 168 | log = logging.getLogger(__name__) 169 | 170 | # Color the errors and warnings in red 171 | logging.addLevelName(logging.ERROR, "\033[91m %s\033[0m" % logging.getLevelName(logging.ERROR)) 172 | logging.addLevelName(logging.WARNING, "\033[91m%s\033[0m" % logging.getLevelName(logging.WARNING)) 173 | 174 | log.info("Starting GRIPP3R") 175 | gripper = Gripper() 176 | gripper.main() 177 | log.info("Exiting GRIPP3R") 178 | -------------------------------------------------------------------------------- /robots/MINDCUB3R/.gitignore: -------------------------------------------------------------------------------- 1 | cache 2 | max_rgb.txt 3 | -------------------------------------------------------------------------------- /robots/MINDCUB3R/README.md: -------------------------------------------------------------------------------- 1 | # MINDCUB3R 2 | 3 | A working fork of cavenel's original code ([original video](https://www.youtube.com/watch?v=HuKsfp19yF0)). 4 | 5 | ## Installation 6 | ### Installing kociemba 7 | The kociemba program produces a sequence of moves used to solve 8 | a 3x3x3 rubiks cube. 9 | ``` 10 | $ sudo apt-get update 11 | $ sudo apt-get install build-essential libffi-dev 12 | $ cd ~/ 13 | $ git clone https://github.com/dwalton76/kociemba.git 14 | $ cd ~/kociemba/kociemba/ckociemba/ 15 | $ make 16 | $ sudo make install 17 | ``` 18 | 19 | ### Installing rubiks-color-resolver 20 | When the cube is scanned we get the RGB (red, green, blue) value for 21 | all 54 squares of a 3x3x3 cube. rubiks-color-resolver analyzes those RGB 22 | values to determine which of the six possible cube colors is the color for 23 | each square. 24 | ``` 25 | $ sudo apt-get install python3-pip 26 | $ sudo pip3 install git+https://github.com/dwalton76/rubiks-color-resolver.git 27 | ``` 28 | 29 | ### Installing the MINDCUB3R demo 30 | We must git clone the ev3dev-lang-python repository. MINDCUB3R is included 31 | in the demo directory. 32 | ``` 33 | $ cd ~/ 34 | $ git clone https://github.com/ev3dev/ev3dev-lang-python-demo.git 35 | $ cd ~/ev3dev-lang-python-demo/robots/MINDCUB3R/ 36 | $ kociemba DRLUUBFBRBLURRLRUBLRDDFDLFUFUFFDBRDUBRUFLLFDDBFLUBLRBD 37 | ``` 38 | 39 | ## Running MINDCUB3R 40 | ``` 41 | $ cd ~/ev3dev-lang-python-demo/robots/MINDCUB3R/ 42 | $ ./mindcuber.py 43 | ``` 44 | It is also a good idea to launch white calibration every time you move robot to a different lightning. 45 | ``` 46 | $ cd ~/ev3dev-lang-python-demo/robots/MINDCUB3R/ 47 | $ ./calibrate_white.py 48 | ``` 49 | 50 | ## About kociemba 51 | You may have noticed that the 52 | `kociemba DRLUUBFBRBLURRLRUBLRDDFDLFUFUFFDBRDUBRUFLLFDDBFLUBLRBD` 53 | step of the install looks a little odd. The "DRLUU..." string is a 54 | representation of the colors of each of the 54 squares of a 3x3x3 cube. So 55 | the D at the beginning means that square `#1` is the same color as the middle 56 | square of the Down side (the bottom), the R means that square `#2` is the same 57 | color as the middle square of the Right side, etc. The kociemba program takes 58 | that color data and returns a sequence of moves that can be used to solve the 59 | cube. 60 | 61 | ``` 62 | $ kociemba DRLUUBFBRBLURRLRUBLRDDFDLFUFUFFDBRDUBRUFLLFDDBFLUBLRBD 63 | D2 R' D' F2 B D R2 D2 R' F2 D' F2 U' B2 L2 U2 D R2 U 64 | $ 65 | ``` 66 | 67 | Running the kociemba program is part of the install process because the first 68 | time you run it, it takes about 30 seconds to build a series of tables that 69 | it caches to the filesystem. After that first run it is nice and fast. 70 | -------------------------------------------------------------------------------- /robots/MINDCUB3R/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ev3dev/ev3dev-lang-python-demo/99eb25392ff350a2f9a3c180c3892c29d1fa5a21/robots/MINDCUB3R/__init__.py -------------------------------------------------------------------------------- /robots/MINDCUB3R/calibrate_white.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | This program is used to measure the white center of your rubiks cube to 5 | calibrate the color sensor as to what "white" is. Place the white center 6 | of your 3x3x3 rubiks cube facing up and run this program. It will only 7 | scan the white center cube and will then write the red/green/blue values 8 | to a max_rgb.txt file. When mindcuber.py runs it will look for the 9 | max_rgb.txt file and if it exists will read in the values to calibrate 10 | the color sensor. 11 | 12 | If you move to a different room with different lighting it is a good idea 13 | to run this program again. 14 | """ 15 | 16 | from mindcuber import MindCuber 17 | import logging 18 | import sys 19 | 20 | 21 | # logging.basicConfig(filename='rubiks.log', 22 | logging.basicConfig(level=logging.INFO, 23 | format='%(asctime)s %(filename)12s %(levelname)8s: %(message)s') 24 | log = logging.getLogger(__name__) 25 | 26 | # Color the errors and warnings in red 27 | logging.addLevelName(logging.ERROR, "\033[91m %s\033[0m" % logging.getLevelName(logging.ERROR)) 28 | logging.addLevelName(logging.WARNING, "\033[91m %s\033[0m" % logging.getLevelName(logging.WARNING)) 29 | 30 | mcube = MindCuber() 31 | 32 | try: 33 | mcube.wait_for_cube_insert() 34 | 35 | # Push the cube to the right so that it is in the expected 36 | # position when we begin scanning 37 | mcube.flipper_hold_cube(100) 38 | mcube.flipper_away(100) 39 | 40 | # Scan the middle square 41 | mcube.colorarm_middle() 42 | mcube.color_sensor.calibrate_white() 43 | 44 | with open('max_rgb.txt', 'w') as fh: 45 | fh.write("red %s\n" % mcube.color_sensor.red_max) 46 | fh.write("green %s\n" % mcube.color_sensor.green_max) 47 | fh.write("blue %s\n" % mcube.color_sensor.blue_max) 48 | 49 | mcube.colorarm_remove() 50 | mcube.shutdown_robot() 51 | 52 | except Exception as e: 53 | log.exception(e) 54 | mcube.shutdown_robot() 55 | sys.exit(1) 56 | -------------------------------------------------------------------------------- /robots/MINDCUB3R/mindcuber.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from ev3dev2.motor import LargeMotor, MediumMotor, OUTPUT_A, OUTPUT_B, OUTPUT_C, SpeedDPS 4 | from ev3dev2.sensor.lego import ColorSensor, InfraredSensor 5 | from pprint import pformat 6 | from rubikscolorresolver import RubiksColorSolverGeneric 7 | from subprocess import check_output 8 | from time import sleep 9 | import json 10 | import logging 11 | import os 12 | import signal 13 | import sys 14 | import time 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | class ScanError(Exception): 20 | pass 21 | 22 | 23 | class MindCuber(object): 24 | scan_order = [ 25 | 5, 9, 6, 3, 2, 1, 4, 7, 8, 26 | 23, 27, 24, 21, 20, 19, 22, 25, 26, 27 | 50, 54, 51, 48, 47, 46, 49, 52, 53, 28 | 14, 10, 13, 16, 17, 18, 15, 12, 11, 29 | 41, 43, 44, 45, 42, 39, 38, 37, 40, 30 | 32, 34, 35, 36, 33, 30, 29, 28, 31] 31 | 32 | hold_cube_pos = 85 33 | rotate_speed = 400 34 | flip_speed = 300 35 | flip_speed_push = 400 36 | 37 | def __init__(self): 38 | self.shutdown = False 39 | self.flipper = LargeMotor(OUTPUT_A) 40 | self.turntable = LargeMotor(OUTPUT_B) 41 | self.colorarm = MediumMotor(OUTPUT_C) 42 | self.color_sensor = ColorSensor() 43 | self.color_sensor.mode = self.color_sensor.MODE_RGB_RAW 44 | self.infrared_sensor = InfraredSensor() 45 | self.init_motors() 46 | self.state = ['U', 'D', 'F', 'L', 'B', 'R'] 47 | self.rgb_solver = None 48 | signal.signal(signal.SIGTERM, self.signal_term_handler) 49 | signal.signal(signal.SIGINT, self.signal_int_handler) 50 | 51 | filename_max_rgb = 'max_rgb.txt' 52 | 53 | if os.path.exists(filename_max_rgb): 54 | with open(filename_max_rgb, 'r') as fh: 55 | for line in fh: 56 | (color, value) = line.strip().split() 57 | 58 | if color == 'red': 59 | self.color_sensor.red_max = int(value) 60 | log.info("red max is %d" % self.color_sensor.red_max) 61 | elif color == 'green': 62 | self.color_sensor.green_max = int(value) 63 | log.info("green max is %d" % self.color_sensor.green_max) 64 | elif color == 'blue': 65 | self.color_sensor.blue_max = int(value) 66 | log.info("blue max is %d" % self.color_sensor.blue_max) 67 | 68 | def init_motors(self): 69 | 70 | for x in (self.flipper, self.turntable, self.colorarm): 71 | x.reset() 72 | 73 | log.info("Initialize flipper %s" % self.flipper) 74 | self.flipper.on(SpeedDPS(-50), block=True) 75 | self.flipper.off() 76 | self.flipper.reset() 77 | 78 | log.info("Initialize colorarm %s" % self.colorarm) 79 | self.colorarm.on(SpeedDPS(500), block=True) 80 | self.colorarm.off() 81 | self.colorarm.reset() 82 | 83 | log.info("Initialize turntable %s" % self.turntable) 84 | self.turntable.off() 85 | self.turntable.reset() 86 | 87 | def shutdown_robot(self): 88 | log.info('Shutting down') 89 | self.shutdown = True 90 | 91 | if self.rgb_solver: 92 | self.rgb_solver.shutdown = True 93 | 94 | for x in (self.flipper, self.turntable, self.colorarm): 95 | # We are shutting down so do not 'hold' the motors 96 | x.stop_action = 'brake' 97 | x.off(False) 98 | 99 | def signal_term_handler(self, signal, frame): 100 | log.error('Caught SIGTERM') 101 | self.shutdown_robot() 102 | 103 | def signal_int_handler(self, signal, frame): 104 | log.error('Caught SIGINT') 105 | self.shutdown_robot() 106 | 107 | def apply_transformation(self, transformation): 108 | self.state = [self.state[t] for t in transformation] 109 | 110 | def rotate_cube(self, direction, nb): 111 | current_pos = self.turntable.position 112 | final_pos = 135 * round((self.turntable.position + (270 * direction * nb)) / 135.0) 113 | log.info("rotate_cube() direction %s, nb %s, current_pos %d, final_pos %d" % (direction, nb, current_pos, final_pos)) 114 | 115 | if self.flipper.position > 35: 116 | self.flipper_away() 117 | 118 | self.turntable.on_to_position(SpeedDPS(MindCuber.rotate_speed), final_pos) 119 | 120 | if nb >= 1: 121 | for i in range(nb): 122 | if direction > 0: 123 | transformation = [0, 1, 5, 2, 3, 4] 124 | else: 125 | transformation = [0, 1, 3, 4, 5, 2] 126 | self.apply_transformation(transformation) 127 | 128 | def rotate_cube_1(self): 129 | self.rotate_cube(1, 1) 130 | 131 | def rotate_cube_2(self): 132 | self.rotate_cube(1, 2) 133 | 134 | def rotate_cube_3(self): 135 | self.rotate_cube(-1, 1) 136 | 137 | def rotate_cube_blocked(self, direction, nb): 138 | 139 | # Move the arm down to hold the cube in place 140 | self.flipper_hold_cube() 141 | 142 | # OVERROTATE depends on lot on MindCuber.rotate_speed 143 | current_pos = self.turntable.position 144 | OVERROTATE = 18 145 | final_pos = int(135 * round((current_pos + (270 * direction * nb)) / 135.0)) 146 | temp_pos = int(final_pos + (OVERROTATE * direction)) 147 | 148 | log.info("rotate_cube_blocked() direction %s nb %s, current pos %s, temp pos %s, final pos %s" % 149 | (direction, nb, current_pos, temp_pos, final_pos)) 150 | 151 | self.turntable.on_to_position(SpeedDPS(MindCuber.rotate_speed), temp_pos) 152 | self.turntable.on_to_position(SpeedDPS(MindCuber.rotate_speed/4), final_pos) 153 | 154 | def rotate_cube_blocked_1(self): 155 | self.rotate_cube_blocked(1, 1) 156 | 157 | def rotate_cube_blocked_2(self): 158 | self.rotate_cube_blocked(1, 2) 159 | 160 | def rotate_cube_blocked_3(self): 161 | self.rotate_cube_blocked(-1, 1) 162 | 163 | def flipper_hold_cube(self, speed=300): 164 | current_position = self.flipper.position 165 | 166 | # Push it forward so the cube is always in the same position 167 | # when we start the flip 168 | if (current_position <= MindCuber.hold_cube_pos - 10 or 169 | current_position >= MindCuber.hold_cube_pos + 10): 170 | 171 | self.flipper.ramp_down_sp=400 172 | self.flipper.on_to_position(SpeedDPS(speed), MindCuber.hold_cube_pos) 173 | sleep(0.05) 174 | 175 | def flipper_away(self, speed=300): 176 | """ 177 | Move the flipper arm out of the way 178 | """ 179 | log.info("flipper_away()") 180 | self.flipper.ramp_down_sp = 400 181 | self.flipper.on_to_position(SpeedDPS(speed), 0) 182 | 183 | def flip(self): 184 | """ 185 | Motors will sometimes stall if you call on_to_position() multiple 186 | times back to back on the same motor. To avoid this we call a 50ms 187 | sleep in flipper_hold_cube() and after each on_to_position() below. 188 | 189 | We have to sleep after the 2nd on_to_position() because sometimes 190 | flip() is called back to back. 191 | """ 192 | log.info("flip()") 193 | 194 | if self.shutdown: 195 | return 196 | 197 | # Move the arm down to hold the cube in place 198 | self.flipper_hold_cube() 199 | 200 | # Grab the cube and pull back 201 | self.flipper.ramp_up_sp = 200 202 | self.flipper.ramp_down_sp = 0 203 | self.flipper.on_to_position(SpeedDPS(self.flip_speed), 190) 204 | sleep(0.05) 205 | 206 | # At this point the cube is at an angle, push it forward to 207 | # drop it back down in the turntable 208 | self.flipper.ramp_up_sp = 200 209 | self.flipper.ramp_down_sp = 400 210 | self.flipper.on_to_position(SpeedDPS(self.flip_speed_push), MindCuber.hold_cube_pos) 211 | sleep(0.05) 212 | 213 | transformation = [2, 4, 1, 3, 0, 5] 214 | self.apply_transformation(transformation) 215 | 216 | def colorarm_middle(self): 217 | log.info("colorarm_middle()") 218 | self.colorarm.on_to_position(SpeedDPS(600), -750) 219 | 220 | def colorarm_corner(self, square_index): 221 | """ 222 | The lower the number the closer to the center 223 | """ 224 | log.info("colorarm_corner(%d)" % square_index) 225 | position_target = -580 226 | 227 | if square_index == 1: 228 | position_target -= 10 229 | 230 | elif square_index == 3: 231 | position_target -= 30 232 | 233 | elif square_index == 5: 234 | position_target -= 20 235 | 236 | elif square_index == 7: 237 | pass 238 | 239 | else: 240 | raise ScanError("colorarm_corner was given unsupported square_index %d" % square_index) 241 | 242 | self.colorarm.on_to_position(SpeedDPS(600), position_target) 243 | 244 | def colorarm_edge(self, square_index): 245 | """ 246 | The lower the number the closer to the center 247 | """ 248 | log.info("colorarm_edge(%d)" % square_index) 249 | position_target = -640 250 | 251 | if square_index == 2: 252 | position_target -= 20 253 | 254 | elif square_index == 4: 255 | position_target -= 40 256 | 257 | elif square_index == 6: 258 | position_target -= 20 259 | 260 | elif square_index == 8: 261 | pass 262 | 263 | else: 264 | raise ScanError("colorarm_edge was given unsupported square_index %d" % square_index) 265 | 266 | self.colorarm.on_to_position(SpeedDPS(600), position_target) 267 | 268 | def colorarm_remove(self): 269 | log.info("colorarm_remove()") 270 | self.colorarm.on_to_position(SpeedDPS(600), 0) 271 | 272 | def colorarm_remove_halfway(self): 273 | log.info("colorarm_remove_halfway()") 274 | self.colorarm.on_to_position(SpeedDPS(600), -400) 275 | 276 | def scan_face(self, face_number): 277 | log.info("scan_face() %d/6" % face_number) 278 | 279 | if self.shutdown: 280 | return 281 | 282 | if self.flipper.position > 35: 283 | self.flipper_away(100) 284 | 285 | self.colorarm_middle() 286 | self.colors[int(MindCuber.scan_order[self.k])] = self.color_sensor.rgb 287 | 288 | self.k += 1 289 | i = 1 290 | target_pos = 115 291 | self.colorarm_corner(i) 292 | 293 | # The gear ratio is 3:1 so 1080 is one full rotation 294 | self.turntable.reset() 295 | self.turntable.on_to_position(SpeedDPS(MindCuber.rotate_speed), 1080, block=False) 296 | self.turntable.wait_until('running') 297 | 298 | while True: 299 | 300 | # 135 is 1/8 of full rotation 301 | if self.turntable.position >= target_pos: 302 | current_color = self.color_sensor.rgb 303 | self.colors[int(MindCuber.scan_order[self.k])] = current_color 304 | 305 | i += 1 306 | self.k += 1 307 | 308 | if i == 9: 309 | # Last face, move the color arm all the way out of the way 310 | if face_number == 6: 311 | self.colorarm_remove() 312 | 313 | # Move the color arm far enough away so that the flipper 314 | # arm doesn't hit it 315 | else: 316 | self.colorarm_remove_halfway() 317 | 318 | break 319 | 320 | elif i % 2: 321 | self.colorarm_corner(i) 322 | 323 | if i == 1: 324 | target_pos = 115 325 | elif i == 3: 326 | target_pos = 380 327 | else: 328 | target_pos = i * 135 329 | 330 | else: 331 | self.colorarm_edge(i) 332 | 333 | if i == 2: 334 | target_pos = 220 335 | elif i == 8: 336 | target_pos = 1060 337 | else: 338 | target_pos = i * 135 339 | 340 | if self.shutdown: 341 | return 342 | 343 | if i < 9: 344 | raise ScanError('i is %d..should be 9' % i) 345 | 346 | self.turntable.wait_until_not_moving() 347 | self.turntable.off() 348 | self.turntable.reset() 349 | log.info("\n") 350 | 351 | def scan(self): 352 | log.info("scan()") 353 | self.colors = {} 354 | self.k = 0 355 | self.scan_face(1) 356 | 357 | self.flip() 358 | self.scan_face(2) 359 | 360 | self.flip() 361 | self.scan_face(3) 362 | 363 | self.rotate_cube(-1, 1) 364 | self.flip() 365 | self.scan_face(4) 366 | 367 | self.rotate_cube(1, 1) 368 | self.flip() 369 | self.scan_face(5) 370 | 371 | self.flip() 372 | self.scan_face(6) 373 | 374 | if self.shutdown: 375 | return 376 | 377 | log.info("RGB json:\n%s\n" % json.dumps(self.colors)) 378 | self.rgb_solver = RubiksColorSolverGeneric(3) 379 | self.rgb_solver.enter_scan_data(self.colors) 380 | self.rgb_solver.crunch_colors() 381 | self.cube_kociemba = self.rgb_solver.cube_for_kociemba_strict() 382 | log.info("Final Colors (kociemba): %s" % ''.join(self.cube_kociemba)) 383 | 384 | # This is only used if you want to rotate the cube so U is on top, F is 385 | # in the front, etc. You would do this if you were troubleshooting color 386 | # detection and you want to pause to compare the color pattern on the 387 | # cube vs. what we think the color pattern is. 388 | ''' 389 | log.info("Position the cube so that U is on top, F is in the front, etc...to make debugging easier") 390 | self.rotate_cube(-1, 1) 391 | self.flip() 392 | self.flipper_away() 393 | self.rotate_cube(1, 1) 394 | input('Paused') 395 | ''' 396 | 397 | def move(self, face_down): 398 | log.info("move() face_down %s" % face_down) 399 | 400 | position = self.state.index(face_down) 401 | actions = { 402 | 0: ["flip", "flip"], 403 | 1: [], 404 | 2: ["rotate_cube_2", "flip"], 405 | 3: ["rotate_cube_1", "flip"], 406 | 4: ["flip"], 407 | 5: ["rotate_cube_3", "flip"] 408 | }.get(position, None) 409 | 410 | for a in actions: 411 | 412 | if self.shutdown: 413 | break 414 | 415 | getattr(self, a)() 416 | 417 | def run_kociemba_actions(self, actions): 418 | log.info('Action (kociemba): %s' % ' '.join(actions)) 419 | total_actions = len(actions) 420 | 421 | for (i, a) in enumerate(actions): 422 | 423 | if self.shutdown: 424 | break 425 | 426 | if a.endswith("'"): 427 | face_down = list(a)[0] 428 | rotation_dir = 1 429 | elif a.endswith("2"): 430 | face_down = list(a)[0] 431 | rotation_dir = 2 432 | else: 433 | face_down = a 434 | rotation_dir = 3 435 | 436 | log.info("Move %d/%d: %s%s (a %s)" % (i, total_actions, face_down, rotation_dir, pformat(a))) 437 | self.move(face_down) 438 | 439 | if rotation_dir == 1: 440 | self.rotate_cube_blocked_1() 441 | elif rotation_dir == 2: 442 | self.rotate_cube_blocked_2() 443 | elif rotation_dir == 3: 444 | self.rotate_cube_blocked_3() 445 | log.info("\n") 446 | 447 | def resolve(self): 448 | 449 | if self.shutdown: 450 | return 451 | 452 | cmd = ['kociemba', ''.join(map(str, self.cube_kociemba))] 453 | output = check_output(cmd).decode('ascii') 454 | 455 | if 'ERROR' in output: 456 | msg = "'%s' returned the following error\n%s\n" % (' '.join(cmd), output) 457 | log.error(msg) 458 | print(msg) 459 | sys.exit(1) 460 | 461 | actions = output.strip().split() 462 | self.run_kociemba_actions(actions) 463 | self.cube_done() 464 | 465 | def cube_done(self): 466 | self.flipper_away() 467 | 468 | def wait_for_cube_insert(self): 469 | rubiks_present = 0 470 | rubiks_present_target = 10 471 | log.info('wait for cube...to be inserted') 472 | 473 | while True: 474 | 475 | if self.shutdown: 476 | break 477 | 478 | dist = self.infrared_sensor.proximity 479 | 480 | # It is odd but sometimes when the cube is inserted 481 | # the IR sensor returns a value of 100...most of the 482 | # time it is just a value less than 50 483 | if dist < 50 or dist == 100: 484 | rubiks_present += 1 485 | log.info("wait for cube...distance %d, present for %d/%d" % 486 | (dist, rubiks_present, rubiks_present_target)) 487 | else: 488 | if rubiks_present: 489 | log.info('wait for cube...cube removed (%d)' % dist) 490 | rubiks_present = 0 491 | 492 | if rubiks_present >= rubiks_present_target: 493 | log.info('wait for cube...cube found and stable') 494 | break 495 | 496 | time.sleep(0.1) 497 | 498 | 499 | if __name__ == '__main__': 500 | 501 | # logging.basicConfig(filename='rubiks.log', 502 | logging.basicConfig(level=logging.INFO, 503 | format='%(asctime)s %(filename)12s %(levelname)8s: %(message)s') 504 | log = logging.getLogger(__name__) 505 | 506 | # Color the errors and warnings in red 507 | logging.addLevelName(logging.ERROR, "\033[91m %s\033[0m" % logging.getLevelName(logging.ERROR)) 508 | logging.addLevelName(logging.WARNING, "\033[91m %s\033[0m" % logging.getLevelName(logging.WARNING)) 509 | 510 | mcube = MindCuber() 511 | 512 | try: 513 | mcube.wait_for_cube_insert() 514 | 515 | # Push the cube to the right so that it is in the expected 516 | # position when we begin scanning 517 | mcube.flipper_hold_cube(100) 518 | mcube.flipper_away(100) 519 | 520 | mcube.scan() 521 | mcube.resolve() 522 | mcube.shutdown_robot() 523 | 524 | except Exception as e: 525 | log.exception(e) 526 | mcube.shutdown_robot() 527 | sys.exit(1) 528 | -------------------------------------------------------------------------------- /robots/R3PTAR/README.md: -------------------------------------------------------------------------------- 1 | R3PTAR 2 | ====== 3 | 4 | One of the most loved robots, the standing 35 cm. / 13,8 inch tall R3PTAR robot 5 | slithers across the floor like a real cobra, and strikes at lightning speed 6 | with it’s pointed red fangs. 7 | 8 | Coincidentally, its also a nice example to demonstrate how to use threads in 9 | Python. 10 | 11 | **Building instructions**: http://www.lego.com/en-us/mindstorms/build-a-robot/r3ptar 12 | 13 | **Resources**: 14 | 15 | * `rattle-snake.wav`: https://www.freesound.org/people/7h3_lark/sounds/268580/ 16 | * `snake-hiss.wav`: https://www.freesound.org/people/Reitanna/sounds/343928/ 17 | -------------------------------------------------------------------------------- /robots/R3PTAR/r3ptar.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | Implementation of R3PTAR 5 | """ 6 | 7 | import logging 8 | import signal 9 | import sys 10 | from ev3dev2.motor import OUTPUT_A, OUTPUT_B, OUTPUT_C, OUTPUT_D, MediumMotor, LargeMotor 11 | from ev3dev2.sensor.lego import InfraredSensor 12 | from ev3dev2.sound import Sound 13 | from threading import Thread, Event 14 | from time import sleep 15 | 16 | log = logging.getLogger(__name__) 17 | 18 | 19 | class MonitorRemoteControl(Thread): 20 | """ 21 | A thread to monitor R3PTAR's InfraredSensor and process signals 22 | from the remote control 23 | """ 24 | 25 | def __init__(self, parent): 26 | Thread.__init__(self) 27 | self.parent = parent 28 | self.shutdown_event = Event() 29 | 30 | def __str__(self): 31 | return "MonitorRemoteControl" 32 | 33 | def run(self): 34 | STRIKE_SPEED_PCT = 40 35 | 36 | while True: 37 | 38 | if self.shutdown_event.is_set(): 39 | log.info('%s: shutdown_event is set' % self) 40 | break 41 | 42 | #log.info("proximity: %s" % self.parent.remote.proximity) 43 | if self.parent.remote.proximity < 30: 44 | self.parent.speaker.play('snake-hiss.wav', Sound.PLAY_NO_WAIT_FOR_COMPLETE) 45 | self.parent.strike_motor.on_for_seconds(speed=STRIKE_SPEED_PCT, seconds=0.5) 46 | self.parent.strike_motor.on_for_seconds(speed=(STRIKE_SPEED_PCT * -1), seconds=0.5) 47 | 48 | self.parent.remote.process() 49 | sleep(0.01) 50 | 51 | 52 | class R3PTAR(object): 53 | 54 | def __init__(self, 55 | drive_motor_port=OUTPUT_B, 56 | strike_motor_port=OUTPUT_D, 57 | steer_motor_port=OUTPUT_A, 58 | drive_speed_pct=60): 59 | 60 | self.drive_motor = LargeMotor(drive_motor_port) 61 | self.strike_motor = LargeMotor(strike_motor_port) 62 | self.steer_motor = MediumMotor(steer_motor_port) 63 | self.speaker = Sound() 64 | STEER_SPEED_PCT = 30 65 | 66 | self.remote = InfraredSensor() 67 | self.remote.on_channel1_top_left = self.make_move(self.drive_motor, drive_speed_pct) 68 | self.remote.on_channel1_bottom_left = self.make_move(self.drive_motor, drive_speed_pct * -1) 69 | self.remote.on_channel1_top_right = self.make_move(self.steer_motor, STEER_SPEED_PCT) 70 | self.remote.on_channel1_bottom_right = self.make_move(self.steer_motor, STEER_SPEED_PCT * -1) 71 | 72 | self.shutdown_event = Event() 73 | self.mrc = MonitorRemoteControl(self) 74 | 75 | # Register our signal_term_handler() to be called if the user sends 76 | # a 'kill' to this process or does a Ctrl-C from the command line 77 | signal.signal(signal.SIGTERM, self.signal_term_handler) 78 | signal.signal(signal.SIGINT, self.signal_int_handler) 79 | 80 | def make_move(self, motor, speed): 81 | def move(state): 82 | if state: 83 | motor.on(speed) 84 | else: 85 | motor.stop() 86 | return move 87 | 88 | def shutdown_robot(self): 89 | 90 | if self.shutdown_event.is_set(): 91 | return 92 | 93 | self.shutdown_event.set() 94 | log.info('shutting down') 95 | self.mrc.shutdown_event.set() 96 | 97 | self.remote.on_channel1_top_left = None 98 | self.remote.on_channel1_bottom_left = None 99 | self.remote.on_channel1_top_right = None 100 | self.remote.on_channel1_bottom_right = None 101 | 102 | self.drive_motor.off(brake=False) 103 | self.strike_motor.off(brake=False) 104 | self.steer_motor.off(brake=False) 105 | 106 | self.mrc.join() 107 | 108 | def signal_term_handler(self, signal, frame): 109 | log.info('Caught SIGTERM') 110 | self.shutdown_robot() 111 | 112 | def signal_int_handler(self, signal, frame): 113 | log.info('Caught SIGINT') 114 | self.shutdown_robot() 115 | 116 | def main(self): 117 | self.mrc.start() 118 | self.shutdown_event.wait() 119 | 120 | 121 | if __name__ == '__main__': 122 | 123 | # Change level to logging.DEBUG for more details 124 | logging.basicConfig(level=logging.INFO, 125 | format="%(asctime)s %(levelname)5s %(filename)s: %(message)s") 126 | log = logging.getLogger(__name__) 127 | 128 | # Color the errors and warnings in red 129 | logging.addLevelName(logging.ERROR, "\033[91m %s\033[0m" % logging.getLevelName(logging.ERROR)) 130 | logging.addLevelName(logging.WARNING, "\033[91m%s\033[0m" % logging.getLevelName(logging.WARNING)) 131 | 132 | log.info("Starting R3PTAR") 133 | snake = R3PTAR() 134 | snake.main() 135 | log.info("Exiting R3PTAR") 136 | -------------------------------------------------------------------------------- /robots/R3PTAR/rattle-snake.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ev3dev/ev3dev-lang-python-demo/99eb25392ff350a2f9a3c180c3892c29d1fa5a21/robots/R3PTAR/rattle-snake.wav -------------------------------------------------------------------------------- /robots/R3PTAR/snake-hiss.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ev3dev/ev3dev-lang-python-demo/99eb25392ff350a2f9a3c180c3892c29d1fa5a21/robots/R3PTAR/snake-hiss.wav -------------------------------------------------------------------------------- /robots/SPIK3R/Blip 3.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ev3dev/ev3dev-lang-python-demo/99eb25392ff350a2f9a3c180c3892c29d1fa5a21/robots/SPIK3R/Blip 3.wav -------------------------------------------------------------------------------- /robots/SPIK3R/README.md: -------------------------------------------------------------------------------- 1 | # SPIK3R 2 | 3 | > This six-legged creature doesn’t just look like a scorpion, it also acts like one. It turns sharply, snaps with it’s crushing claw, and it’s lightning tail is ready to fire at anyone or anything that gets in its way. 4 | 5 | Control SPIK3R as follows: 6 | 7 | - Make SPIK3R move straight forward by pressing the IR Remote Control's two Up buttons at the same time 8 | - Make SPIK3R turn right by pressing the IR Remote Control's Right Up button 9 | - Make SPIK3R sting by pressing the IR Remote Control's Beacon button 10 | - Make SPIK3R snap its claw by pressing the Touch Sensor (please do connect one to enable this) 11 | 12 | The build instructions may be found at the official LEGO MINDSTROMS site [here](https://www.lego.com/cdn/cs/set/assets/blt7dca5180ea66ea5e/31313_SPIK3R_2016.pdf). 13 | -------------------------------------------------------------------------------- /robots/SPIK3R/spik3r.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env micropython 2 | 3 | 4 | from ev3dev2.motor import LargeMotor, MediumMotor, OUTPUT_A, OUTPUT_B, OUTPUT_D 5 | from ev3dev2.sensor import INPUT_1, INPUT_4 6 | from ev3dev2.sensor.lego import TouchSensor, InfraredSensor 7 | from ev3dev2.sound import Sound 8 | 9 | 10 | class Spik3r: 11 | def __init__( 12 | self, 13 | claw_motor_port: str = OUTPUT_A, 14 | move_motor_port: str = OUTPUT_B, 15 | sting_motor_port: str = OUTPUT_D, 16 | touch_sensor_port: str = INPUT_1, 17 | ir_sensor_port: str = INPUT_4, ir_beacon_channel: int = 1): 18 | self.claw_motor = MediumMotor(address=claw_motor_port) 19 | self.move_motor = LargeMotor(address=move_motor_port) 20 | self.sting_motor = LargeMotor(address=sting_motor_port) 21 | 22 | self.ir_sensor = InfraredSensor(address=ir_sensor_port) 23 | self.ir_beacon_channel = ir_beacon_channel 24 | 25 | self.touch_sensor = TouchSensor(address=touch_sensor_port) 26 | 27 | self.speaker = Sound() 28 | 29 | def snap_claw_if_touched(self): 30 | if self.touch_sensor.is_pressed: 31 | self.claw_motor.on_for_seconds( 32 | speed=50, 33 | seconds=1, 34 | brake=True, 35 | block=True) 36 | 37 | self.claw_motor.on_for_seconds( 38 | speed=-50, 39 | seconds=0.3, 40 | brake=True, 41 | block=True) 42 | 43 | def move_by_ir_beacon(self): 44 | if self.ir_sensor.top_left(channel=self.ir_beacon_channel) and \ 45 | self.ir_sensor.top_right(channel=self.ir_beacon_channel): 46 | self.move_motor.on( 47 | speed=100, 48 | block=False, 49 | brake=False) 50 | 51 | elif self.ir_sensor.top_right(channel=self.ir_beacon_channel): 52 | self.move_motor.on( 53 | speed=-100, 54 | brake=False, 55 | block=False) 56 | 57 | else: 58 | self.move_motor.off(brake=False) 59 | 60 | def sting_by_ir_beacon(self): 61 | if self.ir_sensor.beacon(channel=self.ir_beacon_channel): 62 | self.sting_motor.on_for_degrees( 63 | speed=-75, 64 | degrees=220, 65 | brake=True, 66 | block=False) 67 | 68 | self.speaker.play_file( 69 | wav_file='Blip 3.wav', 70 | volume=100, 71 | play_type=Sound.PLAY_WAIT_FOR_COMPLETE) 72 | 73 | self.sting_motor.on_for_seconds( 74 | speed=-100, 75 | seconds=1, 76 | brake=True, 77 | block=True) 78 | 79 | self.sting_motor.on_for_seconds( 80 | speed=40, 81 | seconds=1, 82 | brake=True, 83 | block=True) 84 | 85 | while self.ir_sensor.beacon(channel=self.ir_beacon_channel): 86 | pass 87 | 88 | def main(self): 89 | while True: 90 | self.snap_claw_if_touched() 91 | self.move_by_ir_beacon() 92 | self.sting_by_ir_beacon() 93 | 94 | 95 | if __name__ == '__main__': 96 | SPIK3R = Spik3r() 97 | SPIK3R.main() 98 | -------------------------------------------------------------------------------- /robots/TRACK3R/TRACK3R.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import logging 4 | import sys 5 | from ev3dev2.motor import OUTPUT_A, OUTPUT_B, OUTPUT_C, MediumMotor 6 | from ev3dev2.control.rc_tank import RemoteControlledTank 7 | 8 | log = logging.getLogger(__name__) 9 | 10 | 11 | class TRACK3R(RemoteControlledTank): 12 | """ 13 | Base class for all TRACK3R variations. The only difference in the child 14 | classes are in how the medium motor is handled. 15 | 16 | To enable the medium motor toggle the beacon button on the EV3 remote. 17 | """ 18 | 19 | def __init__(self, medium_motor, left_motor, right_motor): 20 | RemoteControlledTank.__init__(self, left_motor, right_motor) 21 | self.medium_motor = MediumMotor(medium_motor) 22 | self.medium_motor.reset() 23 | 24 | 25 | class TRACK3RWithBallShooter(TRACK3R): 26 | 27 | def __init__(self, medium_motor=OUTPUT_A, left_motor=OUTPUT_B, right_motor=OUTPUT_C): 28 | TRACK3R.__init__(self, medium_motor, left_motor, right_motor) 29 | self.remote.on_channel1_beacon = self.fire_ball 30 | 31 | def fire_ball(self, state): 32 | if state: 33 | self.medium_motor.run_to_rel_pos(speed_sp=400, position_sp=3*360) 34 | else: 35 | self.medium_motor.stop() 36 | 37 | 38 | class TRACK3RWithSpinner(TRACK3R): 39 | 40 | def __init__(self, medium_motor=OUTPUT_A, left_motor=OUTPUT_B, right_motor=OUTPUT_C): 41 | TRACK3R.__init__(self, medium_motor, left_motor, right_motor) 42 | self.remote.on_channel1_beacon = self.spinner 43 | 44 | def spinner(self, state): 45 | if state: 46 | self.medium_motor.run_forever(speed_sp=50) 47 | else: 48 | self.medium_motor.stop() 49 | 50 | 51 | class TRACK3RWithClaw(TRACK3R): 52 | 53 | def __init__(self, medium_motor=OUTPUT_A, left_motor=OUTPUT_B, right_motor=OUTPUT_C): 54 | TRACK3R.__init__(self, medium_motor, left_motor, right_motor) 55 | self.remote.on_channel1_beacon = self.move_claw 56 | 57 | def move_claw(self, state): 58 | if state: 59 | self.medium_motor.run_to_rel_pos(speed_sp=200, position_sp=-75) 60 | else: 61 | self.medium_motor.run_to_rel_pos(speed_sp=200, position_sp=75) 62 | -------------------------------------------------------------------------------- /robots/TRACK3R/TRACK3RWithBallShooter: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import logging 4 | from TRACK3R import TRACK3RWithBallShooter 5 | 6 | # Change level to logging.INFO to make less chatty 7 | logging.basicConfig(level=logging.DEBUG, 8 | format="%(asctime)s %(levelname)5s %(filename)s:%(lineno)5s - %(funcName)25s(): %(message)s") 9 | log = logging.getLogger(__name__) 10 | 11 | # Color the errors and warnings in red 12 | logging.addLevelName(logging.ERROR, "\033[91m %s\033[0m" % logging.getLevelName(logging.ERROR)) 13 | logging.addLevelName(logging.WARNING, "\033[91m%s\033[0m" % logging.getLevelName(logging.WARNING)) 14 | 15 | log.info("Starting TRACK3RWithBallShooter") 16 | tracker = TRACK3RWithBallShooter() 17 | tracker.main() 18 | log.info("Exiting TRACK3RWithBallShooter") 19 | -------------------------------------------------------------------------------- /robots/TRACK3R/TRACK3RWithClaw: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import logging 4 | from TRACK3R import TRACK3RWithClaw 5 | 6 | # Change level to logging.INFO to make less chatty 7 | logging.basicConfig(level=logging.DEBUG, 8 | format="%(asctime)s %(levelname)5s %(filename)s:%(lineno)5s - %(funcName)25s(): %(message)s") 9 | log = logging.getLogger(__name__) 10 | 11 | # Color the errors and warnings in red 12 | logging.addLevelName(logging.ERROR, "\033[91m %s\033[0m" % logging.getLevelName(logging.ERROR)) 13 | logging.addLevelName(logging.WARNING, "\033[91m%s\033[0m" % logging.getLevelName(logging.WARNING)) 14 | 15 | log.info("Starting TRACK3RWithClaw") 16 | tracker = TRACK3RWithClaw() 17 | tracker.main() 18 | log.info("Exiting TRACK3RWithClaw") 19 | -------------------------------------------------------------------------------- /robots/TRACK3R/TRACK3RWithSpinner: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import logging 4 | from TRACK3R import TRACK3RWithSpinner 5 | 6 | # Change level to logging.INFO to make less chatty 7 | logging.basicConfig(level=logging.DEBUG, 8 | format="%(asctime)s %(levelname)5s %(filename)s:%(lineno)5s - %(funcName)25s(): %(message)s") 9 | log = logging.getLogger(__name__) 10 | 11 | # Color the errors and warnings in red 12 | logging.addLevelName(logging.ERROR, "\033[91m %s\033[0m" % logging.getLevelName(logging.ERROR)) 13 | logging.addLevelName(logging.WARNING, "\033[91m%s\033[0m" % logging.getLevelName(logging.WARNING)) 14 | 15 | log.info("Starting TRACK3RWithSpinner") 16 | tracker = TRACK3RWithSpinner() 17 | tracker.main() 18 | log.info("Exiting TRACK3RWithSpinner") 19 | -------------------------------------------------------------------------------- /robots/misc/console_menu.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env micropython 2 | from time import sleep 3 | from sys import stderr 4 | from os import listdir 5 | from ev3dev2.button import Button 6 | from ev3dev2.console import Console 7 | from ev3dev2.led import Leds 8 | from ev3dev2.sensor import list_sensors, INPUT_1, INPUT_2, INPUT_3, INPUT_4 9 | 10 | 11 | """ 12 | Used to create a console menu for switching between programs quickly 13 | without having to return to Brickman to find and launch a program. 14 | Demonstrates the EV3DEV2 Console(), Led(), and Button() classes. 15 | """ 16 | 17 | 18 | def get_positions(console): 19 | """ 20 | Compute a dictionary keyed by button names with horizontal alignment, 21 | and column/row location to show each choice on the EV3 LCD console. 22 | Parameter: 23 | - `console` (Console): an instance of the EV3 Console() class 24 | returns a dictionary keyed by button names with column/row location 25 | """ 26 | 27 | midrow = 1 + console.rows // 2 28 | midcol = 1 + console.columns // 2 29 | # horiz_alignment, col, row 30 | return { 31 | "up": ("C", midcol, 1), 32 | "right": ("R", console.columns, midrow), 33 | "down": ("C", midcol, console.rows), 34 | "left": ("L", 1, midrow), 35 | "enter": ("C", midcol, midrow) 36 | } 37 | 38 | 39 | def wait_for_button_press(button): 40 | """ 41 | Wait for a button to be pressed and released. 42 | Parameter: 43 | - `button` (Button): an instance of the EV3 Button() class 44 | return the Button name that was pressed. 45 | """ 46 | pressed = None 47 | while True: 48 | allpressed = button.buttons_pressed 49 | if bool(allpressed): 50 | pressed = allpressed[0] # just get the first one 51 | while not button.wait_for_released(pressed): 52 | pass 53 | break 54 | return pressed 55 | 56 | 57 | def menu(choices, before_run_function=None, after_run_function=None): 58 | """ 59 | Console Menu that accepts choices and corresponding functions to call. 60 | The user must press the same button twice: once to see their choice highlited, 61 | a second time to confirm and run the function. The EV3 LEDs show each state change: 62 | Green = Ready for button, Amber = Ready for second button, Red = Running 63 | Parameters: 64 | - `choices` a dictionary of tuples "button-name": ("mission-name", function-to-call) 65 | Example: 66 | choices = { 67 | # "button-name": ("mission-name", function-to-call) 68 | # or "button-name": ("mission-name", lambda: call(x, y, z)) 69 | "enter": ("CAL", lambda: auto_calibrate(robot, 1.0)), 70 | "up": ("MI2", fmission2), 71 | "right": ("MI3", fmission3), 72 | "down": ("MI4", fmission4), 73 | "left": ("MI5", fmission5) 74 | } 75 | where fmission2, fmission3 are functions; 76 | note don't call them with parentheses, unless preceded by lambda: to defer the call 77 | - `before_run_function` when not None, call this function before each mission run, passed with mission-name 78 | - `after_run_function` when not None, call this function after each mission run, passed with mission-name 79 | """ 80 | 81 | console = Console() 82 | leds = Leds() 83 | button = Button() 84 | 85 | leds.all_off() 86 | leds.set_color("LEFT", "GREEN") 87 | leds.set_color("RIGHT", "GREEN") 88 | menu_positions = get_positions(console) 89 | 90 | last = None # the last choice--initialize to None 91 | 92 | while True: 93 | # display the menu of choices, but show the last choice in inverse 94 | console.reset_console() 95 | for btn, (name, _) in choices.items(): 96 | align, col, row = menu_positions[btn] 97 | console.text_at(name, col, row, inverse=(btn == last), alignment=align) 98 | 99 | pressed = wait_for_button_press(button) 100 | 101 | # get the choice for the button pressed 102 | if pressed in choices: 103 | if last == pressed: # was same button pressed? 104 | console.reset_console() 105 | leds.set_color("LEFT", "RED") 106 | leds.set_color("RIGHT", "RED") 107 | 108 | # call the user's subroutine to run the mission, but catch any errors 109 | try: 110 | name, mission_function = choices[pressed] 111 | if before_run_function is not None: 112 | before_run_function(name) 113 | mission_function() 114 | except Exception as ex: 115 | print("**** Exception when running") 116 | print(ex) 117 | finally: 118 | if after_run_function is not None: 119 | after_run_function(name) 120 | last = None 121 | leds.set_color("LEFT", "GREEN") 122 | leds.set_color("RIGHT", "GREEN") 123 | else: # different button pressed 124 | last = pressed 125 | leds.set_color("LEFT", "AMBER") 126 | leds.set_color("RIGHT", "AMBER") 127 | 128 | 129 | if __name__ == "__main__": 130 | 131 | # This is the main program to demonstrate the console menu logic above. 132 | # 133 | # Define functions that represent different missions 134 | # Note: these can be imported from different modules (files) 135 | # and use lambda notation to defer the function call 136 | # i.e. lambda : call(a, b, c) 137 | 138 | def calibrate(): 139 | """ Placeholder for call to your calibration logic to set the black and white values for your color sensors """ 140 | print("calibrating...") 141 | sleep(1) 142 | 143 | def show_sensors(iterations): 144 | """ Show the EV3 sensors, current mode and value """ 145 | sensors = list(list_sensors(address=[INPUT_1, INPUT_2, INPUT_3])) # , INPUT_4 146 | for _ in range(iterations): 147 | for sensor in sensors: 148 | print("{} {}: {}".format(sensor.address, sensor.mode, sensor.value())) 149 | sleep(.5) 150 | sleep(10) 151 | 152 | def mission1(): 153 | print("mission 1...") 154 | sleep(1) 155 | 156 | def mission2(): 157 | print("mission 2...") 158 | sleep(1) 159 | 160 | def mission3(): 161 | print("mission 3...") 162 | sleep(1) 163 | # for testing when a function generates an error 164 | raise Exception('Raised error') 165 | 166 | # Define the functions to be called before and after each run. 167 | # Functions will be called with the mission_name as the argument. 168 | # Useful for resetting motor positions between runs, etc. 169 | 170 | def before(mission_name): 171 | print("before " + mission_name) 172 | 173 | def after(mission_name): 174 | print("after " + mission_name) 175 | sleep(1) 176 | 177 | # Define the buttons, mission names, functions for the console menu. 178 | # Key is the button assignment: one of "enter", "up", "right", "down", "left" 179 | # Note the "backspace" button interrupts the program and returns to Brickman 180 | # Example: 181 | # CHOICES = { 182 | # # "button-name": ("mission-name", function-to-call) 183 | # # or "button-name": ("mission-name", lambda: call(x, y, z)) 184 | # "up": ("MI1", mission1), 185 | # "right": ("MI2", mission2), 186 | # "left": ("MI3", mission3), 187 | # "down": ("SHOW", lambda: show_sensors(5)), 188 | # "enter": ("CAL", calibrate) 189 | # } 190 | # menu(CHOICES, before_run_function=before, after_run_function=after) 191 | 192 | CHOICES = { 193 | "up": ("MI1", mission1), 194 | "right": ("MI2", mission2), 195 | "left": ("MI3", mission3), 196 | "down": ("SHOW", lambda: show_sensors(5)), 197 | "enter": ("CAL", calibrate) 198 | } 199 | 200 | menu(CHOICES, before_run_function=before, after_run_function=after) 201 | -------------------------------------------------------------------------------- /robots/misc/leds.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | # ------------------------------------------------------------------------------ 5 | # Copyright (c) 2015 Eric Pascual 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | # THE SOFTWARE. 24 | # ----------------------------------------------------------------------------- 25 | 26 | """ This demo illustrates how to use the two red-green LEDs of the EV3 brick. 27 | """ 28 | 29 | import time 30 | import math 31 | 32 | from ev3dev.ev3 import Leds 33 | 34 | print(__doc__.lstrip()) 35 | 36 | print('saving current LEDs state') 37 | 38 | # save current state 39 | saved_state = [led.brightness_pct for led in Leds.LEFT + Leds.RIGHT] 40 | 41 | Leds.all_off() 42 | time.sleep(1) 43 | 44 | # cycle LEDs like a traffic light 45 | print('traffic light') 46 | for _ in range(3): 47 | for color in (Leds.GREEN, Leds.YELLOW, Leds.RED): 48 | for group in (Leds.LEFT, Leds.RIGHT): 49 | Leds.set_color(group, color) 50 | time.sleep(0.5) 51 | 52 | Leds.all_off() 53 | time.sleep(0.5) 54 | 55 | # blink LEDs from side to side now 56 | print('side to side') 57 | for _ in range(3): 58 | for led in (Leds.red_left, Leds.red_right, Leds.green_left, Leds.green_right): 59 | led.brightness_pct = 100 60 | time.sleep(0.5) 61 | led.brightness_pct = 0 62 | 63 | Leds.all_off() 64 | time.sleep(0.5) 65 | 66 | # continuous mix of colors 67 | print('colors fade') 68 | for i in range(360): 69 | rd = math.radians(10 * i) 70 | Leds.red_left.brightness_pct = .5 * (1 + math.cos(rd)) 71 | Leds.green_left.brightness_pct = .5 * (1 + math.sin(rd)) 72 | Leds.red_right.brightness_pct = .5 * (1 + math.sin(rd)) 73 | Leds.green_right.brightness_pct = .5 * (1 + math.cos(rd)) 74 | time.sleep(0.05) 75 | 76 | Leds.all_off() 77 | time.sleep(0.5) 78 | 79 | print('restoring initial LEDs state') 80 | for led, level in zip(Leds.RIGHT + Leds.LEFT, saved_state) : 81 | led.brightness_pct = level 82 | 83 | -------------------------------------------------------------------------------- /robots/misc/snd/r2d2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ev3dev/ev3dev-lang-python-demo/99eb25392ff350a2f9a3c180c3892c29d1fa5a21/robots/misc/snd/r2d2.wav -------------------------------------------------------------------------------- /robots/misc/sound.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | # ------------------------------------------------------------------------------ 5 | # Copyright (c) 2017 Eric Pascual 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | # THE SOFTWARE. 24 | # ----------------------------------------------------------------------------- 25 | 26 | """ Sound capabilities demonstration. 27 | """ 28 | 29 | from textwrap import dedent 30 | import os 31 | 32 | from ev3dev2.sound import Sound 33 | 34 | _HERE = os.path.dirname(__file__) 35 | 36 | print(dedent(""" 37 | A long time ago 38 | in a galaxy far, 39 | far away... 40 | """)) 41 | 42 | speaker = Sound() 43 | 44 | speaker.play_song(( 45 | ('D4', 'e3'), 46 | ('D4', 'e3'), 47 | ('D4', 'e3'), 48 | ('G4', 'h'), 49 | ('D5', 'h'), 50 | ('C5', 'e3'), 51 | ('B4', 'e3'), 52 | ('A4', 'e3'), 53 | ('G5', 'h'), 54 | ('D5', 'q'), 55 | ('C5', 'e3'), 56 | ('B4', 'e3'), 57 | ('A4', 'e3'), 58 | ('G5', 'h'), 59 | ('D5', 'q'), 60 | ('C5', 'e3'), 61 | ('B4', 'e3'), 62 | ('C5', 'e3'), 63 | ('A4', 'h.'), 64 | )) 65 | 66 | speaker.play_file(os.path.join(_HERE, 'snd/r2d2.wav')) 67 | 68 | #speaker.speak("Luke, I am your father") 69 | --------------------------------------------------------------------------------