StepperHAL
Encoder        
Warning
Encoder not yet verified due to missing hardware
Class to get position information from encoder
Attributes:
| Name | Type | Description | 
|---|---|---|
| pin_A | Encoder channel A connected to GPIO pin for Tx | |
| pin_B | Encoder channel B connectet to GPIO pin for Rx | 
angle_calc(self)
Stepper angle calculation
Source code in hardware/stepper_hal.py
def angle_calc(self):
    """
        Stepper angle calculation
    """
    total_angle = self.enc_imp * self.motor_step_angle
    return total_angle
enc_impulses(self)
Callbacks when edges are detected
Decode Gray Code and update counter
Source code in hardware/stepper_hal.py
def enc_impulses(self):
    """
        Callbacks when edges are detected
        Decode Gray Code and update counter
    """
    current_A= int(self.input_A.is_pressed)
    current_B= int(self.input_B.is_pressed)
    state=f"{current_A}{current_B}"
    if self.old_state=='00':
        if state=='01':
            logging.debug(f"old_state {self.old_state}, new_state {state}, direction ->")
            self.direction='Right'
        elif self.state=='10':
            logging.debug(f"old_state {self.old_state}, new_state {state}, direction <-")
            self.direction='Left'
    elif self.old_state=='01':
        if state=='11':
            logging.debug(f"old_state {self.old_state}, new_state {state}, direction ->")
            self.direction='Right'
        elif state=='00':
            logging.debug(f"old_state {self.old_state}, new_state {state}, direction <-")
            self.direction='Left'
            self.enc_imp= self.enc_imp-1
    elif self.old_state=='10':
        if state=='00':
            logging.debug(f"old_state {self.old_state}, new_state {state}, direction ->")
            self.direction='Right'
            self.enc_imp= self.enc_imp+1
        elif state=='11':
            logging.debug(f"old_state {self.old_state}, new_state {state}, direction <-")
            self.direction='Left'
    elif self.old_state=='11':
        if state=='10':
            logging.debug(f"old_state {self.old_state}, new_state {state}, direction ->")
            self.direction='Right'
        elif state=='01':
            logging.debug(f"old_state {self.old_state}, new_state {state}, direction <-")
            self.direction='Left'
    self.old_state=state
set_imp_count_zero(self)
async
Set pulse counter to zero
Source code in hardware/stepper_hal.py
async def set_imp_count_zero(self):
    """
        Set pulse counter to zero
    """
    old_imp_val=self.enc_imp
    self.enc_imp=0
    logging.info(f"Encoder.set_imp_zero: old {old_imp_val} new {self.enc_imp}")
EncoderStepperHAL            (StepperHAL)
        
Stepper hardware abstraction layer for Rocking Stepper with encoder simulated by counting the steps
Attributes:
| Name | Type | Description | 
|---|---|---|
| direction | Literal | stepper direction with default FORWARD | 
| step_style | int | step type with default DOUBLE | 
| max_angle | float | forward limit for Rocking Stepper | 
| min_angle | float | backward limit for Rocking Stepper | 
__init__(self, stepper_name, port_number, stop_at_exit=True)
special
Parameters:
| Name | Type | Description | Default | 
|---|---|---|---|
| stepper_name | stepper naming | required | |
| port_number | Motor HAT ports 1,2 | required | 
Source code in hardware/stepper_hal.py
def __init__(self, stepper_name, port_number, stop_at_exit=True):
    """
        Arguments:
            stepper_name: stepper naming
            port_number: Motor HAT ports 1,2
    """
    super().__init__(stepper_name, port_number, stop_at_exit=True)
    #self.encoder = Encoder()      #currently not used
calibrate(self)
async
Determine middle position rocking stepper by stopper
Source code in hardware/stepper_hal.py
async def calibrate(self): #with stopper
    """
        Determine middle position rocking stepper by stopper
    """
    direction=STEPPER.FORWARD               #used current interval
    interval=0.05
    while(not self.forward_stopper.is_pressed):
        self.motor.onestep(direction=direction, style=self.step_style)
        await asyncio.sleep(interval)
    direction=STEPPER.BACKWARD
    counter=0
    while(not self.backward_stopper.is_pressed):
        self.motor.onestep(direction=direction, style=self.step_style)
        await asyncio.sleep(interval)
        counter += 1
    middle=round(counter/2)
    logging.info(f"Found center at {middle} steps")
    for _ in range(middle):
        self.motor.onestep(direction=STEPPER.FORWARD, style=self.step_style)
        await asyncio.sleep(interval)
    self.step_count=0             #start_position; counter reset
get_position(self)
async
Returns:
| Type | Description | 
|---|---|
| Stepper position in steps (0 == Center) | 
Source code in hardware/stepper_hal.py
async def get_position(self):      #currently without stopper
    """
        Return:
            Stepper position in steps (0 == Center)
    """
    return self.step_count  #WITHOUT encoder device
    #WITH encoder device - not verified due to missing working hardware
    # angle_pos=await self.encoder.angle_calc()
    # logging.debug(f"current angle: {angle_pos}")
    # pos_current=int(angle_pos/self.encoder.motor_step_angle)
    # return int(pos_current)     #Algo adaptation necessary so that angle can be passed
