#include <Wire.h>
#include <U8g2lib.h>
#define CLK 2 // Chân CLK của rotary encoder
#define DT 3 // Chân DT của rotary encoder
#define SW 4 // Chân nhấn của rotary encoder
#define STEP_PIN 5 // Chân STEP của A4988
#define DIR_PIN 6 // Chân DIR của A4988
// Biến toàn cục
volatile int encoderValue = 0;
int lastStateCLK;
int menuIndex = 0; // 0: Rounds, 1: Speed, 2: Start
int rounds = 10; // Số vòng hiện tại
int maxRounds = 50; // Số vòng tối đa
int speed = 5; // Tốc độ (1-10)
bool isEditing = false; // Đang chỉnh sửa thông số
bool blinkState = false; // Trạng thái nhấp nháy
// Biến lưu trạng thái nút nhấn SW
bool lastButtonState = HIGH;
bool buttonState = HIGH;
// Khởi tạo OLED 128x64 I2C
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
void setup() {
pinMode(CLK, INPUT);
pinMode(DT, INPUT);
pinMode(SW, INPUT_PULLUP);
pinMode(STEP_PIN, OUTPUT);
pinMode(DIR_PIN, OUTPUT);
attachInterrupt(digitalPinToInterrupt(CLK), readEncoder, CHANGE);
u8g2.begin();
lastStateCLK = digitalRead(CLK);
}
void loop() {
// Kiểm tra nút nhấn SW
buttonState = digitalRead(SW);
if (lastButtonState == HIGH && buttonState == LOW) {
handleButtonPress();
delay(200); // Chống dội phím (debounce)
}
lastButtonState = buttonState;
// Hiển thị menu, nhấp nháy nếu đang chỉnh
blinkState = !blinkState;
displayMenu();
delay(300);
}
// 📌 Đọc tín hiệu từ rotary encoder
void readEncoder() {
int currentStateCLK = digitalRead(CLK);
if (currentStateCLK != lastStateCLK) {
if (digitalRead(DT) != currentStateCLK) {
encoderValue++;
} else {
encoderValue--;
}
if (isEditing) {
if (menuIndex == 0) { // Đang chỉnh Rounds
rounds = constrain(rounds + (encoderValue > 0 ? 1 : -1), 1, maxRounds);
} else if (menuIndex == 1) { // Đang chỉnh Speed
speed = constrain(speed + (encoderValue > 0 ? 1 : -1), 1, 10);
}
} else {
menuIndex = constrain(menuIndex + (encoderValue > 0 ? 1 : -1), 0, 2);
}
encoderValue = 0; // Reset giá trị
}
lastStateCLK = currentStateCLK;
}
// 📌 Xử lý khi nhấn nút SW
void handleButtonPress() {
if (isEditing) {
isEditing = false; // Thoát chế độ chỉnh sửa
} else {
if (menuIndex < 2) {
isEditing = true; // Bật chế độ chỉnh sửa
} else {
startFunction(); // Gọi chức năng Start
}
}
}
// 📌 Hiển thị giao diện menu trên OLED
void displayMenu() {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB10_tr);
u8g2.setCursor(10, 20);
u8g2.print("Rounds: ");
u8g2.print(rounds);
u8g2.print("/");
u8g2.print(maxRounds);
if (menuIndex == 0) {
if (!isEditing || blinkState) u8g2.print(" <");
}
u8g2.setCursor(10, 40);
u8g2.print("Speed: ");
u8g2.print(speed);
if (menuIndex == 1) {
if (!isEditing || blinkState) u8g2.print(" <");
}
u8g2.setCursor(10, 60);
u8g2.print("Start");
if (menuIndex == 2) u8g2.print(" <");
u8g2.sendBuffer();
}
// 📌 Chức năng chạy khi nhấn "Start"
void startFunction() {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_ncenB14_tr);
u8g2.setCursor(20, 40);
u8g2.print("Running...");
u8g2.sendBuffer();
digitalWrite(DIR_PIN, HIGH); // Hướng quay
int stepsPerRound = 200; // Số bước cho 1 vòng
int totalSteps = stepsPerRound * rounds;
int delayTime = map(speed, 1, 10, 1000, 100); // Chuyển speed (1-10) thành delay (1000-100 ms)
for (int i = 0; i < totalSteps; i++) {
digitalWrite(STEP_PIN, HIGH);
delayMicroseconds(delayTime);
digitalWrite(STEP_PIN, LOW);
delayMicroseconds(delayTime);
}
// Hiển thị hoàn thành
u8g2.clearBuffer();
u8g2.setCursor(20, 40);
u8g2.print("Done!");
u8g2.sendBuffer();
delay(2000);
}