Skip to content

Bug: Multiple Encoders Causes Code to Hang #5

@perspector

Description

@perspector

Hello,
First of all, thank you for all of your incredible work on Circuitpython with Pimoroni! I really appreciate it!
I noticed that initializing multiple MMME encoders for gearmotors sometimes caused the program to hang. I tested the read_encoders example.
Using a single encoder instead of four worked flawlessly multiple times.
Next, I checked the rotaryio CircuitPython documentation.
Is it possible that the encoders need to be deinitialized each time?
Something like:

def get_position(self, motor):
        if motor == "FL": # front left
            with rotaryio.IncrementalEncoder(board.ENCODER_A_B, board.ENCODER_A_A, divisor=1) as encoder:
                position = encoder.position + self.encoder_FL_prev_position
                self.encoder_FL_prev_position = position
                return position * 360 / self.counts_per_rev
        elif motor == "BL": # back left
            with rotaryio.IncrementalEncoder(board.ENCODER_B_B, board.ENCODER_B_A, divisor=1) as encoder:
                position = encoder.position + self.encoder_BL_prev_position
                self.encoder_BL_prev_position = position
                return position * 360 / self.counts_per_rev
        elif motor == "BR": # back right
            with rotaryio.IncrementalEncoder(board.ENCODER_C_B, board.ENCODER_C_A, divisor=1) as encoder:
                position = encoder.position + self.encoder_BR_prev_position
                self.encoder_BR_prev_position = position
                return position * 360 / self.counts_per_rev
        elif motor == "FR": # front right
            with rotaryio.IncrementalEncoder(board.ENCODER_D_B, board.ENCODER_D_A, divisor=1) as encoder:
                position = encoder.position + self.encoder_FR_prev_position
                self.encoder_FR_prev_position = position
                return position * 360 / self.counts_per_rev
My entire code is below.
import board
import pwmio
import digitalio
from adafruit_motor import motor
import rotaryio
import neopixel


