├── README.md ├── badger2040 ├── displayio_example.py └── displayio_example_with_shutdown.py ├── motor2040 ├── multiple_motors.py ├── onboard_neopixel.py ├── position_control.py ├── quad_position_wave.py ├── quad_velocity_sequence.py ├── read_encoders.py ├── sensor_reading.py ├── single_encoder.py ├── single_motor.py └── velocity_control.py ├── pico_motor_shim └── sequence.py ├── plasma2040 └── rainbow.py ├── plasma_stick ├── neopixel_simpletest.py └── neopixels_rainbow.py └── servo2040 ├── onboard_neopixels.py ├── sensor_reading.py └── servo_driving.py /README.md: -------------------------------------------------------------------------------- 1 | # Pico CircuitPython Examples 2 | This repo contains CircuitPython example code for Pimoroni RP2040 boards and our Pico Bases and Packs: https://shop.pimoroni.com/collections/pico 3 | 4 | ## Getting Started 5 | 6 | - Download a version of CircuitPython for your board at https://circuitpython.org/downloads 7 | - Hold down the BOOT button whilst plugging in the USB cable or pressing RESET to go into DFU mode - your board will show up as a drive on your computer called `RPI-RP2`. 8 | - Drag the newly downloaded CircuitPython .uf2 to the `RPI-RP2` drive - it will then reboot running CircuitPython (and will show up as a drive called `CIRCUITPY`). 9 | - Edit `code.py` on the CIRCUITPY drive with any text editor - when saved your code will run automatically. If you need access to the serial console (useful for viewing errors and troubleshooting your code), consider downloading Mu - https://codewith.mu/ 10 | 11 | ## Additional Libraries 12 | 13 | Some code in this repo uses additional CircuitPython libraries. You can find these in the CircuitPython library bundle at https://circuitpython.org/libraries - make sure to download the bundle that matches your version of CircuitPython! Once you've located the relevant file or directory, copy it into the `lib` folder on `CIRCUITPY`. 14 | 15 | ## Board Definitions 16 | 17 | With the serial console open in Mu, press Ctrl-C to enter the REPL. 18 | 19 | To view the available pins for your board (very handy), enter the following: 20 | ``` python 21 | import board 22 | dir(board) 23 | ``` 24 | Hit Ctrl-D when you're done to reload `code.py`. 25 | 26 | ## More Resources 27 | 28 | Want to know more about CircuitPython? Start here: https://learn.adafruit.com/welcome-to-circuitpython 29 | -------------------------------------------------------------------------------- /badger2040/displayio_example.py: -------------------------------------------------------------------------------- 1 | import board 2 | import terminalio 3 | import displayio 4 | import vectorio 5 | from adafruit_display_text import label 6 | 7 | """ 8 | An example of how to show text on the Badger 2040's 9 | screen using the built-in DisplayIO object. 10 | """ 11 | 12 | display = board.DISPLAY 13 | 14 | # Set text, font, and color 15 | title = "HELLO WORLD" 16 | subtitle = "From CircuitPython" 17 | font = terminalio.FONT 18 | color = 0x000000 19 | 20 | # Set the palette for the background color 21 | palette = displayio.Palette(1) 22 | palette[0] = 0xFFFFFF 23 | 24 | # Add a background rectangle 25 | rectangle = vectorio.Rectangle(pixel_shader=palette, width=display.width + 1, height=display.height, x=0, y=0) 26 | 27 | # Create the title and subtitle labels 28 | title_label = label.Label(font, text=title, color=color, scale=4) 29 | subtitle_label = label.Label(font, text=subtitle, color=color, scale=2) 30 | 31 | # Set the label locations 32 | title_label.x = 20 33 | title_label.y = 45 34 | 35 | subtitle_label.x = 40 36 | subtitle_label.y = 90 37 | 38 | # Create the display group and append objects to it 39 | group = displayio.Group() 40 | group.append(rectangle) 41 | group.append(title_label) 42 | group.append(subtitle_label) 43 | 44 | # Show the group and refresh the screen to see the result 45 | display.show(group) 46 | display.refresh() 47 | 48 | # Loop forever so you can enjoy your message 49 | while True: 50 | pass 51 | -------------------------------------------------------------------------------- /badger2040/displayio_example_with_shutdown.py: -------------------------------------------------------------------------------- 1 | import time 2 | import board 3 | import terminalio 4 | import displayio 5 | import digitalio 6 | import vectorio 7 | from adafruit_display_text import label 8 | 9 | """ 10 | An extension to the DisplayIO example that includes the activity LED 11 | as well as puts the board to sleep once the screen has been updated. 12 | 13 | To wake your badger when on battery, hold down any of the 14 | front buttons until the activity light stops flashing. 15 | """ 16 | 17 | display = board.DISPLAY 18 | enable = board.ENABLE_DIO 19 | 20 | # Set up and turn on the activity LED 21 | act = digitalio.DigitalInOut(board.USER_LED) 22 | act.direction = digitalio.Direction.OUTPUT 23 | act.value = True 24 | 25 | # Set text, font, and color 26 | title = "HELLO WORLD" 27 | subtitle = "From CircuitPython" 28 | font = terminalio.FONT 29 | color = 0x000000 30 | 31 | # Set the palette for the background color 32 | palette = displayio.Palette(1) 33 | palette[0] = 0xFFFFFF 34 | 35 | # Add a background rectangle 36 | rectangle = vectorio.Rectangle(pixel_shader=palette, width=display.width + 1, height=display.height, x=0, y=0) 37 | 38 | # Create the title and subtitle labels 39 | title_label = label.Label(font, text=title, color=color, scale=4) 40 | subtitle_label = label.Label(font, text=subtitle, color=color, scale=2) 41 | 42 | # Set the label locations 43 | title_label.x = 20 44 | title_label.y = 45 45 | 46 | subtitle_label.x = 40 47 | subtitle_label.y = 90 48 | 49 | # Create the display group and append objects to it 50 | group = displayio.Group() 51 | group.append(rectangle) 52 | group.append(title_label) 53 | group.append(subtitle_label) 54 | 55 | # Show the group and refresh the screen to see the result 56 | display.show(group) 57 | display.refresh() 58 | 59 | # Wait a few seconds for the screen to refresh 60 | time.sleep(3) 61 | 62 | # Turn the board off 63 | enable.value = False 64 | 65 | # Loop forever so you can enjoy your message when on USB power 66 | # When on battery this will never be reached, as indicated 67 | # by the activity LED turning off rather than flashing 68 | while True: 69 | act.value = False 70 | time.sleep(0.25) 71 | act.value = True 72 | time.sleep(0.25) 73 | -------------------------------------------------------------------------------- /motor2040/multiple_motors.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | import time 4 | import board 5 | import pwmio 6 | import digitalio 7 | from adafruit_motor import motor 8 | 9 | # Motor constants 10 | FREQUENCY = 25000 # Chose a frequency above human hearing 11 | DECAY_MODE = motor.SLOW_DECAY # The decay mode affects how the motor 12 | # responds, with SLOW_DECAY having improved spin 13 | # threshold and speed-to-throttle linearity 14 | 15 | # Create a digitalinout object for the user switch 16 | user_sw = digitalio.DigitalInOut(board.USER_SW) 17 | user_sw.direction = digitalio.Direction.INPUT 18 | user_sw.pull = digitalio.Pull.UP 19 | 20 | # Create the pwm objects 21 | pwm_a_p = pwmio.PWMOut(board.MOTOR_A_P, frequency=FREQUENCY) 22 | pwm_a_n = pwmio.PWMOut(board.MOTOR_A_N, frequency=FREQUENCY) 23 | pwm_b_p = pwmio.PWMOut(board.MOTOR_B_P, frequency=FREQUENCY) 24 | pwm_b_n = pwmio.PWMOut(board.MOTOR_B_N, frequency=FREQUENCY) 25 | pwm_c_p = pwmio.PWMOut(board.MOTOR_C_P, frequency=FREQUENCY) 26 | pwm_c_n = pwmio.PWMOut(board.MOTOR_C_N, frequency=FREQUENCY) 27 | pwm_d_p = pwmio.PWMOut(board.MOTOR_D_P, frequency=FREQUENCY) 28 | pwm_d_n = pwmio.PWMOut(board.MOTOR_D_N, frequency=FREQUENCY) 29 | 30 | # Create the motor objects 31 | mot_a = motor.DCMotor(pwm_a_p, pwm_a_n) 32 | mot_b = motor.DCMotor(pwm_b_p, pwm_b_n) 33 | mot_c = motor.DCMotor(pwm_c_p, pwm_c_n) 34 | mot_d = motor.DCMotor(pwm_d_p, pwm_d_n) 35 | 36 | # Set the motor decay modes (if unset the default will be FAST_DECAY) 37 | mot_a.decay_mode = DECAY_MODE 38 | mot_b.decay_mode = DECAY_MODE 39 | mot_c.decay_mode = DECAY_MODE 40 | mot_d.decay_mode = DECAY_MODE 41 | 42 | 43 | def button_pressed(): 44 | return not user_sw.value 45 | 46 | 47 | # Run the motor sequence 48 | while True: 49 | # Forward slow 50 | mot_a.throttle = 0.5 51 | mot_b.throttle = 0.5 52 | mot_c.throttle = 0.5 53 | mot_d.throttle = 0.5 54 | time.sleep(1) 55 | if button_pressed(): # Exit if button is pressed 56 | break 57 | 58 | # Stop 59 | mot_a.throttle = 0 60 | mot_b.throttle = 0 61 | mot_c.throttle = 0 62 | mot_d.throttle = 0 63 | time.sleep(1) 64 | if button_pressed(): # Exit if button is pressed 65 | break 66 | 67 | # Forward fast 68 | mot_a.throttle = 1.0 69 | mot_b.throttle = 1.0 70 | mot_c.throttle = 1.0 71 | mot_d.throttle = 1.0 72 | time.sleep(1) 73 | if button_pressed(): # Exit if button is pressed 74 | break 75 | 76 | # Spin freely 77 | mot_a.throttle = None 78 | mot_b.throttle = None 79 | mot_c.throttle = None 80 | mot_d.throttle = None 81 | time.sleep(1) 82 | if button_pressed(): # Exit if button is pressed 83 | break 84 | 85 | # Backwards slow 86 | mot_a.throttle = -0.5 87 | mot_b.throttle = -0.5 88 | mot_c.throttle = -0.5 89 | mot_d.throttle = -0.5 90 | time.sleep(1) 91 | if button_pressed(): # Exit if button is pressed 92 | break 93 | 94 | # Stop 95 | mot_a.throttle = 0 96 | mot_b.throttle = 0 97 | mot_c.throttle = 0 98 | mot_d.throttle = 0 99 | time.sleep(1) 100 | if button_pressed(): # Exit if button is pressed 101 | break 102 | 103 | # Backwards fast 104 | mot_a.throttle = -1.0 105 | mot_b.throttle = -1.0 106 | mot_c.throttle = -1.0 107 | mot_d.throttle = -1.0 108 | time.sleep(1) 109 | if button_pressed(): # Exit if button is pressed 110 | break 111 | 112 | # Spin freely 113 | mot_a.throttle = None 114 | mot_b.throttle = None 115 | mot_c.throttle = None 116 | mot_d.throttle = None 117 | time.sleep(1) 118 | if button_pressed(): # Exit if button is pressed 119 | break 120 | -------------------------------------------------------------------------------- /motor2040/onboard_neopixel.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """CircuitPython Essentials Internal RGB LED red, green, blue example""" 6 | import time 7 | import board 8 | 9 | import neopixel 10 | led = neopixel.NeoPixel(board.LED_DATA, board.NUM_LEDS) 11 | 12 | led.brightness = 0.3 13 | 14 | while True: 15 | for i in range(board.NUM_LEDS): 16 | led[i] = (255, 0, 0) 17 | time.sleep(0.5) 18 | 19 | for i in range(board.NUM_LEDS): 20 | led[i] = (0, 255, 0) 21 | time.sleep(0.5) 22 | 23 | for i in range(board.NUM_LEDS): 24 | led[i] = (0, 0, 255) 25 | time.sleep(0.5) 26 | -------------------------------------------------------------------------------- /motor2040/position_control.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | import time 4 | import board 5 | import math 6 | import random 7 | import pwmio 8 | import digitalio 9 | import rotaryio 10 | from adafruit_motor import motor 11 | 12 | # Pin constants 13 | MOTOR_P = board.MOTOR_A_P 14 | MOTOR_N = board.MOTOR_A_N 15 | CHANNEL_A = board.ENCODER_A_A 16 | CHANNEL_B = board.ENCODER_A_B 17 | 18 | # Setting constants 19 | FREQUENCY = 25000 # Chose a frequency above human hearing 20 | DECAY_MODE = motor.SLOW_DECAY # The decay mode affects how the motor 21 | # responds, with SLOW_DECAY having improved spin 22 | # threshold and speed-to-throttle linearity 23 | GEAR_RATIO = 50 # The gear ratio of the motor 24 | COUNTS_PER_REV = 12 * GEAR_RATIO # The counts per revolution of the motor's output shaft 25 | UPDATES = 100 # How many times to update the motor per second 26 | UPDATE_RATE = 1 / UPDATES 27 | TIME_FOR_EACH_MOVE = 1 # The time to travel between each random value 28 | UPDATES_PER_MOVE = TIME_FOR_EACH_MOVE * UPDATES 29 | PRINT_DIVIDER = 4 # How many of the updates should be printed (i.e. 2 would be every other update) 30 | SPEED_SCALE = 5.4 # The scaling to apply to the motor's speed to match its real-world speed 31 | 32 | # Multipliers for the different printed values, so they appear nicely on the Thonny plotter 33 | SPD_PRINT_SCALE = 20 # Driving Speed multipler 34 | 35 | POSITION_EXTENT = 180 # How far from zero to move the motor, in degrees 36 | INTERP_MODE = 2 # The interpolating mode between setpoints. STEP (0), LINEAR (1), COSINE (2) 37 | 38 | 39 | # PID values 40 | POS_KP = 0.14 # Position proportional (P) gain 41 | POS_KI = 0.0 # Position integral (I) gain 42 | POS_KD = 0.0022 # Position derivative (D) gain 43 | 44 | # Create a digitalinout object for the user switch 45 | user_sw = digitalio.DigitalInOut(board.USER_SW) 46 | user_sw.direction = digitalio.Direction.INPUT 47 | user_sw.pull = digitalio.Pull.UP 48 | 49 | # Create the pwm and objects 50 | pwm_p = pwmio.PWMOut(MOTOR_P, frequency=FREQUENCY) 51 | pwm_n = pwmio.PWMOut(MOTOR_N, frequency=FREQUENCY) 52 | mot = motor.DCMotor(pwm_p, pwm_n) 53 | 54 | # Set the motor decay modes (if unset the default will be FAST_DECAY) 55 | mot.decay_mode = DECAY_MODE 56 | 57 | # Create the encoder object 58 | encoder = rotaryio.IncrementalEncoder(CHANNEL_B, CHANNEL_A, divisor=1) 59 | 60 | 61 | # A simple class for handling Proportional, Integral & Derivative (PID) control calculations 62 | class PID: 63 | def __init__(self, kp, ki, kd, sample_rate): 64 | self.kp = kp 65 | self.ki = ki 66 | self.kd = kd 67 | self.setpoint = 0 68 | self._error_sum = 0 69 | self._last_value = 0 70 | self._sample_rate = sample_rate 71 | 72 | def calculate(self, value): 73 | error = self.setpoint - value 74 | self._error_sum += error * self._sample_rate 75 | rate_error = (value - self._last_value) / self._sample_rate 76 | self._last_value = value 77 | 78 | return (error * self.kp) + (self._error_sum * self.ki) - (rate_error * self.kd) 79 | 80 | 81 | def button_pressed(): 82 | return not user_sw.value 83 | 84 | 85 | def to_degrees(position): 86 | return (position * 360.0) / COUNTS_PER_REV 87 | 88 | 89 | # Create PID object for position control 90 | pos_pid = PID(POS_KP, POS_KI, POS_KD, UPDATE_RATE) 91 | 92 | update = 0 93 | print_count = 0 94 | 95 | # Set the initial value and create a random end value between the extents 96 | start_value = 0.0 97 | end_value = random.uniform(-POSITION_EXTENT, POSITION_EXTENT) 98 | 99 | # Run until the user switch is pressed 100 | while not button_pressed(): 101 | 102 | # Capture the state of the encoder 103 | angle = to_degrees(encoder.position) 104 | 105 | # Calculate how far along this movement to be 106 | percent_along = min(update / UPDATES_PER_MOVE, 1.0) 107 | 108 | if INTERP_MODE == 0: 109 | # Move the motor instantly to the end value 110 | pos_pid.setpoint = end_value 111 | elif INTERP_MODE == 2: 112 | # Move the motor between values using cosine 113 | pos_pid.setpoint = (((-math.cos(percent_along * math.pi) + 1.0) / 2.0) * (end_value - start_value)) + start_value 114 | else: 115 | # Move the motor linearly between values 116 | pos_pid.setpoint = (percent_along * (end_value - start_value)) + start_value 117 | 118 | # Calculate the velocity to move the motor closer to the position setpoint 119 | vel = pos_pid.calculate(angle) 120 | 121 | # Set the new motor driving speed 122 | mot.throttle = max(min(vel / SPEED_SCALE, 1.0), -1.0) 123 | 124 | # Print out the current motor values and their setpoints, but only on every multiple 125 | if print_count == 0: 126 | print("Pos =", angle, end=", ") 127 | print("Pos SP =", pos_pid.setpoint, end=", ") 128 | print("Speed =", mot.throttle * SPEED_SCALE * SPD_PRINT_SCALE) 129 | 130 | # Increment the print count, and wrap it 131 | print_count = (print_count + 1) % PRINT_DIVIDER 132 | 133 | update += 1 # Move along in time 134 | 135 | # Have we reached the end of this movement? 136 | if update >= UPDATES_PER_MOVE: 137 | update = 0 # Reset the counter 138 | 139 | # Set the start as the last end and create a new random end value 140 | start_value = end_value 141 | end_value = random.uniform(-POSITION_EXTENT, POSITION_EXTENT) 142 | 143 | time.sleep(UPDATE_RATE) 144 | -------------------------------------------------------------------------------- /motor2040/quad_position_wave.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | import time 4 | import board 5 | import math 6 | import pwmio 7 | import digitalio 8 | import rotaryio 9 | from adafruit_motor import motor 10 | 11 | # Setting constants 12 | FREQUENCY = 25000 # Chose a frequency above human hearing 13 | DECAY_MODE = motor.SLOW_DECAY # The decay mode affects how the motor 14 | # responds, with SLOW_DECAY having improved spin 15 | # threshold and speed-to-throttle linearity 16 | GEAR_RATIO = 50 # The gear ratio of the motor 17 | COUNTS_PER_REV = 12 * GEAR_RATIO # The counts per revolution of the motor's output shaft 18 | UPDATES = 100 # How many times to update the motor per second 19 | UPDATE_RATE = 1 / UPDATES 20 | TIME_FOR_EACH_MOVE = 1 # The time to travel between each random value 21 | UPDATES_PER_MOVE = TIME_FOR_EACH_MOVE * UPDATES 22 | PRINT_DIVIDER = 4 # How many of the updates should be printed (i.e. 2 would be every other update) 23 | SPEED_SCALE = 5.4 # The scaling to apply to the motor's speed to match its real-world speed 24 | 25 | # Multipliers for the different printed values, so they appear nicely on the Thonny plotter 26 | SPD_PRINT_SCALE = 20 # Driving Speed multipler 27 | 28 | POSITION_EXTENT = 180 # How far from zero to move the motor, in degrees 29 | INTERP_MODE = 2 # The interpolating mode between setpoints. STEP (0), LINEAR (1), COSINE (2) 30 | 31 | # PID values 32 | POS_KP = 0.14 # Position proportional (P) gain 33 | POS_KI = 0.0 # Position integral (I) gain 34 | POS_KD = 0.0022 # Position derivative (D) gain 35 | 36 | # Create a digitalinout object for the user switch 37 | user_sw = digitalio.DigitalInOut(board.USER_SW) 38 | user_sw.direction = digitalio.Direction.INPUT 39 | user_sw.pull = digitalio.Pull.UP 40 | 41 | # Create the pwm objects 42 | pwm_a_p = pwmio.PWMOut(board.MOTOR_A_P, frequency=FREQUENCY) 43 | pwm_a_n = pwmio.PWMOut(board.MOTOR_A_N, frequency=FREQUENCY) 44 | pwm_b_p = pwmio.PWMOut(board.MOTOR_B_P, frequency=FREQUENCY) 45 | pwm_b_n = pwmio.PWMOut(board.MOTOR_B_N, frequency=FREQUENCY) 46 | pwm_c_p = pwmio.PWMOut(board.MOTOR_C_P, frequency=FREQUENCY) 47 | pwm_c_n = pwmio.PWMOut(board.MOTOR_C_N, frequency=FREQUENCY) 48 | pwm_d_p = pwmio.PWMOut(board.MOTOR_D_P, frequency=FREQUENCY) 49 | pwm_d_n = pwmio.PWMOut(board.MOTOR_D_N, frequency=FREQUENCY) 50 | 51 | # Create the motor objects 52 | mot_a = motor.DCMotor(pwm_a_p, pwm_a_n) 53 | mot_b = motor.DCMotor(pwm_b_n, pwm_b_p) # Reversed direction 54 | mot_c = motor.DCMotor(pwm_c_n, pwm_c_p) # Reversed direction 55 | mot_d = motor.DCMotor(pwm_d_p, pwm_d_n) 56 | motors = [mot_a, mot_b, mot_c, mot_d] 57 | 58 | # Set the motor decay modes (if unset the default will be FAST_DECAY) 59 | mot_a.decay_mode = DECAY_MODE 60 | mot_b.decay_mode = DECAY_MODE 61 | mot_c.decay_mode = DECAY_MODE 62 | mot_d.decay_mode = DECAY_MODE 63 | 64 | # Create the encoder objects 65 | enc_a = rotaryio.IncrementalEncoder(board.ENCODER_A_B, board.ENCODER_A_A, divisor=1) 66 | enc_b = rotaryio.IncrementalEncoder(board.ENCODER_B_A, board.ENCODER_B_B, divisor=1) # Reversed direction 67 | enc_c = rotaryio.IncrementalEncoder(board.ENCODER_C_A, board.ENCODER_C_B, divisor=1) # Reversed direction 68 | enc_d = rotaryio.IncrementalEncoder(board.ENCODER_D_B, board.ENCODER_D_A, divisor=1) 69 | encoders = [enc_a, enc_b, enc_c, enc_d] 70 | ENCODER_NAMES = ["A", "B", "C", "D"] 71 | 72 | 73 | # A simple class for handling Proportional, Integral & Derivative (PID) control calculations 74 | class PID: 75 | def __init__(self, kp, ki, kd, sample_rate): 76 | self.kp = kp 77 | self.ki = ki 78 | self.kd = kd 79 | self.setpoint = 0 80 | self._error_sum = 0 81 | self._last_value = 0 82 | self._sample_rate = sample_rate 83 | 84 | def calculate(self, value): 85 | error = self.setpoint - value 86 | self._error_sum += error * self._sample_rate 87 | rate_error = (value - self._last_value) / self._sample_rate 88 | self._last_value = value 89 | 90 | return (error * self.kp) + (self._error_sum * self.ki) - (rate_error * self.kd) 91 | 92 | 93 | def button_pressed(): 94 | return not user_sw.value 95 | 96 | 97 | def to_degrees(position): 98 | return (position * 360.0) / COUNTS_PER_REV 99 | 100 | 101 | # Create PID object for position control 102 | pos_pids = [PID(POS_KP, POS_KI, POS_KD, UPDATE_RATE) for i in range(board.NUM_MOTORS)] 103 | 104 | update = 0 105 | print_count = 0 106 | 107 | # Set the initial and end values 108 | start_value = 0.0 109 | end_value = 270.0 110 | 111 | angles = [0.0] * board.NUM_MOTORS 112 | 113 | # Run until the user switch is pressed 114 | while not button_pressed(): 115 | 116 | # Capture the state of the encoders 117 | for i in range(board.NUM_MOTORS): 118 | angles[i] = to_degrees(encoders[i].position) 119 | 120 | # Calculate how far along this movement to be 121 | percent_along = min(update / UPDATES_PER_MOVE, 1.0) 122 | 123 | for i in range(board.NUM_MOTORS): 124 | # Move the motor between values using cosine 125 | pos_pids[i].setpoint = (((-math.cos(percent_along * math.pi) + 1.0) / 2.0) * (end_value - start_value)) + start_value 126 | 127 | # Calculate the velocity to move the motor closer to the position setpoint 128 | vel = pos_pids[i].calculate(angles[i]) 129 | 130 | # Set the new motor driving speed 131 | motors[i].throttle = max(min(vel / SPEED_SCALE, 1.0), -1.0) 132 | 133 | # Print out the current motor values and their setpoints, but only on every multiple 134 | if print_count == 0: 135 | for i in range(board.NUM_MOTORS): 136 | print(ENCODER_NAMES[i], "=", angles[i], end=", ") 137 | print() 138 | 139 | # Increment the print count, and wrap it 140 | print_count = (print_count + 1) % PRINT_DIVIDER 141 | 142 | update += 1 # Move along in time 143 | 144 | # Have we reached the end of this movement? 145 | if update >= UPDATES_PER_MOVE: 146 | update = 0 # Reset the counter 147 | 148 | # Swap the start and end values 149 | temp = start_value 150 | start_value = end_value 151 | end_value = temp 152 | 153 | time.sleep(UPDATE_RATE) 154 | -------------------------------------------------------------------------------- /motor2040/quad_velocity_sequence.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | import time 4 | import board 5 | import pwmio 6 | import digitalio 7 | import rotaryio 8 | from adafruit_motor import motor 9 | 10 | # Wheel friendly names 11 | FL = 2 12 | FR = 3 13 | RL = 1 14 | RR = 0 15 | 16 | # Setting constants 17 | FREQUENCY = 25000 # Chose a frequency above human hearing 18 | DECAY_MODE = motor.SLOW_DECAY # The decay mode affects how the motor 19 | # responds, with SLOW_DECAY having improved spin 20 | # threshold and speed-to-throttle linearity 21 | GEAR_RATIO = 50 # The gear ratio of the motor 22 | COUNTS_PER_REV = 12 * GEAR_RATIO # The counts per revolution of the motor's output shaft 23 | 24 | SPEED_SCALE = 5.4 # The scaling to apply to each motor's speed to match its real-world speed 25 | 26 | UPDATES = 100 # How many times to update the motor per second 27 | UPDATE_RATE = 1 / UPDATES 28 | TIME_FOR_EACH_MOVE = 1 # The time to travel between each random value 29 | UPDATES_PER_MOVE = TIME_FOR_EACH_MOVE * UPDATES 30 | PRINT_DIVIDER = 4 # How many of the updates should be printed (i.e. 2 would be every other update) 31 | 32 | DRIVING_SPEED = 1.0 # The speed to drive the wheels at, from 0.0 to SPEED_SCALE 33 | 34 | # PID values 35 | VEL_KP = 30.0 # Velocity proportional (P) gain 36 | VEL_KI = 0.0 # Velocity integral (I) gain 37 | VEL_KD = 0.4 # Velocity derivative (D) gain 38 | 39 | # Create a digitalinout object for the user switch 40 | user_sw = digitalio.DigitalInOut(board.USER_SW) 41 | user_sw.direction = digitalio.Direction.INPUT 42 | user_sw.pull = digitalio.Pull.UP 43 | 44 | # Create the pwm objects 45 | pwm_a_p = pwmio.PWMOut(board.MOTOR_A_P, frequency=FREQUENCY) 46 | pwm_a_n = pwmio.PWMOut(board.MOTOR_A_N, frequency=FREQUENCY) 47 | pwm_b_p = pwmio.PWMOut(board.MOTOR_B_P, frequency=FREQUENCY) 48 | pwm_b_n = pwmio.PWMOut(board.MOTOR_B_N, frequency=FREQUENCY) 49 | pwm_c_p = pwmio.PWMOut(board.MOTOR_C_P, frequency=FREQUENCY) 50 | pwm_c_n = pwmio.PWMOut(board.MOTOR_C_N, frequency=FREQUENCY) 51 | pwm_d_p = pwmio.PWMOut(board.MOTOR_D_P, frequency=FREQUENCY) 52 | pwm_d_n = pwmio.PWMOut(board.MOTOR_D_N, frequency=FREQUENCY) 53 | 54 | # Create the motor objects 55 | mot_a = motor.DCMotor(pwm_a_p, pwm_a_n) 56 | mot_b = motor.DCMotor(pwm_b_n, pwm_b_p) # Reversed direction 57 | mot_c = motor.DCMotor(pwm_c_n, pwm_c_p) # Reversed direction 58 | mot_d = motor.DCMotor(pwm_d_p, pwm_d_n) 59 | motors = [mot_a, mot_b, mot_c, mot_d] 60 | 61 | # Set the motor decay modes (if unset the default will be FAST_DECAY) 62 | mot_a.decay_mode = DECAY_MODE 63 | mot_b.decay_mode = DECAY_MODE 64 | mot_c.decay_mode = DECAY_MODE 65 | mot_d.decay_mode = DECAY_MODE 66 | 67 | # Create the encoder objects 68 | enc_a = rotaryio.IncrementalEncoder(board.ENCODER_A_B, board.ENCODER_A_A, divisor=1) 69 | enc_b = rotaryio.IncrementalEncoder(board.ENCODER_B_A, board.ENCODER_B_B, divisor=1) # Reversed direction 70 | enc_c = rotaryio.IncrementalEncoder(board.ENCODER_C_A, board.ENCODER_C_B, divisor=1) # Reversed direction 71 | enc_d = rotaryio.IncrementalEncoder(board.ENCODER_D_B, board.ENCODER_D_A, divisor=1) 72 | encoders = [enc_a, enc_b, enc_c, enc_d] 73 | ENCODER_NAMES = ["RR", "RL", "FL", "FR"] 74 | 75 | 76 | # A simple class for handling Proportional, Integral & Derivative (PID) control calculations 77 | class PID: 78 | def __init__(self, kp, ki, kd, sample_rate): 79 | self.kp = kp 80 | self.ki = ki 81 | self.kd = kd 82 | self.setpoint = 0 83 | self._error_sum = 0 84 | self._last_value = 0 85 | self._sample_rate = sample_rate 86 | 87 | def calculate(self, value): 88 | error = self.setpoint - value 89 | self._error_sum += error * self._sample_rate 90 | rate_error = (value - self._last_value) / self._sample_rate 91 | self._last_value = value 92 | 93 | return (error * self.kp) + (self._error_sum * self.ki) - (rate_error * self.kd) 94 | 95 | 96 | def button_pressed(): 97 | return not user_sw.value 98 | 99 | 100 | def to_revs(position): 101 | return position / COUNTS_PER_REV 102 | 103 | 104 | # Helper functions for driving in common directions 105 | def drive_forward(speed): 106 | vel_pids[FL].setpoint = speed 107 | vel_pids[FR].setpoint = speed 108 | vel_pids[RL].setpoint = speed 109 | vel_pids[RR].setpoint = speed 110 | 111 | 112 | def turn_right(speed): 113 | vel_pids[FL].setpoint = speed 114 | vel_pids[FR].setpoint = -speed 115 | vel_pids[RL].setpoint = speed 116 | vel_pids[RR].setpoint = -speed 117 | 118 | 119 | def strafe_right(speed): 120 | vel_pids[FL].setpoint = speed 121 | vel_pids[FR].setpoint = -speed 122 | vel_pids[RL].setpoint = -speed 123 | vel_pids[RR].setpoint = speed 124 | 125 | 126 | def stop(): 127 | vel_pids[FL].setpoint = 0 128 | vel_pids[FR].setpoint = 0 129 | vel_pids[RL].setpoint = 0 130 | vel_pids[RR].setpoint = 0 131 | 132 | 133 | # Create PID objects for velocity control 134 | vel_pids = [PID(VEL_KP, VEL_KI, VEL_KD, UPDATE_RATE) for i in range(board.NUM_MOTORS)] 135 | 136 | update = 0 137 | print_count = 0 138 | sequence = 0 139 | 140 | # Initialise the motors 141 | for i in range(board.NUM_MOTORS): 142 | motors[i].throttle = 0.0 143 | 144 | revs = [0.0] * board.NUM_MOTORS 145 | last_revs = [0.0] * board.NUM_MOTORS 146 | 147 | # Run until the user switch is pressed 148 | while not button_pressed(): 149 | 150 | for i in range(board.NUM_MOTORS): 151 | # Capture the state of the encoder 152 | last_revs[i] = revs[i] 153 | revs[i] = to_revs(encoders[i].position) 154 | 155 | for i in range(board.NUM_MOTORS): 156 | vel = (revs[i] - last_revs[i]) / UPDATE_RATE 157 | 158 | # Calculate the acceleration to apply to the motor to move it closer to the velocity setpoint 159 | accel = vel_pids[i].calculate(vel) 160 | 161 | # Accelerate or decelerate the motor 162 | motors[i].throttle = max(min(motors[i].throttle + ((accel * UPDATE_RATE) / SPEED_SCALE), 1.0), -1.0) 163 | 164 | # Print out the current motor values, but only on every multiple 165 | if print_count == 0: 166 | for i in range(board.NUM_MOTORS): 167 | print(ENCODER_NAMES[i], "=", revs[i], end=", ") 168 | print() 169 | 170 | # Increment the print count, and wrap it 171 | print_count = (print_count + 1) % PRINT_DIVIDER 172 | 173 | update += 1 # Move along in time 174 | 175 | # Have we reached the end of this movement? 176 | if update >= UPDATES_PER_MOVE: 177 | update = 0 # Reset the counter 178 | 179 | # Move on to the next part of the sequence 180 | sequence += 1 181 | 182 | # Loop the sequence back around 183 | if sequence >= 7: 184 | sequence = 0 185 | 186 | # Set the motor speeds, based on the sequence 187 | if sequence == 0: 188 | drive_forward(DRIVING_SPEED) 189 | elif sequence == 1: 190 | drive_forward(-DRIVING_SPEED) 191 | elif sequence == 2: 192 | turn_right(DRIVING_SPEED) 193 | elif sequence == 3: 194 | turn_right(-DRIVING_SPEED) 195 | elif sequence == 4: 196 | strafe_right(DRIVING_SPEED) 197 | elif sequence == 5: 198 | strafe_right(-DRIVING_SPEED) 199 | elif sequence == 6: 200 | stop() 201 | 202 | time.sleep(UPDATE_RATE) 203 | -------------------------------------------------------------------------------- /motor2040/read_encoders.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | import time 4 | import board 5 | import digitalio 6 | import rotaryio 7 | 8 | # Encoder constants 9 | GEAR_RATIO = 50 # The gear ratio of the motor 10 | COUNTS_PER_REV = 12 * GEAR_RATIO # The counts per revolution of the motor's output shaft 11 | ENCODER_NAMES = ["A", "B", "C", "D"] 12 | 13 | # Create a digitalinout object for the user switch 14 | user_sw = digitalio.DigitalInOut(board.USER_SW) 15 | user_sw.direction = digitalio.Direction.INPUT 16 | user_sw.pull = digitalio.Pull.UP 17 | 18 | # Create the encoder objects 19 | enc_a = rotaryio.IncrementalEncoder(board.ENCODER_A_B, board.ENCODER_A_A, divisor=1) 20 | enc_b = rotaryio.IncrementalEncoder(board.ENCODER_B_B, board.ENCODER_B_A, divisor=1) 21 | enc_c = rotaryio.IncrementalEncoder(board.ENCODER_C_B, board.ENCODER_C_A, divisor=1) 22 | enc_d = rotaryio.IncrementalEncoder(board.ENCODER_D_B, board.ENCODER_D_A, divisor=1) 23 | encoders = [enc_a, enc_b, enc_c, enc_d] 24 | 25 | 26 | def button_pressed(): 27 | return not user_sw.value 28 | 29 | 30 | def to_degrees(position): 31 | return (position * 360.0) / COUNTS_PER_REV 32 | 33 | 34 | # Run until the user switch is pressed 35 | while not button_pressed(): 36 | 37 | # Print out the angle of each encoder 38 | for i in range(board.NUM_ENCODERS): 39 | print(ENCODER_NAMES[i], "=", to_degrees(encoders[i].position), end=", ") 40 | print() 41 | 42 | time.sleep(0.1) 43 | -------------------------------------------------------------------------------- /motor2040/sensor_reading.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """CircuitPython Essentials Analog In example""" 6 | import time 7 | import board 8 | from digitalio import DigitalInOut, Direction 9 | from analogio import AnalogIn 10 | 11 | addr0_pin = DigitalInOut(board.ADC_ADDR_0) 12 | addr0_pin.direction = Direction.OUTPUT 13 | 14 | addr1_pin = DigitalInOut(board.ADC_ADDR_1) 15 | addr1_pin.direction = Direction.OUTPUT 16 | 17 | addr2_pin = DigitalInOut(board.ADC_ADDR_2) 18 | addr2_pin.direction = Direction.OUTPUT 19 | 20 | analog_in = AnalogIn(board.SHARED_ADC) 21 | 22 | 23 | def get_voltage(pin): 24 | return (pin.value * 3.3) / 65536 25 | 26 | 27 | def select(address): 28 | addr0_pin.value = address & 0b001 29 | addr1_pin.value = address & 0b010 30 | addr2_pin.value = address & 0b100 31 | 32 | 33 | VOLTAGE_GAIN = 13.9 / 3.9 34 | CURRENT_GAIN = 1 / 0.47 35 | CURRENT_OFFSET = -0.005 36 | 37 | while True: 38 | # Read each sensor in turn and print its voltage 39 | for i in range(board.NUM_SENSORS): 40 | select(i + board.SENSOR_1_ADDR) 41 | print("S", i + 1, " = ", round(get_voltage(analog_in), 3), sep="", end=", ") 42 | 43 | # Read the voltage sense and print the value 44 | select(board.VOLTAGE_SENSE_ADDR) 45 | voltage = get_voltage(analog_in) * VOLTAGE_GAIN 46 | print("Voltage =", round(voltage, 4), end=", ") 47 | 48 | # Read the current sense and print the value 49 | for i in range(board.NUM_MOTORS): 50 | select(i + board.CURRENT_SENSE_A_ADDR) 51 | current = (get_voltage(analog_in) + CURRENT_OFFSET) * CURRENT_GAIN 52 | print("C", i + 1, " = ", round(current, 4), sep="", end=", ") 53 | 54 | print() 55 | 56 | time.sleep(0.5) 57 | -------------------------------------------------------------------------------- /motor2040/single_encoder.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | import board 6 | import digitalio 7 | import rotaryio 8 | 9 | # Pins of the motor encoder to read 10 | CHANNEL_A = board.ENCODER_A_A 11 | CHANNEL_B = board.ENCODER_A_B 12 | 13 | # Encoder constants 14 | REVERSED = True # Whether to reverse the counting direction (set to True if using MMME) 15 | 16 | # Create a digitalinout object for the user switch 17 | user_sw = digitalio.DigitalInOut(board.USER_SW) 18 | user_sw.direction = digitalio.Direction.INPUT 19 | user_sw.pull = digitalio.Pull.UP 20 | 21 | # Create the encoder object 22 | if REVERSED: 23 | encoder = rotaryio.IncrementalEncoder(CHANNEL_B, CHANNEL_A, divisor=1) 24 | else: 25 | encoder = rotaryio.IncrementalEncoder(CHANNEL_A, CHANNEL_B, divisor=1) 26 | 27 | 28 | last_position = None 29 | 30 | 31 | def button_pressed(): 32 | return not user_sw.value 33 | 34 | 35 | # Run until the user switch is pressed 36 | while not button_pressed(): 37 | position = encoder.position 38 | if last_position is None or position != last_position: 39 | print(position) 40 | last_position = position 41 | -------------------------------------------------------------------------------- /motor2040/single_motor.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | import time 4 | import board 5 | import pwmio 6 | import digitalio 7 | from adafruit_motor import motor 8 | 9 | # Pins of the motor to drive 10 | MOTOR_P = board.MOTOR_A_P 11 | MOTOR_N = board.MOTOR_A_N 12 | 13 | # Motor constants 14 | FREQUENCY = 25000 # Chose a frequency above human hearing 15 | DECAY_MODE = motor.SLOW_DECAY # The decay mode affects how the motor 16 | # responds, with SLOW_DECAY having improved spin 17 | # threshold and speed-to-throttle linearity 18 | 19 | # Create a digitalinout object for the user switch 20 | user_sw = digitalio.DigitalInOut(board.USER_SW) 21 | user_sw.direction = digitalio.Direction.INPUT 22 | user_sw.pull = digitalio.Pull.UP 23 | 24 | # Create the pwm and motor objects 25 | pwm_p = pwmio.PWMOut(MOTOR_P, frequency=FREQUENCY) 26 | pwm_n = pwmio.PWMOut(MOTOR_N, frequency=FREQUENCY) 27 | mot = motor.DCMotor(pwm_p, pwm_n) 28 | 29 | # Set the motor decay modes (if unset the default will be FAST_DECAY) 30 | mot.decay_mode = DECAY_MODE 31 | 32 | 33 | def button_pressed(): 34 | return not user_sw.value 35 | 36 | 37 | # Run the motor sequence 38 | while True: 39 | # Forward slow 40 | mot.throttle = 0.5 41 | time.sleep(1) 42 | if button_pressed(): # Exit if button is pressed 43 | break 44 | 45 | # Stop 46 | mot.throttle = 0 47 | time.sleep(1) 48 | if button_pressed(): # Exit if button is pressed 49 | break 50 | 51 | # Forward fast 52 | mot.throttle = 1.0 53 | time.sleep(1) 54 | if button_pressed(): # Exit if button is pressed 55 | break 56 | 57 | # Spin freely 58 | mot.throttle = None 59 | time.sleep(1) 60 | if button_pressed(): # Exit if button is pressed 61 | break 62 | 63 | # Backwards slow 64 | mot.throttle = -0.5 65 | time.sleep(1) 66 | if button_pressed(): # Exit if button is pressed 67 | break 68 | 69 | # Stop 70 | mot.throttle = 0 71 | time.sleep(1) 72 | if button_pressed(): # Exit if button is pressed 73 | break 74 | 75 | # Backwards fast 76 | mot.throttle = -1.0 77 | time.sleep(1) 78 | if button_pressed(): # Exit if button is pressed 79 | break 80 | 81 | # Spin freely 82 | mot.throttle = None 83 | time.sleep(1) 84 | if button_pressed(): # Exit if button is pressed 85 | break 86 | -------------------------------------------------------------------------------- /motor2040/velocity_control.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | import time 4 | import board 5 | import math 6 | import random 7 | import pwmio 8 | import digitalio 9 | import rotaryio 10 | from adafruit_motor import motor 11 | 12 | # Pin constants 13 | MOTOR_P = board.MOTOR_A_P 14 | MOTOR_N = board.MOTOR_A_N 15 | CHANNEL_A = board.ENCODER_A_A 16 | CHANNEL_B = board.ENCODER_A_B 17 | 18 | # Setting constants 19 | FREQUENCY = 25000 # Chose a frequency above human hearing 20 | DECAY_MODE = motor.SLOW_DECAY # The decay mode affects how the motor 21 | # responds, with SLOW_DECAY having improved spin 22 | # threshold and speed-to-throttle linearity 23 | GEAR_RATIO = 50 # The gear ratio of the motor 24 | COUNTS_PER_REV = 12 * GEAR_RATIO # The counts per revolution of the motor's output shaft 25 | 26 | SPEED_SCALE = 5.4 # The scaling to apply to each motor's speed to match its real-world speed 27 | 28 | UPDATES = 100 # How many times to update the motor per second 29 | UPDATE_RATE = 1 / UPDATES 30 | TIME_FOR_EACH_MOVE = 1 # The time to travel between each random value 31 | UPDATES_PER_MOVE = TIME_FOR_EACH_MOVE * UPDATES 32 | PRINT_DIVIDER = 4 # How many of the updates should be printed (i.e. 2 would be every other update) 33 | 34 | # Multipliers for the different printed values, so they appear nicely on the Thonny plotter 35 | ACC_PRINT_SCALE = 0.05 # Acceleration multiplier 36 | 37 | VELOCITY_EXTENT = 3 # How far from zero to drive the motor at, in revolutions per second 38 | INTERP_MODE = 2 # The interpolating mode between setpoints. STEP (0), LINEAR (1), COSINE (2) 39 | 40 | # PID values 41 | VEL_KP = 30.0 # Velocity proportional (P) gain 42 | VEL_KI = 0.0 # Velocity integral (I) gain 43 | VEL_KD = 0.4 # Velocity derivative (D) gain 44 | 45 | # Create a digitalinout object for the user switch 46 | user_sw = digitalio.DigitalInOut(board.USER_SW) 47 | user_sw.direction = digitalio.Direction.INPUT 48 | user_sw.pull = digitalio.Pull.UP 49 | 50 | # Create the pwm and objects 51 | pwm_p = pwmio.PWMOut(MOTOR_P, frequency=FREQUENCY) 52 | pwm_n = pwmio.PWMOut(MOTOR_N, frequency=FREQUENCY) 53 | mot = motor.DCMotor(pwm_p, pwm_n) 54 | 55 | # Set the motor decay modes (if unset the default will be FAST_DECAY) 56 | mot.decay_mode = DECAY_MODE 57 | 58 | # Create the encoder object 59 | encoder = rotaryio.IncrementalEncoder(CHANNEL_B, CHANNEL_A, divisor=1) 60 | 61 | 62 | # A simple class for handling Proportional, Integral & Derivative (PID) control calculations 63 | class PID: 64 | def __init__(self, kp, ki, kd, sample_rate): 65 | self.kp = kp 66 | self.ki = ki 67 | self.kd = kd 68 | self.setpoint = 0 69 | self._error_sum = 0 70 | self._last_value = 0 71 | self._sample_rate = sample_rate 72 | 73 | def calculate(self, value): 74 | error = self.setpoint - value 75 | self._error_sum += error * self._sample_rate 76 | rate_error = (value - self._last_value) / self._sample_rate 77 | self._last_value = value 78 | 79 | return (error * self.kp) + (self._error_sum * self.ki) - (rate_error * self.kd) 80 | 81 | 82 | def button_pressed(): 83 | return not user_sw.value 84 | 85 | 86 | def to_revs(position): 87 | return position / COUNTS_PER_REV 88 | 89 | 90 | # Create PID object for velocity control 91 | vel_pid = PID(VEL_KP, VEL_KI, VEL_KD, UPDATE_RATE) 92 | 93 | update = 0 94 | print_count = 0 95 | 96 | # Initialise the motor 97 | mot.throttle = 0.0 98 | 99 | # Set the initial value and create a random end value between the extents 100 | start_value = 0.0 101 | end_value = random.uniform(-VELOCITY_EXTENT, VELOCITY_EXTENT) 102 | 103 | revs = 0.0 104 | last_revs = 0.0 105 | 106 | # Run until the user switch is pressed 107 | while not button_pressed(): 108 | 109 | # Capture the state of the encoder 110 | last_revs = revs 111 | revs = to_revs(encoder.position) 112 | 113 | # Calculate how far along this movement to be 114 | percent_along = min(update / UPDATES_PER_MOVE, 1.0) 115 | 116 | if INTERP_MODE == 0: 117 | # Move the motor instantly to the end value 118 | vel_pid.setpoint = end_value 119 | elif INTERP_MODE == 2: 120 | # Move the motor between values using cosine 121 | vel_pid.setpoint = (((-math.cos(percent_along * math.pi) + 1.0) / 2.0) * (end_value - start_value)) + start_value 122 | else: 123 | # Move the motor linearly between values 124 | vel_pid.setpoint = (percent_along * (end_value - start_value)) + start_value 125 | 126 | # Calculate the acceleration to apply to the motor to move it closer to the velocity setpoint 127 | vel = (revs - last_revs) / UPDATE_RATE 128 | accel = vel_pid.calculate(vel) 129 | 130 | # Set the new motor driving speed 131 | mot.throttle = max(min(mot.throttle + ((accel * UPDATE_RATE) / SPEED_SCALE), 1.0), -1.0) 132 | 133 | # Print out the current motor values and their setpoints, but only on every multiple 134 | if print_count == 0: 135 | print("Vel =", vel, end=", ") 136 | print("Vel SP =", vel_pid.setpoint, end=", ") 137 | print("Accel =", accel * ACC_PRINT_SCALE, end=", ") 138 | print("Speed =", mot.throttle * SPEED_SCALE) 139 | 140 | # Increment the print count, and wrap it 141 | print_count = (print_count + 1) % PRINT_DIVIDER 142 | 143 | update += 1 # Move along in time 144 | 145 | # Have we reached the end of this movement? 146 | if update >= UPDATES_PER_MOVE: 147 | update = 0 # Reset the counter 148 | 149 | # Set the start as the last end and create a new random end value 150 | start_value = end_value 151 | end_value = random.uniform(-VELOCITY_EXTENT, VELOCITY_EXTENT) 152 | 153 | time.sleep(UPDATE_RATE) 154 | -------------------------------------------------------------------------------- /pico_motor_shim/sequence.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | import time 4 | import board 5 | import pwmio 6 | import digitalio 7 | from adafruit_motor import motor 8 | 9 | # Pin names for the Pico Motor Shim 10 | BUTTON_A = board.GP2 11 | MOTOR_1_P = board.GP6 12 | MOTOR_1_N = board.GP7 13 | MOTOR_2_P = board.GP27 14 | MOTOR_2_N = board.GP26 15 | 16 | # Motor constants 17 | FREQUENCY = 25000 # Chose a frequency above human hearing 18 | DECAY_MODE = motor.SLOW_DECAY # The decay mode affects how the motor 19 | # responds, with SLOW_DECAY having improved spin 20 | # threshold and speed-to-throttle linearity 21 | 22 | # Create a digitalinout object for the button 23 | button_a = digitalio.DigitalInOut(BUTTON_A) 24 | button_a.direction = digitalio.Direction.INPUT 25 | button_a.pull = digitalio.Pull.UP 26 | 27 | # Create a digitalinout object for the Pico's LED 28 | led = digitalio.DigitalInOut(board.LED) 29 | led.direction = digitalio.Direction.OUTPUT 30 | 31 | # Create the pwm and motor objects 32 | pwm_1p = pwmio.PWMOut(MOTOR_1_P, frequency=FREQUENCY) 33 | pwm_1n = pwmio.PWMOut(MOTOR_1_N, frequency=FREQUENCY) 34 | motor1 = motor.DCMotor(pwm_1p, pwm_1n) 35 | 36 | pwm_2p = pwmio.PWMOut(MOTOR_2_P, frequency=FREQUENCY) 37 | pwm_2n = pwmio.PWMOut(MOTOR_2_N, frequency=FREQUENCY) 38 | motor2 = motor.DCMotor(pwm_2p, pwm_2n) 39 | 40 | # Set the motor decay modes (if unset the default will be FAST_DECAY) 41 | motor1.decay_mode = DECAY_MODE 42 | motor2.decay_mode = DECAY_MODE 43 | 44 | 45 | # Turn on the Pico's LED to show the program is running 46 | led.value = True 47 | 48 | def button_pressed(): 49 | return not button_a.value 50 | 51 | # Run the motor sequence 52 | while True: 53 | # Forward slow 54 | motor1.throttle = 0.5 55 | motor2.throttle = -0.5 56 | time.sleep(1) 57 | if button_pressed(): # Exit if button is pressed 58 | break 59 | 60 | # Stop 61 | motor1.throttle = 0 62 | motor2.throttle = 0 63 | time.sleep(1) 64 | if button_pressed(): # Exit if button is pressed 65 | break 66 | 67 | # Forward fast 68 | motor1.throttle = 1.0 69 | motor2.throttle = -1.0 70 | time.sleep(1) 71 | if button_pressed(): # Exit if button is pressed 72 | break 73 | 74 | # Spin freely 75 | motor1.throttle = None 76 | motor2.throttle = None 77 | time.sleep(1) 78 | if button_pressed(): # Exit if button is pressed 79 | break 80 | 81 | # Backwards slow 82 | motor1.throttle = -0.5 83 | motor2.throttle = 0.5 84 | time.sleep(1) 85 | if button_pressed(): # Exit if button is pressed 86 | break 87 | 88 | # Stop 89 | motor1.throttle = 0 90 | motor2.throttle = 0 91 | time.sleep(1) 92 | if button_pressed(): # Exit if button is pressed 93 | break 94 | 95 | # Backwards fast 96 | motor1.throttle = -1.0 97 | motor2.throttle = 1.0 98 | time.sleep(1) 99 | if button_pressed(): # Exit if button is pressed 100 | break 101 | 102 | # Spin freely 103 | motor1.throttle = None 104 | motor2.throttle = None 105 | time.sleep(1) 106 | if button_pressed(): # Exit if button is pressed 107 | break 108 | -------------------------------------------------------------------------------- /plasma2040/rainbow.py: -------------------------------------------------------------------------------- 1 | import time 2 | import board 3 | import digitalio 4 | from analogio import AnalogIn 5 | import adafruit_rgbled 6 | import busio 7 | import neopixel 8 | import adafruit_dotstar as dotstar 9 | import math 10 | 11 | # Press "B" to speed up the LED cycling effect. 12 | # Press "A" to slow it down again. 13 | # Press "Boot" to reset the speed back to default. 14 | 15 | 16 | # Set how many LEDs you have 17 | NUM_LEDS = 30 18 | 19 | # The speed that the LEDs will start cycling at 20 | DEFAULT_SPEED = 10 21 | 22 | # How many times the LEDs will be updated per second 23 | UPDATES = 60 24 | 25 | # How bright the LEDs will be (between 0.0 and 1.0) 26 | BRIGHTNESS = 0.5 27 | 28 | 29 | # Pick *one* LED type by uncommenting the relevant line below: 30 | 31 | # APA102 / DotStar™ LEDs 32 | # led_strip = dotstar.DotStar(board.CLK, board.DATA, NUM_LEDS, brightness=BRIGHTNESS) 33 | 34 | # WS2812 / NeoPixel™ LEDs 35 | led_strip = neopixel.NeoPixel(board.DATA, NUM_LEDS, brightness=BRIGHTNESS, auto_write=False) 36 | 37 | user_sw = digitalio.DigitalInOut(board.USER_SW) 38 | user_sw.direction = digitalio.Direction.INPUT 39 | user_sw.pull = digitalio.Pull.UP 40 | 41 | sw_a = digitalio.DigitalInOut(board.SW_A) 42 | sw_a.direction = digitalio.Direction.INPUT 43 | sw_a.pull = digitalio.Pull.UP 44 | 45 | sw_b = digitalio.DigitalInOut(board.SW_B) 46 | sw_b.direction = digitalio.Direction.INPUT 47 | sw_b.pull = digitalio.Pull.UP 48 | 49 | led = adafruit_rgbled.RGBLED(board.LED_R, board.LED_G, board.LED_B, invert_pwm = True) 50 | 51 | sense = AnalogIn(board.CURRENT_SENSE) 52 | 53 | # Constants used for current conversion 54 | ADC_GAIN = 50 55 | SHUNT_RESISTOR = 0.015 # Yes, this is 0.015 Ohm 56 | 57 | def get_voltage(pin): 58 | return (pin.value * 3.3) / 65536 59 | 60 | def get_current(pin): 61 | return get_voltage(pin) / (ADC_GAIN * SHUNT_RESISTOR) 62 | 63 | def hsv_to_rgb(h, s, v): 64 | # All inputs are from 0.0 to 1.0 65 | i = math.floor(h * 6.0) 66 | f = h * 6.0 - i 67 | v *= 255.0 68 | p = v * (1.0 - s) 69 | q = v * (1.0 - f * s) 70 | t = v * (1.0 - (1.0 - f) * s) 71 | 72 | zone = int(i) % 6 73 | if zone == 0: 74 | return (v, t, p) 75 | if zone == 1: 76 | return (q, v, p) 77 | if zone == 2: 78 | return (p, v, t) 79 | if zone == 3: 80 | return (p, q, v) 81 | if zone == 4: 82 | return (t, p, v) 83 | if zone == 5: 84 | return (v, p, q) 85 | return (0, 0, 0) 86 | 87 | def button_read(button): 88 | return not button.value 89 | 90 | speed = DEFAULT_SPEED 91 | offset = 0.0 92 | 93 | count = 0 94 | # Make rainbows 95 | while True: 96 | sw = not user_sw.value 97 | a = not sw_a.value 98 | b = not sw_b.value 99 | 100 | if sw: 101 | speed = DEFAULT_SPEED 102 | else: 103 | if a: 104 | speed -= 1 105 | if b: 106 | speed += 1 107 | 108 | speed = min(255, max(1, speed)) 109 | 110 | offset += float(speed) / 2000.0 111 | 112 | for i in range(NUM_LEDS): 113 | hue = float(i) / NUM_LEDS 114 | led_strip[i] = hsv_to_rgb(hue + offset, 1.0, 1.0) 115 | led_strip.show() 116 | 117 | led.color = (speed, 0, 255 - speed) 118 | 119 | count += 1 120 | if count >= UPDATES: 121 | # Display the current value once every second 122 | print("Current =", get_current(sense), "A") 123 | count = 0 124 | 125 | time.sleep(1.0 / UPDATES) 126 | -------------------------------------------------------------------------------- /plasma_stick/neopixel_simpletest.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries 2 | # SPDX-License-Identifier: MIT 3 | 4 | import time 5 | import board 6 | import neopixel 7 | 8 | 9 | # On CircuitPlayground Express, and boards with built in status NeoPixel -> board.NEOPIXEL 10 | # Otherwise choose an open pin connected to the Data In of the NeoPixel strip, i.e. board.D1 11 | # pixel_pin = board.NEOPIXEL 12 | 13 | # On a Raspberry pi, use this instead, not all pins are supported 14 | pixel_pin = board.GP15 15 | 16 | # The number of NeoPixels 17 | num_pixels = 50 18 | 19 | # The order of the pixel colors - RGB or GRB. Some NeoPixels have red and green reversed! 20 | # For RGBW NeoPixels, simply change the ORDER to RGBW or GRBW. 21 | ORDER = neopixel.GRB 22 | 23 | pixels = neopixel.NeoPixel( 24 | pixel_pin, num_pixels, brightness=0.5, auto_write=False, pixel_order=ORDER 25 | ) 26 | 27 | 28 | def wheel(pos): 29 | # Input a value 0 to 255 to get a color value. 30 | # The colours are a transition r - g - b - back to r. 31 | if pos < 0 or pos > 255: 32 | r = g = b = 0 33 | elif pos < 85: 34 | r = int(pos * 3) 35 | g = int(255 - pos * 3) 36 | b = 0 37 | elif pos < 170: 38 | pos -= 85 39 | r = int(255 - pos * 3) 40 | g = 0 41 | b = int(pos * 3) 42 | else: 43 | pos -= 170 44 | r = 0 45 | g = int(pos * 3) 46 | b = int(255 - pos * 3) 47 | return (r, g, b) if ORDER in (neopixel.RGB, neopixel.GRB) else (r, g, b, 0) 48 | 49 | 50 | def rainbow_cycle(wait): 51 | for j in range(255): 52 | for i in range(num_pixels): 53 | pixel_index = (i * 256 // num_pixels) + j 54 | pixels[i] = wheel(pixel_index & 255) 55 | pixels.show() 56 | time.sleep(wait) 57 | 58 | 59 | while True: 60 | # Comment this line out if you have RGBW/GRBW NeoPixels 61 | pixels.fill((255, 0, 0)) 62 | # Uncomment this line if you have RGBW/GRBW NeoPixels 63 | # pixels.fill((255, 0, 0, 0)) 64 | pixels.show() 65 | time.sleep(1) 66 | 67 | # Comment this line out if you have RGBW/GRBW NeoPixels 68 | pixels.fill((0, 255, 0)) 69 | # Uncomment this line if you have RGBW/GRBW NeoPixels 70 | # pixels.fill((0, 255, 0, 0)) 71 | pixels.show() 72 | time.sleep(1) 73 | 74 | # Comment this line out if you have RGBW/GRBW NeoPixels 75 | pixels.fill((0, 0, 255)) 76 | # Uncomment this line if you have RGBW/GRBW NeoPixels 77 | # pixels.fill((0, 0, 255, 0)) 78 | pixels.show() 79 | time.sleep(1) 80 | 81 | rainbow_cycle(0.001) # rainbow cycle with 1ms delay per step 82 | -------------------------------------------------------------------------------- /plasma_stick/neopixels_rainbow.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | NeoPixel example for Pico. Displays a rainbow on the NeoPixels. 7 | REQUIRED HARDWARE: 8 | * RGB NeoPixel LEDs connected to pin GP15. 9 | """ 10 | import time 11 | import board 12 | from rainbowio import colorwheel 13 | import neopixel 14 | 15 | # Update this to match the number of NeoPixel LEDs connected to your board. 16 | num_pixels = 50 17 | 18 | pixels = neopixel.NeoPixel(board.GP15, num_pixels, auto_write=False) 19 | pixels.brightness = 0.5 20 | 21 | 22 | def rainbow(speed): 23 | for j in range(255): 24 | for i in range(num_pixels): 25 | pixel_index = (i * 256 // num_pixels) + j 26 | pixels[i] = colorwheel(pixel_index & 255) 27 | pixels.show() 28 | time.sleep(speed) 29 | 30 | 31 | while True: 32 | rainbow(0) -------------------------------------------------------------------------------- /servo2040/onboard_neopixels.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """CircuitPython Essentials Internal RGB LED red, green, blue example""" 6 | import time 7 | import board 8 | 9 | import neopixel 10 | led = neopixel.NeoPixel(board.LED_DATA, board.NUM_LEDS) 11 | 12 | led.brightness = 0.3 13 | 14 | while True: 15 | for i in range(board.NUM_LEDS): 16 | led[i] = (255, 0, 0) 17 | time.sleep(0.5) 18 | 19 | for i in range(board.NUM_LEDS): 20 | led[i] = (0, 255, 0) 21 | time.sleep(0.5) 22 | 23 | for i in range(board.NUM_LEDS): 24 | led[i] = (0, 0, 255) 25 | time.sleep(0.5) -------------------------------------------------------------------------------- /servo2040/sensor_reading.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """CircuitPython Essentials Analog In example""" 6 | import time 7 | import board 8 | from digitalio import DigitalInOut, Direction 9 | from analogio import AnalogIn 10 | 11 | addr0_pin = DigitalInOut(board.ADC_ADDR_0) 12 | addr0_pin.direction = Direction.OUTPUT 13 | 14 | addr1_pin = DigitalInOut(board.ADC_ADDR_1) 15 | addr1_pin.direction = Direction.OUTPUT 16 | 17 | addr2_pin = DigitalInOut(board.ADC_ADDR_2) 18 | addr2_pin.direction = Direction.OUTPUT 19 | 20 | analog_in = AnalogIn(board.SHARED_ADC) 21 | 22 | 23 | def get_voltage(pin): 24 | return (pin.value * 3.3) / 65536 25 | 26 | def select(address): 27 | addr0_pin.value = address & 0b001 28 | addr1_pin.value = address & 0b010 29 | addr2_pin.value = address & 0b100 30 | 31 | 32 | VOLTAGE_GAIN = 13.9 / 3.9 33 | CURRENT_GAIN = 1 / (69 * 0.003) 34 | CURRENT_OFFSET = -0.02 35 | 36 | while True: 37 | # Read each sensor in turn and print its voltage 38 | for i in range(board.NUM_SENSORS): 39 | select(i) 40 | print("S", i + 1, " = ", round(get_voltage(analog_in), 3), sep="", end=", ") 41 | 42 | # Read the voltage sense and print the value 43 | select(board.VOLTAGE_SENSE_ADDR) 44 | voltage = get_voltage(analog_in) * VOLTAGE_GAIN 45 | print("Voltage =", round(voltage, 4), end=", ") 46 | 47 | # Read the current sense and print the value 48 | select(board.CURRENT_SENSE_ADDR) 49 | current = (get_voltage(analog_in) + CURRENT_OFFSET) * CURRENT_GAIN 50 | print("Current =", round(current, 4)) 51 | 52 | time.sleep(0.5) -------------------------------------------------------------------------------- /servo2040/servo_driving.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """CircuitPython Essentials Servo standard servo example""" 6 | import time 7 | import board 8 | import pwmio 9 | from adafruit_motor import servo 10 | 11 | # Specify which servo pins to use. 12 | pins = [ board.SERVO_1, board.SERVO_2, board.SERVO_3, board.SERVO_4 ] 13 | 14 | # Create a servo object for each pin 15 | servos = [ servo.Servo(pwmio.PWMOut(pin, duty_cycle=2 ** 15, frequency=50)) for pin in pins] 16 | 17 | while True: 18 | for angle in range(0, 180, 5): # 0 - 180 degrees, 5 degrees at a time. 19 | for i in range(len(pins)): 20 | servos[i].angle = angle 21 | time.sleep(0.05) 22 | for angle in range(180, 0, -5): # 180 - 0 degrees, 5 degrees at a time. 23 | for i in range(len(pins)): 24 | servos[i].angle = angle 25 | time.sleep(0.05) --------------------------------------------------------------------------------