TKK_E32231943/kode_penggulungan_otomaris.ino

1144 lines
31 KiB
C++

#include <LiquidCrystal_I2C.h>
#include <AccelStepper.h>
#include <Keypad.h>
#include <EEPROM.h>
// ======================================================
// LCD I2C
// ======================================================
LiquidCrystal_I2C lcd(0x27, 16, 2);
// ======================================================
// TB6600 + NEMA17 (Guide Kiri-Kanan)
// ======================================================
#define STEP_PIN 26
#define DIR_PIN 27
#define EN_PIN 15
AccelStepper stepper(AccelStepper::DRIVER, STEP_PIN, DIR_PIN);
// ======================================================
// BTS7960 + PG45 (Motor Penarik Kabel) - SELALU KANAN/FORWARD
// ======================================================
#define RPWM 14
#define LPWM 12
#define PWM_FREQ 20000
#define PWM_RESOLUTION 8
int motorSpeed = 0;
unsigned long lastMotorRamp = 0;
// ======================================================
// BUZZER
// ======================================================
#define BUZZER_PIN 25
// ======================================================
// KY-040 ENCODER [FIX: DT = GPIO 34 (input only OK)]
// ======================================================
#define ENC_CLK 32
#define ENC_DT 34
volatile unsigned long pulseCount = 0;
unsigned long lastPulseCount = 0;
unsigned long lastPulseDetected = 0;
volatile unsigned long lastPulseTime = 0;
const unsigned long DEBOUNCE_US = 3000;
// ======================================================
// ENCODER OFFSET - KABEL MELEWATI ENCODER SEBELUM SPOOL
// ======================================================
const float ENCODER_OFFSET_METER = 0.80; // 80 cm offset
bool encoderReset = false; // Flag untuk reset encoder di awal proses
unsigned long resetPulseCount = 0; // Pulse count saat reset
void IRAM_ATTR onEncoderPulse() {
unsigned long now = micros();
if ((now - lastPulseTime) > DEBOUNCE_US) {
if (digitalRead(ENC_DT) == HIGH) {
pulseCount++;
}
lastPulseTime = now;
}
}
// ======================================================
// KEYPAD 4x4 [FIX: C4 = GPIO 33 (full I/O)]
// ======================================================
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte rowPins[ROWS] = {4, 16, 17, 5};
byte colPins[COLS] = {18, 19, 23, 33};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
// ======================================================
// VARIABLE INPUT & PROSES
// ======================================================
char inputBuffer[8] = "";
byte inputIndex = 0;
float targetMeter = 0;
float sisaMeter = 0;
float totalMeter = 0;
// ======================================================
// KALIBRASI ENCODER
// ======================================================
float meterPerPulse = 0.01;
bool kalibrasiMode = false;
unsigned long kalibrasiPulseStart = 0;
unsigned long kalibrasiStartTime = 0;
const unsigned long KALIBRASI_TIMEOUT = 30000; // 30 detik timeout
bool autoMode = false;
bool prosesSelesai = false;
// ======================================================
// MODE MANUAL 20 PUTARAN
// ======================================================
enum ManualState {
MANUAL_IDLE,
MANUAL_MAJU_20,
MANUAL_JEDA_1,
MANUAL_MUNDUR_20,
MANUAL_JEDA_2,
MANUAL_SELESAI
};
ManualState manualState = MANUAL_IDLE;
const float STEPS_PER_REV = 3200.0;
const int PUTARAN_20 = 20;
const long TARGET_STEPS_20 = (long)(STEPS_PER_REV * PUTARAN_20);
const float MANUAL_MAX_SPEED = 3000;
const float MANUAL_ACCEL = 1500;
const unsigned long MANUAL_JEDA_MS = 500;
unsigned long manualJedaStart = 0;
float putaranSaatIni = 0;
// ======================================================
// LOGIKA STEPPER AUTO - 65 PUTARAN KANAN-KIRI LOOP
// ======================================================
const int PUTARAN_65 = 65;
const long STEPS_65_PUTARAN = (long)(STEPS_PER_REV * PUTARAN_65);
const float AUTO_MAX_SPEED = 15000;
const float AUTO_ACCEL = 8000;
bool arahKanan = true;
// ======================================================
// TOGGLE STEPPER KE KIRI (TOMBOL A)
// ======================================================
bool stepperKiriJalan = false;
// ======================================================
// EEPROM ADDRESSES
// ======================================================
#define EEPROM_ADDR_CALIBRATED 0
#define EEPROM_ADDR_METER_PP 4
// ======================================================
// TIMING VARIABLES
// ======================================================
unsigned long lastKey = 0;
unsigned long lastLCD = 0;
unsigned long lastSerialPrint = 0;
// ======================================================
// BUZZER FUNCTIONS
// ======================================================
void beep(int waktu) {
digitalWrite(BUZZER_PIN, HIGH);
delay(waktu);
digitalWrite(BUZZER_PIN, LOW);
}
void selesaiBuzzer() {
for (int i = 0; i < 5; i++) {
beep(100);
delay(100);
}
}
void errorBuzzer() {
for (int i = 0; i < 3; i++) {
beep(300);
delay(200);
}
}
void kalibrasiBuzzer() {
beep(500);
delay(200);
beep(500);
delay(200);
beep(500);
}
// ======================================================
// LCD FUNCTIONS
// ======================================================
void lcdInit() {
Wire.begin(21, 22);
delay(100);
lcd.begin(16, 2);
delay(100);
lcd.backlight();
delay(100);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("M14 Spool 16cm");
lcd.setCursor(0,1);
lcd.print("Ready");
delay(800);
lcd.clear();
}
void tampilLCD() {
static bool lastModeKalibrasi = false;
static bool lastModeAuto = false;
static bool lastModeSelesai = false;
static bool lastModeInput = false;
static bool lastKiriJalan = false;
static ManualState lastManualState = MANUAL_IDLE;
bool modeKalibrasi = kalibrasiMode;
bool modeAuto = autoMode;
bool modeSelesai = prosesSelesai;
bool modeInput = !kalibrasiMode && !autoMode && !prosesSelesai && manualState == MANUAL_IDLE && !stepperKiriJalan;
bool kiriJalan = stepperKiriJalan;
if (modeKalibrasi != lastModeKalibrasi ||
modeAuto != lastModeAuto ||
modeSelesai != lastModeSelesai ||
modeInput != lastModeInput ||
manualState != lastManualState ||
kiriJalan != lastKiriJalan) {
lcd.clear();
lastModeKalibrasi = modeKalibrasi;
lastModeAuto = modeAuto;
lastModeSelesai = modeSelesai;
lastModeInput = modeInput;
lastManualState = manualState;
lastKiriJalan = kiriJalan;
}
if (stepperKiriJalan) {
lcd.setCursor(0,0);
lcd.print("KE KIRI... ");
lcd.setCursor(0,1);
lcd.print("Pos:");
lcd.print(stepper.currentPosition());
lcd.print(" ");
}
else if (kalibrasiMode) {
lcd.setCursor(0,0);
lcd.print("KALIBRASI ");
lcd.setCursor(0,1);
lcd.print("Pulse:");
unsigned long currentPulse = pulseCount;
unsigned long diff = currentPulse - kalibrasiPulseStart;
lcd.print(" ");
lcd.setCursor(6,1);
lcd.print(diff);
lcd.print(" ");
// Tampilkan sisa waktu
unsigned long elapsed = millis() - kalibrasiStartTime;
unsigned long remaining = (KALIBRASI_TIMEOUT - elapsed) / 1000;
lcd.setCursor(12,1);
lcd.print(remaining);
lcd.print("s ");
}
else if (autoMode) {
lcd.setCursor(0,0);
lcd.print("Sisa:");
lcd.print(sisaMeter, 1);
lcd.print("m ");
lcd.setCursor(0,1);
lcd.print(arahKanan ? ">>" : "<<");
lcd.print(" ");
lcd.print(totalMeter, 1);
lcd.print("m ");
// Tambahkan indikator offset di pojok
lcd.setCursor(13,0);
lcd.print("+80");
}
else if (manualState != MANUAL_IDLE && manualState != MANUAL_SELESAI) {
lcd.setCursor(0,0);
lcd.print("M20:");
lcd.print(abs(putaranSaatIni), 1);
lcd.print("/20");
lcd.setCursor(0,1);
if (manualState == MANUAL_MAJU_20) {
lcd.print("MAJU CW ");
} else if (manualState == MANUAL_JEDA_1) {
lcd.print("JEDA... ");
} else if (manualState == MANUAL_MUNDUR_20) {
lcd.print("MUNDUR CCW ");
} else if (manualState == MANUAL_JEDA_2) {
lcd.print("JEDA... ");
}
}
else if (manualState == MANUAL_SELESAI) {
lcd.setCursor(0,0);
lcd.print("M20 SELESAI! ");
lcd.setCursor(0,1);
lcd.print("20CW 20CCW Done");
}
else if (prosesSelesai) {
lcd.setCursor(0,0);
lcd.print("SELESAI ");
lcd.setCursor(0,1);
lcd.print("Total:");
lcd.print(totalMeter, 2);
lcd.print("m");
}
else {
lcd.setCursor(0,0);
lcd.print("INPUT:");
lcd.print(inputBuffer);
for (int i = inputIndex; i < 6; i++) {
lcd.print(" ");
}
lcd.setCursor(0,1);
lcd.print("A=KIRI #=GO");
lcd.print(" ");
// Tampilkan status kalibrasi di pojok
if (meterPerPulse > 0 && meterPerPulse < 1) {
lcd.setCursor(13,1);
lcd.print("CLB");
}
}
}
// ======================================================
// STOP STEPPER
// ======================================================
void stopStepperNow() {
stepper.stop();
unsigned long timeout = millis() + 3000;
while (stepper.distanceToGo() != 0 && millis() < timeout) {
stepper.run();
}
long currentPos = stepper.currentPosition();
stepper.setCurrentPosition(currentPos);
stepper.moveTo(currentPos);
stepper.setSpeed(0);
digitalWrite(EN_PIN, HIGH);
}
// ======================================================
// EEPROM
// ======================================================
void saveCalibration() {
EEPROM.writeFloat(EEPROM_ADDR_METER_PP, meterPerPulse);
EEPROM.writeInt(EEPROM_ADDR_CALIBRATED, 1);
EEPROM.commit();
Serial.println("Kalibrasi tersimpan ke EEPROM!");
}
void loadCalibration() {
int calibrated = EEPROM.readInt(EEPROM_ADDR_CALIBRATED);
if (calibrated == 1) {
float saved = EEPROM.readFloat(EEPROM_ADDR_METER_PP);
if (saved > 0.0 && saved < 1.0) {
meterPerPulse = saved;
Serial.print("Calibration loaded: ");
Serial.println(meterPerPulse, 6);
}
}
}
// ======================================================
// KALIBRASI FUNCTIONS
// ======================================================
void toggleKalibrasi() {
if (autoMode || manualState != MANUAL_IDLE || stepperKiriJalan) {
Serial.println("ERROR: Sedang mode aktif!");
errorBuzzer();
return;
}
if (!kalibrasiMode) {
// Start kalibrasi
kalibrasiMode = true;
kalibrasiPulseStart = pulseCount;
kalibrasiStartTime = millis();
// Matikan auto mode
autoMode = false;
prosesSelesai = false;
// Hidupkan motor penarik dengan kecepatan rendah
motorSpeed = 50;
ledcWrite(RPWM, motorSpeed);
ledcWrite(LPWM, 0);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("KALIBRASI");
lcd.setCursor(0,1);
lcd.print("Gulung 1 meter");
Serial.println("");
Serial.println("========================================");
Serial.println(" MULAI KALIBRASI");
Serial.println("========================================");
Serial.println("Gulung kabel tepat 1 meter");
Serial.println("Tekan C lagi untuk selesai");
Serial.println("Atau tunggu 30 detik timeout");
Serial.println("========================================");
kalibrasiBuzzer();
} else {
// Stop kalibrasi
unsigned long currentPulse = pulseCount;
unsigned long totalPulse = currentPulse - kalibrasiPulseStart;
// Matikan motor
motorSpeed = 0;
ledcWrite(RPWM, 0);
ledcWrite(LPWM, 0);
kalibrasiMode = false;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Pulse:");
lcd.print(totalPulse);
Serial.println("");
Serial.println("========================================");
Serial.println(" SELESAI KALIBRASI");
Serial.println("========================================");
Serial.print("Total pulse untuk 1 meter: ");
Serial.println(totalPulse);
if (totalPulse > 0) {
float newMeterPerPulse = 1.0 / (float)totalPulse;
meterPerPulse = newMeterPerPulse;
Serial.print("meterPerPulse = ");
Serial.println(newMeterPerPulse, 6);
saveCalibration();
lcd.setCursor(0,1);
lcd.print("Saved! ");
lcd.print(meterPerPulse, 6);
kalibrasiBuzzer();
} else {
Serial.println("ERROR: Tidak ada pulse terbaca!");
lcd.setCursor(0,1);
lcd.print("ERROR! No pulse");
errorBuzzer();
}
delay(3000);
tampilLCD();
}
}
void checkKalibrasiTimeout() {
if (kalibrasiMode) {
if (millis() - kalibrasiStartTime > KALIBRASI_TIMEOUT) {
Serial.println("Kalibrasi timeout!");
// Matikan motor
motorSpeed = 0;
ledcWrite(RPWM, 0);
ledcWrite(LPWM, 0);
kalibrasiMode = false;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("TIMEOUT!");
lcd.setCursor(0,1);
lcd.print("Coba ulang");
errorBuzzer();
delay(2000);
tampilLCD();
}
}
}
// ======================================================
// RESET ENCODER DI AWAL PROSES
// ======================================================
void resetEncoderForProcess() {
if (!encoderReset) {
noInterrupts();
pulseCount = 0;
lastPulseCount = 0;
interrupts();
encoderReset = true;
resetPulseCount = 0;
// Set total meter dengan offset 80cm
totalMeter = ENCODER_OFFSET_METER;
sisaMeter = targetMeter - ENCODER_OFFSET_METER;
if (sisaMeter < 0) sisaMeter = 0;
Serial.println("=== ENCODER RESET ===");
Serial.print("Offset 80cm ditambahkan, totalMeter = ");
Serial.println(totalMeter, 3);
Serial.print("Sisa meter = ");
Serial.println(sisaMeter, 3);
}
}
// ======================================================
// SETUP
// ======================================================
void setup() {
Serial.begin(115200);
delay(1000);
EEPROM.begin(512);
loadCalibration();
Serial.println("========================================");
Serial.println(" M14 SPOOL 16cm - KABEL OTOMATIS");
Serial.println(" [v5.5 - With Encoder Offset 80cm]");
Serial.println("========================================");
Serial.print("Keypad Row: ");
Serial.print(rowPins[0]); Serial.print(", ");
Serial.print(rowPins[1]); Serial.print(", ");
Serial.print(rowPins[2]); Serial.print(", ");
Serial.println(rowPins[3]);
Serial.print("Keypad Col: ");
Serial.print(colPins[0]); Serial.print(", ");
Serial.print(colPins[1]); Serial.print(", ");
Serial.print(colPins[2]); Serial.print(", ");
Serial.println(colPins[3]);
Serial.print("Encoder CLK: "); Serial.println(ENC_CLK);
Serial.print("Encoder DT: "); Serial.println(ENC_DT);
Serial.print("Steps 20 putaran: ");
Serial.println(TARGET_STEPS_20);
Serial.print("Steps 65 putaran: ");
Serial.println(STEPS_65_PUTARAN);
Serial.print("Current meterPerPulse: ");
Serial.println(meterPerPulse, 6);
Serial.print("Encoder Offset: ");
Serial.print(ENCODER_OFFSET_METER);
Serial.println(" meter (80cm)");
Serial.println("========================================");
Serial.println("C = Kalibrasi (tekan 2x)");
Serial.println("A = Ke Kiri");
Serial.println("B = Manual 20 Putaran");
Serial.println("* = Stop");
Serial.println("# = GO (mulai proses)");
Serial.println("========================================");
lcdInit();
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(BUZZER_PIN, LOW);
pinMode(ENC_CLK, INPUT_PULLUP);
pinMode(ENC_DT, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(ENC_CLK), onEncoderPulse, FALLING);
pinMode(EN_PIN, OUTPUT);
digitalWrite(EN_PIN, HIGH);
ledcAttach(RPWM, PWM_FREQ, PWM_RESOLUTION);
ledcAttach(LPWM, PWM_FREQ, PWM_RESOLUTION);
ledcWrite(RPWM, 0);
ledcWrite(LPWM, 0);
stepper.setMaxSpeed(AUTO_MAX_SPEED);
stepper.setAcceleration(AUTO_ACCEL);
stepper.setSpeed(0);
tampilLCD();
Serial.println("System Ready");
}
// ======================================================
// LOOP
// ======================================================
// ======================================================
// LOOP (LENGKAP)
// ======================================================
void loop() {
stepper.run();
bacaKeypad();
bacaEncoder();
handleManual20();
checkKalibrasiTimeout();
if (stepperKiriJalan && stepper.distanceToGo() == 0) {
stepperKiriJalan = false;
digitalWrite(EN_PIN, HIGH);
Serial.println(">>> SUDAH SAMPAI KIRI <<<");
lcd.clear();
lcd.setCursor(0,0);
lcd.print("SUDAH KIRI");
delay(800);
tampilLCD();
}
if (autoMode) {
if (millis() - lastMotorRamp > 30) {
lastMotorRamp = millis();
if (motorSpeed < 100) motorSpeed += 4;
}
ledcWrite(RPWM, motorSpeed);
ledcWrite(LPWM, 0);
}
else {
if (millis() - lastMotorRamp > 30) {
lastMotorRamp = millis();
if (motorSpeed > 0) {
motorSpeed -= 8;
if (motorSpeed < 0) motorSpeed = 0;
}
}
ledcWrite(RPWM, motorSpeed);
ledcWrite(LPWM, 0);
}
if (autoMode && (millis() - lastPulseDetected > 15000)) {
autoMode = false;
stopStepperNow();
lcd.clear();
lcd.setCursor(0,0);
lcd.print("ENCODER ERROR");
errorBuzzer();
}
if (millis() - lastLCD > 200) {
tampilLCD();
lastLCD = millis();
}
if (millis() - lastSerialPrint > 1000) {
if (autoMode || kalibrasiMode || manualState != MANUAL_IDLE || stepperKiriJalan) {
Serial.print("Pos: ");
Serial.print(stepper.currentPosition());
Serial.print(" | KiriJalan: ");
Serial.print(stepperKiriJalan ? "YES" : "NO");
Serial.print(" | Putaran: ");
Serial.print(abs(putaranSaatIni), 2);
if (kalibrasiMode) {
Serial.print(" | Kalibrasi Pulse: ");
Serial.print(pulseCount - kalibrasiPulseStart);
}
if (autoMode) {
Serial.print(" | Total: ");
Serial.print(totalMeter, 3);
Serial.print("m | Sisa: ");
Serial.print(sisaMeter, 3);
Serial.print("m | Offset: 0.80m");
}
Serial.println("");
}
lastSerialPrint = millis();
}
}
// ======================================================
// HANDLE MANUAL 20 PUTARAN
// ======================================================
void handleManual20() {
if (manualState == MANUAL_IDLE || manualState == MANUAL_SELESAI) return;
hitungPutaranManual();
switch (manualState) {
case MANUAL_MAJU_20:
if (stepper.distanceToGo() == 0) {
Serial.println(">>> 20 PUTARAN MAJU SELESAI!");
Serial.print("Posisi: ");
Serial.print(stepper.currentPosition());
Serial.println(" steps");
manualState = MANUAL_JEDA_1;
manualJedaStart = millis();
}
break;
case MANUAL_JEDA_1:
if (millis() - manualJedaStart >= MANUAL_JEDA_MS) {
Serial.println(">>> MULAI MUNDUR 20 PUTARAN");
stepper.moveTo(0);
manualState = MANUAL_MUNDUR_20;
putaranSaatIni = 0;
}
break;
case MANUAL_MUNDUR_20:
if (stepper.distanceToGo() == 0) {
Serial.println(">>> 20 PUTARAN MUNDUR SELESAI!");
Serial.print("Posisi: ");
Serial.print(stepper.currentPosition());
Serial.println(" steps");
manualState = MANUAL_JEDA_2;
manualJedaStart = millis();
}
break;
case MANUAL_JEDA_2:
if (millis() - manualJedaStart >= MANUAL_JEDA_MS) {
Serial.println("=================================");
Serial.println(" MANUAL 20 PUTARAN SELESAI!");
Serial.println("=================================");
manualState = MANUAL_SELESAI;
digitalWrite(EN_PIN, HIGH);
for (int i = 0; i < 3; i++) {
beep(200);
delay(100);
}
}
break;
default:
break;
}
}
// ======================================================
// HITUNG PUTARAN REAL-TIME
// ======================================================
void hitungPutaranManual() {
long posisi = stepper.currentPosition();
putaranSaatIni = (float)posisi / STEPS_PER_REV;
}
// ======================================================
// FUNGSI MANUAL STEPPER
// ======================================================
void mulaiManualMaju20() {
if (autoMode || kalibrasiMode || stepperKiriJalan) {
Serial.println("ERROR: Sedang mode aktif!");
errorBuzzer();
return;
}
Serial.println("");
Serial.println(">>> MULAI MAJU 20 PUTARAN <<<");
Serial.print("Target: ");
Serial.print(TARGET_STEPS_20);
Serial.println(" steps");
manualState = MANUAL_MAJU_20;
putaranSaatIni = 0;
digitalWrite(EN_PIN, LOW);
stepper.setMaxSpeed(MANUAL_MAX_SPEED);
stepper.setAcceleration(MANUAL_ACCEL);
stepper.setCurrentPosition(0);
stepper.moveTo(TARGET_STEPS_20);
beep(100);
}
void mulaiManualMundur20() {
if (autoMode || kalibrasiMode || stepperKiriJalan) {
Serial.println("ERROR: Sedang mode aktif!");
errorBuzzer();
return;
}
Serial.println("");
Serial.println(">>> MULAI MUNDUR 20 PUTARAN <<<");
manualState = MANUAL_MUNDUR_20;
putaranSaatIni = 0;
digitalWrite(EN_PIN, LOW);
stepper.setMaxSpeed(MANUAL_MAX_SPEED);
stepper.setAcceleration(MANUAL_ACCEL);
stepper.setCurrentPosition(0);
stepper.moveTo(-TARGET_STEPS_20);
beep(100);
}
void stopManual20() {
Serial.println(">>> STOP MANUAL 20 <<<");
stepper.stop();
unsigned long timeout = millis() + 3000;
while (stepper.distanceToGo() != 0 && millis() < timeout) {
stepper.run();
}
long currentPos = stepper.currentPosition();
stepper.setCurrentPosition(currentPos);
stepper.moveTo(currentPos);
stepper.setSpeed(0);
digitalWrite(EN_PIN, HIGH);
manualState = MANUAL_IDLE;
putaranSaatIni = 0;
beep(100);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("MANUAL STOPPED");
delay(1000);
tampilLCD();
}
// ======================================================
// STEPPER KE KIRI (TOMBOL A)
// ======================================================
void toggleStepperKiri() {
if (autoMode || kalibrasiMode) {
Serial.println("ERROR: Sedang mode auto/kalibrasi!");
errorBuzzer();
return;
}
if (manualState != MANUAL_IDLE && manualState != MANUAL_SELESAI) {
Serial.println("ERROR: Sedang mode manual!");
errorBuzzer();
return;
}
if (!stepperKiriJalan) {
Serial.println("");
Serial.println(">>> MULAI KE KIRI <<<");
Serial.print("Dari: ");
Serial.print(stepper.currentPosition());
Serial.println(" -> 0");
stepperKiriJalan = true;
digitalWrite(EN_PIN, LOW);
stepper.setMaxSpeed(AUTO_MAX_SPEED);
stepper.setAcceleration(AUTO_ACCEL);
stepper.moveTo(0);
arahKanan = false;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("KE KIRI... ");
beep(100);
} else {
Serial.println(">>> STOP KE KIRI <<<");
stepperKiriJalan = false;
stopStepperNow();
lcd.clear();
lcd.setCursor(0,0);
lcd.print("STOP KIRI");
delay(1000);
tampilLCD();
}
}
// ======================================================
// KEYPAD
// ======================================================
void bacaKeypad() {
char key = keypad.getKey();
if (millis() - lastKey < 150) return;
if (!key) return;
lastKey = millis();
beep(15);
// ====================================================
// TOMBOL C = KALIBRASI (TANPA MENGGANGGU SISTEM LAIN)
// ====================================================
if (key == 'C') {
// Cek apakah bisa kalibrasi
if (autoMode || manualState != MANUAL_IDLE || stepperKiriJalan) {
Serial.println("ERROR: Sedang mode aktif!");
errorBuzzer();
return;
}
toggleKalibrasi();
return;
}
// Tombol A = TOGGLE KE KIRI
if (key == 'A') {
// Kalibrasi mode tidak bisa akses ke kiri
if (kalibrasiMode) {
Serial.println("ERROR: Sedang kalibrasi!");
errorBuzzer();
return;
}
toggleStepperKiri();
return;
}
// Tombol * = STOP
if (key == '*') {
if (stepperKiriJalan) {
toggleStepperKiri();
} else if (manualState != MANUAL_IDLE && manualState != MANUAL_SELESAI) {
stopManual20();
} else if (kalibrasiMode) {
// Jika kalibrasi, stop motor tapi tetap di mode kalibrasi
motorSpeed = 0;
ledcWrite(RPWM, 0);
ledcWrite(LPWM, 0);
Serial.println("Motor kalibrasi di-stop");
} else {
stopProses();
}
return;
}
// Tombol B = Manual Maju 20 + Mundur 20
if (key == 'B') {
if (autoMode || kalibrasiMode || stepperKiriJalan) {
Serial.println("ERROR: Sedang mode aktif!");
errorBuzzer();
return;
}
if (manualState != MANUAL_IDLE && manualState != MANUAL_SELESAI) {
stopManual20();
return;
}
mulaiManualMaju20();
return;
}
// Jika sedang kalibrasi, tombol lain tidak berfungsi (kecuali C dan *)
if (kalibrasiMode) return;
// Jika mode manual aktif, tombol angka tidak berfungsi
if (manualState != MANUAL_IDLE && manualState != MANUAL_SELESAI) return;
// Tombol D = DELETE / HAPUS
if (key == 'D') {
if (inputIndex > 0) {
inputIndex--;
inputBuffer[inputIndex] = '\0';
Serial.print("Hapus: inputBuffer = ");
Serial.println(inputBuffer);
tampilLCD();
}
return;
}
// Tombol angka = Input meter
if (key >= '0' && key <= '9') {
if (prosesSelesai) {
prosesSelesai = false;
inputBuffer[0] = '\0';
inputIndex = 0;
}
if (inputIndex < 5) {
inputBuffer[inputIndex] = key;
inputIndex++;
inputBuffer[inputIndex] = '\0';
Serial.print("Input: ");
Serial.println(inputBuffer);
tampilLCD();
}
}
else if (key == '#') {
if (inputIndex > 0) {
startProses();
}
}
}
// ======================================================
// START PROSES AUTO - 65 PUTARAN LOOP
// ======================================================
void startProses() {
if (manualState != MANUAL_IDLE && manualState != MANUAL_SELESAI) {
Serial.println("ERROR: Sedang mode manual!");
errorBuzzer();
return;
}
if (stepperKiriJalan) {
Serial.println("ERROR: Stepper sedang ke kiri!");
errorBuzzer();
return;
}
if (kalibrasiMode) {
Serial.println("ERROR: Sedang kalibrasi!");
errorBuzzer();
return;
}
targetMeter = atof(inputBuffer);
if (targetMeter <= 0.0 || targetMeter > 9999.0) {
lcd.clear();
lcd.setCursor(0,0);
lcd.print("ERROR: Invalid");
lcd.setCursor(0,1);
lcd.print("Input!");
errorBuzzer();
delay(2000);
inputBuffer[0] = '\0';
inputIndex = 0;
return;
}
// Cek apakah kalibrasi sudah dilakukan
if (meterPerPulse <= 0.0 || meterPerPulse >= 1.0) {
lcd.clear();
lcd.setCursor(0,0);
lcd.print("ERROR: Belum");
lcd.setCursor(0,1);
lcd.print("Kalibrasi!");
errorBuzzer();
delay(2000);
return;
}
prosesSelesai = false;
autoMode = true;
arahKanan = true;
motorSpeed = 0;
lastPulseDetected = millis();
encoderReset = false; // Reset flag untuk proses baru
// RESET ENCODER KE 0 DAN SET OFFSET 80CM
noInterrupts();
pulseCount = 0;
lastPulseCount = 0;
interrupts();
// Set total meter dengan offset 80cm
totalMeter = ENCODER_OFFSET_METER;
sisaMeter = targetMeter - ENCODER_OFFSET_METER;
if (sisaMeter < 0) sisaMeter = 0;
Serial.println("=== ENCODER RESET ===");
Serial.print("Offset 80cm ditambahkan, totalMeter = ");
Serial.println(totalMeter, 3);
Serial.print("Sisa meter = ");
Serial.println(sisaMeter, 3);
digitalWrite(EN_PIN, LOW);
stepper.setMaxSpeed(AUTO_MAX_SPEED);
stepper.setAcceleration(AUTO_ACCEL);
stepper.setMinPulseWidth(5);
stepper.setCurrentPosition(0);
stepper.moveTo(STEPS_65_PUTARAN);
Serial.println("");
Serial.println("=== PROSES DIMULAI ===");
Serial.print("Target: ");
Serial.print(targetMeter);
Serial.println(" meter");
Serial.print("meterPerPulse: ");
Serial.println(meterPerPulse, 6);
Serial.println("Stepper loop: 65 putaran kanan <-> kiri");
Serial.print("Encoder offset: ");
Serial.print(ENCODER_OFFSET_METER);
Serial.println(" meter (80cm)");
beep(100);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("MULAI");
lcd.setCursor(0,1);
lcd.print("Target:");
lcd.print(targetMeter, 1);
lcd.print("m");
}
// ======================================================
// STOP PROSES - BERHENTI TOTAL
// ======================================================
void stopProses() {
autoMode = false;
kalibrasiMode = false;
unsigned long stopTimeout = millis() + 2000;
while (motorSpeed > 0 && millis() < stopTimeout) {
if (millis() - lastMotorRamp > 30) {
lastMotorRamp = millis();
motorSpeed -= 8;
if (motorSpeed < 0) motorSpeed = 0;
ledcWrite(RPWM, motorSpeed);
}
delay(5);
}
ledcWrite(RPWM, 0);
ledcWrite(LPWM, 0);
stopStepperNow();
inputBuffer[0] = '\0';
inputIndex = 0;
prosesSelesai = true;
// Hitung total aktual (sudah termasuk offset 80cm)
float totalAktual = totalMeter;
if (totalAktual < 0) totalAktual = 0;
Serial.println("");
Serial.println("=== PROSES SELESAI ===");
Serial.print("Total meter (termasuk 80cm offset): ");
Serial.print(totalAktual, 3);
Serial.println("m");
Serial.print("Panjang kabel yang digulung: ");
Serial.print(totalAktual - ENCODER_OFFSET_METER, 3);
Serial.println("m (dari encoder)");
Serial.print("Target: ");
Serial.print(targetMeter, 3);
Serial.println("m");
selesaiBuzzer();
lcd.clear();
lcd.setCursor(0,0);
lcd.print("SELESAI");
lcd.setCursor(0,1);
lcd.print("Total:");
lcd.print(totalAktual, 2);
lcd.print("m");
}
// ======================================================
// ENCODER + STEPPER 65 PUTARAN LOOP
// ======================================================
void bacaEncoder() {
if (!autoMode && !kalibrasiMode) return;
unsigned long currentPulse = pulseCount;
if (currentPulse > lastPulseCount) {
unsigned long newPulses = currentPulse - lastPulseCount;
lastPulseCount = currentPulse;
lastPulseDetected = millis();
// Tambahkan pulse ke total meter (mulai dari offset 80cm)
totalMeter += (newPulses * meterPerPulse);
sisaMeter -= (newPulses * meterPerPulse);
if (sisaMeter < 0) sisaMeter = 0;
if (autoMode) {
if (stepper.distanceToGo() == 0) {
if (arahKanan) {
stepper.moveTo(0);
arahKanan = false;
Serial.println("Kanan 65x selesai -> Langsung kiri 65x");
} else {
stepper.moveTo(STEPS_65_PUTARAN);
arahKanan = true;
Serial.println("Kiri 65x selesai -> Langsung kanan 65x");
}
}
if (sisaMeter <= 0) {
Serial.println("TARGET TERCAPAI! STOP SEMUA.");
stopProses();
}
}
}
}