#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Encoder.h>
#include <EEPROM.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define ENCODER_CLK 3
#define ENCODER_DT 2
#define ENCODER_SW 4
#define STEP_PIN 5
#define DIR_PIN 6
#define ENABLE_PIN 7
Encoder myEnc(ENCODER_DT, ENCODER_CLK);
long oldPosition = -999;
unsigned long lastEncoderMoveTime = 0;
#define EEPROM_CW_ADDR 0
#define EEPROM_CCW_ADDR 2
#define EEPROM_RPM_ADDR 4 // EEPROM 주소 변경: DELAY 대신 RPM 값을 저장
int menuState = 0; // 0:CW, 1:CCW, 2:RPM, 3:PLAY
int cwValue = 0;
int ccwValue = 0;
int motorRPM = 10; // 기본 RPM 값
bool editMode = false;
const unsigned char PROGMEM IMG_CJ_MM_P[] = {
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0x0f,0x9f,0xc3,0xe3,0x0f,0x8f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xfc,0x07,0x9f,0xc3,0xc3,0x0f,0x0f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xfc,0x77,0x9f,0xc3,0xc3,0x0f,0x0f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xf8,0xff,0x9f,0xc9,0xd3,0x27,0x4f,0xe4,0x72,0x60,0xf0,0x32,0x61,0xe1,0xc3,0xff,
0xf9,0xff,0x9f,0xc9,0x93,0x26,0x4f,0xe0,0x30,0x40,0x60,0x30,0x40,0xcf,0x9f,0xff,
0xf9,0xff,0x9f,0xc9,0x93,0x26,0x4f,0xe3,0x31,0xce,0x67,0x31,0xce,0x4f,0x9f,0xff,
0xf9,0xff,0x9f,0xcd,0xb3,0x36,0xcf,0xe7,0x13,0xce,0x27,0x33,0xc0,0x47,0x8f,0xff,
0xf9,0xff,0x9f,0xcc,0x33,0x30,0xcf,0xe7,0x13,0xce,0x07,0x33,0xcf,0xe1,0xc3,0xff,
0xf8,0x77,0x1f,0xcc,0x33,0x30,0xcf,0xe7,0x33,0xce,0x66,0x33,0xce,0xf8,0xf1,0xff,
0xfc,0x00,0x3f,0xce,0x73,0x39,0xcf,0xe0,0x33,0xc0,0x60,0x33,0xc0,0xd8,0xb1,0xff,
0xfe,0x0c,0x7f,0xce,0x73,0x39,0xcf,0xe4,0x73,0xe0,0xf1,0x33,0xe1,0xc1,0x83,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe7,0xff,0xff,0xff,0x3f,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe7,0xff,0xff,0xee,0x3f,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe7,0xff,0xff,0xe0,0x7f,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe7,0xff,0xff,0xf0,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
};
void setup() {
pinMode(ENCODER_SW, INPUT_PULLUP);
pinMode(STEP_PIN, OUTPUT);
pinMode(DIR_PIN, OUTPUT);
pinMode(ENABLE_PIN, OUTPUT);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
cwValue = EEPROM.read(EEPROM_CW_ADDR);
ccwValue = EEPROM.read(EEPROM_CCW_ADDR);
motorRPM = EEPROM.read(EEPROM_RPM_ADDR); // RPM 값을 EEPROM에서 읽습니다.
// 이미지 출력
display.drawBitmap(0, 0, IMG_CJ_MM_P, 128, 32, WHITE);
display.display();
delay(3000);
Serial.begin(9600);
while (!Serial) ;
Serial.print("CW Value: ");
Serial.println(cwValue);
Serial.print("CCW Value: ");
Serial.println(ccwValue);
Serial.print("Motor Delay: ");
Serial.println(motorRPM);
}
void loop() {
displayMenu();
handleEncoder();
if (digitalRead(ENCODER_SW) == LOW) {
delay(300); // Debounce delay
if (menuState != 3) { // Not PLAY
if (editMode) {
if (menuState == 0) EEPROM.write(EEPROM_CW_ADDR, cwValue);
if (menuState == 1) EEPROM.write(EEPROM_CCW_ADDR, ccwValue);
if (menuState == 2) EEPROM.write(EEPROM_RPM_ADDR, motorRPM);
editMode = false;
} else {
editMode = true;
}
} else {
executeMotorMovement();
}
}
}
void displayMenu() {
display.clearDisplay();
display.setCursor(0,0);
// Display CW, CCW, DELAY, and PLAY menu items with current values
display.println((menuState == 0) ? ">CW" : " CW");
display.setCursor(50,0);
display.print(cwValue);
if (menuState == 0 && editMode) display.print(" *");
display.setCursor(0,8);
display.println((menuState == 1) ? ">CCW" : " CCW");
display.setCursor(50,8);
display.print(ccwValue);
if (menuState == 1 && editMode) display.print(" *");
// DELAY 메뉴 항목을 PLAY 위로 이동
display.setCursor(0,16);
display.println((menuState == 2) ? ">RPM" : " RPM");
display.setCursor(50,16);
display.print(motorRPM);
if (menuState == 2 && editMode) display.print(" *");
// PLAY 메뉴 항목을 가장 아래로 이동
display.setCursor(0,24);
display.println((menuState == 3) ? ">PLAY" : " PLAY");
display.display();
}
void handleEncoder() {
static const unsigned long accelerationThreshold = 300; // 가속도 임계값 조정
long newPosition = myEnc.read() / 4; //각 클릭당 변화량을 조절 엔코더
unsigned long currentTime = millis();
if (newPosition != oldPosition) {
unsigned long timeSinceLastMove = currentTime - lastEncoderMoveTime;
int changeAmount = 1; // 기본 변경량
if (timeSinceLastMove < accelerationThreshold) {
changeAmount = accelerationThreshold / max(timeSinceLastMove, 1);
changeAmount = max(changeAmount, 1);
}
if (editMode) {
int delta = (newPosition - oldPosition > 0) ? changeAmount : -changeAmount;
switch(menuState) {
case 0: cwValue += delta; break;
case 1: ccwValue += delta; break;
case 2: motorRPM += delta; break;
}
} else {
menuState += (newPosition - oldPosition > 0) ? 1 : -1;
if (menuState > 3) menuState = 0;
if (menuState < 0) menuState = 3;
}
oldPosition = newPosition;
lastEncoderMoveTime = currentTime;
}
}
void executeMotorMovement() {
const long stepsPerRevolution = 200; // 모터가 한 바퀴 회전하는데 필요한 스탭 수
long delayBetweenSteps = 60000 / (motorRPM * stepsPerRevolution); // 분당 회전수를 기반으로 각 스탭 사이의 지연 시간을 계산
digitalWrite(ENABLE_PIN, LOW); // 모터 활성화
digitalWrite(DIR_PIN, HIGH); // CW 회전
for (long i = 0; i < cwValue ; i++) {
digitalWrite(STEP_PIN, HIGH);
delay(delayBetweenSteps); // 계산된 지연 시간을 사용
digitalWrite(STEP_PIN, LOW);
delay(delayBetweenSteps);
}
digitalWrite(DIR_PIN, LOW); // CCW 회전
for (long i = 0; i < ccwValue ; i++) {
digitalWrite(STEP_PIN, HIGH);
delay(delayBetweenSteps);
digitalWrite(STEP_PIN, LOW);
delay(delayBetweenSteps);
}
digitalWrite(ENABLE_PIN, HIGH); // 모터 비활성화
}