Construction Description

During a holiday trip, it became necessary to create a device to ensure that the cat left at home wouldn’t go hungry.
This isn’t the first attempt at solving this issue; a previous design based on a threaded mechanism to dispense food didn’t work out. It was difficult to control the amount of food dispensed, and the mechanism often jammed.
Thus, a new construction was developed based on a 28BYJ-48 stepper motor modified to a bipolar version, which rotates the entire container with compartments.

Modifying the motor requires removing the blue cap that covers the wires and cutting the middle track.
Then, the red wire (+5V) connected to it is removed. This modification provides the motor with greater torque.

The container is divided into 8 sections, with one being empty, leaving 7 portions of food.

3D Model

Code

The code snippet below is designed for use with ESPHome (initial ESP configuration and connections are omitted):

time:
  - platform: homeassistant
    id: homeassistant_time
    timezone: Europe/Warsaw
    on_time:
      - cron: 0 0 08 * * * #cron to configure automatic run
        then: 
          - if:
              condition:
                - lambda: return id(run_container).state;
              then:
                - button.press: next_slot
stepper: #stepper motor configuration
  - platform: uln2003
    id: my_stepper
    pin_a: 4
    pin_b: 5
    pin_c: 3
    pin_d: 13
    max_speed: 200 steps/s
    sleep_when_done: true
    acceleration: inf
    deceleration: inf

globals:
   - id: container_state
     type: int
     restore_value: no
     initial_value: '8'
   - id: stepper_position
     type: int
     restore_value: no
     initial_value: '0'

number:
  - platform: template
    id: stepper_steps
    initial_value: 1024
    min_value: 1020
    max_value: 1030
    step: 1
    optimistic: true

button:
  - platform: template
    name: "Reset Container State"
    entity_category: config
    icon: 'mdi:restore'
    on_press:
    - globals.set:
        id: container_state
        value: '8'
    - sensor.template.publish:
        id: container_state_sensor
        state: !lambda return id(container_state);
  - platform: template
    name: "Next Container Slot"
    icon: 'mdi:skip-next-circle-outline'
    id: next_slot
    on_press:
      - lambda: |-
          if (id(container_state) > 0)
            id(container_state)--;          
      - sensor.template.publish:
          id: container_state_sensor
          state: !lambda return id(container_state);
      - stepper.set_target:
          id: my_stepper
          target: !lambda return id(my_stepper).current_position + id(stepper_steps).state;
  - platform: template
    id: "Clean"
    icon: 'mdi:trash-can'
    entity_category: config
    on_press:
      - stepper.set_target:
          id: my_stepper
          target: !lambda return id(my_stepper).current_position + id(stepper_steps).state * 8;
      - globals.set:
          id: container_state
          value: '0'
      - sensor.template.publish:
          id: container_state_sensor
          state: !lambda return id(container_state);

switch:
  - platform: template
    id: run_container
    icon: 'mdi:clock-star-four-points-outline'
    name: "Auto run container"
    optimistic: true

sensor:
  - platform: template
    name: "Container State"
    icon: 'mdi:counter'
    id: container_state_sensor
    lambda: |-
      return id(container_state);      
    update_interval: 300s

Video Demonstration