#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// Wiring: Define the pins for connections
#define encoder_clk 7    // Pin connected to rotary encoder's CLK
#define encoder_dt 6     // Pin connected to rotary encoder's DT
#define encoder_sw 5     // Pin connected to rotary encoder's SW
#define relay_out 4      // Pin connected to relay for BB dispenser control
#define switch_in 3      // Pin connected to the limit switch input

// Changeable configuration:
#define bb_increse 10     // Increment/decrement value for each rotary encoder step
#define bb_min 30         // Minimum BB count that can be set (minimum = 10)
#define bb_max 300        // Maximum BB count that can be set (maximum = 1000)
#define debounce_delay 20 // Delay to minimize the bouncing effect of the limit switch
int bb_count = 30;        // Default BB count after device startup

// Variables for encoder state tracking
int currentStateCLK, lastStateCLK;

// Setting up the OLED display
#define SCREEN_ADDRESS 0x3D
Adafruit_SSD1306 display(128, 64, &Wire, -1); // OLED display instance with specified parameters

void setup() {
  Serial.begin(115200); // Start serial communication

  // Set pin modes for various components
  pinMode(encoder_clk, INPUT);
  pinMode(encoder_dt, INPUT);
  pinMode(encoder_sw, INPUT_PULLUP);
  pinMode(switch_in, INPUT_PULLUP);
  pinMode(relay_out, OUTPUT);

  digitalWrite(relay_out, LOW); // Set initial state of relay pin

  // Initialize the OLED display
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println("SSD1306 allocation failed");
    for (;;); // Halt further execution, loop forever
  }

  updateOLED(bb_count); // Update OLED with initial BB count

  lastStateCLK = digitalRead(encoder_clk); // Record initial state of the encoder
}

void loop() {
  currentStateCLK = digitalRead(encoder_clk); // Read the current state of the encoder's CLK pin

  // Check for encoder rotation
  if (currentStateCLK != lastStateCLK && currentStateCLK == 1) {
    if (digitalRead(encoder_dt) != currentStateCLK) {
      if (bb_count < bb_max) bb_count += bb_increse; // Increase BB count on rotation
    } else {
      if (bb_count > bb_min) bb_count -= bb_increse; // Decrease BB count on rotation
    }
    updateOLED(bb_count); // Update the OLED display with the new BB count
  }

  lastStateCLK = currentStateCLK; // Update last encoder state

  int btnState = digitalRead(encoder_sw); // Read the button state

  // Activate BB dispenser if button is pressed
  if (btnState == LOW) {
    Serial.println("BBs dispenser activated");
    digitalWrite(relay_out, HIGH); // Turn on the BB dispenser relay

    int bbs_dispensed = 0; // Initialize the count of dispensed BBs

    // Dispense BBs until the desired count is reached
    while (bbs_dispensed < bb_count) {
      if (!digitalRead(switch_in)) {
        delay(debounce_delay);

        while (!digitalRead(switch_in)) {} // Wait for the switch to be released

        delay(debounce_delay);
        bbs_dispensed++; // Increment dispensed BB count
        delay(1);
        updateOLED(bb_count - bbs_dispensed); // Update OLED with remaining BB count
      }
    }

    delay(50);

    // All BBs dispensed, update and indicate the count
    Serial.print("All ");
    Serial.print(bb_count);
    Serial.println(" dispensed.");
    digitalWrite(relay_out, LOW); // Turn off the BB dispenser

    updateOLED(bb_count); // Update OLED with the original BB count
  }
  delay(1); // Small delay to avoid tight loop
}

// Function to update the OLED display with BB count
void updateOLED(int displaycount) {
  display.clearDisplay(); // Clear the display

  // Map the BB count to a bar size on the display
  int barsize = map(displaycount, 0, bb_max, 4, 128);
  display.fillRect(0, 0, barsize, 16, SSD1306_WHITE); // Display a bar representing BB count

  display.setTextSize(3); // Set text size for BB count display
  display.setTextColor(SSD1306_WHITE); // Set text color

  // Set position for displaying BB count based on its value
  if (displaycount < 100 && displaycount >= 10) {
    display.setCursor(49, 30);
    display.print(displaycount);
  } else if (displaycount < 10) {
    display.setCursor(56, 30);
    display.print(displaycount);
  } else {
    display.setCursor(36, 30);
    display.print(displaycount);
  }

  display.display(); // Update the OLED display
}
NOCOMNCVCCGNDINLED1PWRRelay Module
This LED represents DC Motor
You can also use Arduino NANO with the same pinout
This button represents limit switch used to count BBs