set_position(self, pos_goal)
async
Move stepper to given position
Parameters:
| Name | Type | Description | Default | 
|---|---|---|---|
| pos_goal | int | target position to be moved to in steps (negative => backward, positive => forward) | required | 
Source code in hardware/stepper_hal.py
async def set_position(self, pos_goal: int) -> int: #without stopper
    """
        Move stepper to given position
        Arguments:
            pos_goal: target position to be moved to in steps (negative => backward, positive => forward)
    """
    pos_current = await self.get_position()
    logging.debug(f"Port {self.name} current position {pos_current}")
    steps = pos_current-pos_goal
    direction = STEPPER.FORWARD if steps<0 else STEPPER.BACKWARD
    logging.debug(f"steps to go {abs(steps)} in direction {direction}")
    for _ in range(abs(steps)):
        self.motor.onestep(direction=direction, style=self.step_style)
        await asyncio.sleep(self.interval)
        if direction == STEPPER.FORWARD:
            self.step_count = self.step_count+1
        else:
            self.step_count = self.step_count-1
        logging.debug(f"{self.name} step_count {self.step_count}")
    pos_current = await self.get_position()
    logging.debug(f"{self.name} check {pos_current} = {pos_goal} as start position?")
    return pos_current
StepperHAL            (HAL)
        
Stepper hardware abstraction layer
Attributes:
| Name | Type | Description | 
|---|---|---|
| direction | Literal | stepper direction with default FORWARD | 
| step_style | int | step type with default DOUBLE | 
| interval | float | initial waiting time | 
| step_count | int | motor rock control without encoder | 
| motor_step_angle | float | step resolution with step_style DOUBLE | 
| max_angle | float | max rocking angle | 
| min_angle | float | min rocking angle | 
| motor | Stepper reference | |
| forward_stopper | micro-limit-switch at GPIO Pin 24 | |
| backward_stopper | micro-limit-switch at GPIO Pin 25 | 
__init__(self, stepper_name, port_number, stop_at_exit=True)
special
Initialize motors and stopper
Parameters:
| Name | Type | Description | Default | 
|---|---|---|---|
| stepper_name | Name of the stepper | required | |
| port_number | Motor HAT ports 1, 2 | required | 
Source code in hardware/stepper_hal.py
def __init__(self, stepper_name, port_number, stop_at_exit=True):
    """
        Initialize motors and stopper
        Arguments:
            stepper_name: Name of the stepper
            port_number: Motor HAT ports 1, 2
    """
    station=MotorKit(i2c=board.I2C())
    self.name=stepper_name
    self.port_number=port_number
    if port_number==1:
        self.motor=station.stepper1
    elif port_number==2:
        self.motor=station.stepper2
    if stop_at_exit:
        atexit.register(self.motor_stop)  #necessary as motor voltage is otherwise not released
    if port_number ==1:
        self.forward_stopper = Button(24, pull_up=True)             #GPIO 24 stopper
        self.backward_stopper = Button(25, pull_up=True)            #GPIO 25 stopper
        self.forward_stopper.when_pressed = self.change_direction   #Callback functions
        self.backward_stopper.when_pressed = self.change_direction
change_direction(self, angle=0)
Change direction when stopper is pressed
Parameters:
| Name | Type | Description | Default | 
|---|---|---|---|
| angle | float | current position in degree | 0 | 
Source code in hardware/stepper_hal.py
def change_direction(self, angle:float = 0):  #Rocking Stepper
    """
        Change direction when stopper is pressed
        Arguments:
            angle: current position in degree
    """
    if angle >= self.max_angle or self.forward_stopper.is_pressed:                 #trigger: max. def. angle
        self.direction=STEPPER.BACKWARD
        logging.info(f"direction changes to BACKWARD")
    elif angle <= self.min_angle or self.backward_stopper.is_pressed:               #trigger: min. def. angle
        self.direction=STEPPER.FORWARD
        logging.info(f"direction changes to FORWARD")
close(self)
Stop and release motor
Source code in hardware/stepper_hal.py
def close(self):
    """
       Stop and release motor
    """
    self.motor_stop()
    logging.info(f"Close stepper {self.name}")
motor_stop(self)
Single motor hold
Source code in hardware/stepper_hal.py
def motor_stop(self):
    """
        Single motor hold
    """
    self.motor.release()
rock(self)
async
Rock stepper within given angle
Source code in hardware/stepper_hal.py
async def rock(self): #Rocking Stepper
    """
        Rock stepper within given angle
    """
    while True:
        self.motor.onestep(direction=self.direction, style=self.step_style)
        await asyncio.sleep(self.interval)
        logging.debug(f"rock_direction: {self.direction} with interval {self.interval}")
        step = (1 if self.direction == STEPPER.FORWARD else - 1)
        self.step_count = self.step_count + step
        logging.debug(f"{self.name} step_count {self.step_count}")
        rock_angle=self.step_count*self.motor_step_angle
        self.change_direction(rock_angle)     #option: add conditions here
rotate(self)
async
Rotate stepper
Source code in hardware/stepper_hal.py
async def rotate(self):
    """
        Rotate stepper
    """
    while True:
        self.motor.onestep(direction=self.direction, style=self.step_style)
        await asyncio.sleep(self.interval)
        logging.debug(f"rotate: {self.name} direction: {self.direction} with interval {self.interval}")