class ScuttleBot:
    def __init__(self):
        print("Initializing ScuttleBot...")
        self.wheel_dia = 36 # wheel diameter in mm
        self.gear_ratio = 50 # motor gear ratio (IN:OUT)
        self.counts_per_rev = 12 * self.gear_ratio # encoder counts per wheel revolution

        # Motor constants
        self.FREQUENCY = 25000               # Choose a frequency above human hearing
        self.DECAY_MODE = motor.SLOW_DECAY   # The decay mode affects how the motor
                                        # responds, with SLOW_DECAY having improved spin
                                        # threshold and speed-to-throttle linearity
        print("--> Setting up user switch")
        # Create a digitalinout object for the user switch
        self.user_sw = digitalio.DigitalInOut(board.USER_SW)
        self.user_sw.direction = digitalio.Direction.INPUT
        self.user_sw.pull = digitalio.Pull.UP
        print("--> Setting up neopixel")
        # Create the neopixel object
        self.led = neopixel.NeoPixel(board.LED_DATA, board.NUM_LEDS)
        self.led.brightness = 0.05 # 5% brightness
        self.led = self.led[0]
        print("--> Setting up PWM motor objects")
        # Create the pwm objects
        self.pwm_a_n = pwmio.PWMOut(board.MOTOR_A_N, frequency=self.FREQUENCY)
        self.pwm_a_p = pwmio.PWMOut(board.MOTOR_A_P, frequency=self.FREQUENCY)
        self.pwm_b_p = pwmio.PWMOut(board.MOTOR_B_P, frequency=self.FREQUENCY)
        self.pwm_b_n = pwmio.PWMOut(board.MOTOR_B_N, frequency=self.FREQUENCY)
        self.pwm_c_p = pwmio.PWMOut(board.MOTOR_C_P, frequency=self.FREQUENCY)
        self.pwm_c_n = pwmio.PWMOut(board.MOTOR_C_N, frequency=self.FREQUENCY)
        self.pwm_d_p = pwmio.PWMOut(board.MOTOR_D_P, frequency=self.FREQUENCY)
        self.pwm_d_n = pwmio.PWMOut(board.MOTOR_D_N, frequency=self.FREQUENCY)
        print("--> Setting up motors")
        # Create the motor objects
        self.motor_FL = motor.DCMotor(self.pwm_a_n, self.pwm_a_p) # Front left motor, connected to A
        self.motor_BL = motor.DCMotor(self.pwm_b_n, self.pwm_b_p) # Back left motor, connected to B
        self.motor_BR = motor.DCMotor(self.pwm_c_p, self.pwm_c_n) # Back right motor, connected to C
        self.motor_FR = motor.DCMotor(self.pwm_d_p, self.pwm_d_n) # Front right motor, connected to D

        # Set the motor decay modes (if unset the default will be FAST_DECAY)
        self.motor_FL.decay_mode = self.DECAY_MODE
        self.motor_BL.decay_mode = self.DECAY_MODE
        self.motor_BR.decay_mode = self.DECAY_MODE
        self.motor_FR.decay_mode = self.DECAY_MODE

        # setup encoder previous position variables to be continuously updated
        self.encoder_FL_prev_position = 0.0
        self.encoder_BL_prev_position = 0.0
        self.encoder_BR_prev_position = 0.0
        self.encoder_FR_prev_position = 0.0

        print("Scuttlebot Initialized!")


    def set_led(self, colors):
        self.led = colors


    def get_position(self, motor):
        if motor == "FL":
            with rotaryio.IncrementalEncoder(board.ENCODER_A_B, board.ENCODER_A_A, divisor=1) as encoder:
                position = encoder.position + self.encoder_FL_prev_position
                self.encoder_FL_prev_position = position
                return position * 360 / self.counts_per_rev
        elif motor == "BL":
            with rotaryio.IncrementalEncoder(board.ENCODER_B_B, board.ENCODER_B_A, divisor=1) as encoder:
                position = encoder.position + self.encoder_BL_prev_position
                self.encoder_BL_prev_position = position
                return position * 360 / self.counts_per_rev
        elif motor == "BR":
            with rotaryio.IncrementalEncoder(board.ENCODER_C_B, board.ENCODER_C_A, divisor=1) as encoder:
                position = encoder.position + self.encoder_BR_prev_position
                self.encoder_BR_prev_position = position
                return position * 360 / self.counts_per_rev
        elif motor == "FR":
            with rotaryio.IncrementalEncoder(board.ENCODER_D_B, board.ENCODER_D_A, divisor=1) as encoder:
                position = encoder.position + self.encoder_FR_prev_position
                self.encoder_FR_prev_position = position
                return position * 360 / self.counts_per_rev
        else:
            print('Choose a motor') # TODO: setup errors to throw


    def button_pressed(self):
        return not self.user_sw.value


    def stop(self):
        self.motor_FL.throttle = 0
        self.motor_BL.throttle = 0
        self.motor_BR.throttle = 0
        self.motor_FR.throttle = 0


    def forward(self, speed=0.5, squares=1):
        distance = squares * 500 # distance in mm to travel
        self.motor_FL.throttle = speed
        self.motor_BL.throttle = speed
        self.motor_BR.throttle = speed
        self.motor_FR.throttle = speed
    

    def backward(self, speed=0.5, squares=1):
        distance = squares * 500 # distance in mm to travel
        self.motor_FL.throttle = -speed
        self.motor_BL.throttle = -speed
        self.motor_BR.throttle = -speed
        self.motor_FR.throttle = -speed


    def left(self, speed=0.5, squares=1):
        distance = squares * 500 # distance in mm to travel
        self.motor_FL.throttle = -speed
        self.motor_BL.throttle = speed
        self.motor_BR.throttle = -speed
        self.motor_FR.throttle = speed


    def right(self, speed=0.5, squares=1):
        distance = squares * 500 # distance in mm to travel
        self.motor_FL.throttle = speed
        self.motor_BL.throttle = -speed
        self.motor_BR.throttle = speed
        self.motor_FR.throttle = -speed

Thank you so much!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions