From ab86fb925fcea8dffd5b3b3443a44d9e97ce6392 Mon Sep 17 00:00:00 2001 From: Leszek Klich Date: Thu, 25 Jun 2026 22:13:46 +0200 Subject: [PATCH] poprawki --- include/config.h | 15 ++-- src/display.cpp | 17 ++-- src/main.cpp | 3 - src/motion.cpp | 26 +++--- src/ui.cpp | 38 +++------ test/test/test.ino | 192 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 238 insertions(+), 53 deletions(-) create mode 100644 test/test/test.ino diff --git a/include/config.h b/include/config.h index c030325..baffd5a 100644 --- a/include/config.h +++ b/include/config.h @@ -14,10 +14,10 @@ constexpr float WORKSPACE_HEIGHT_MM = 210.0f; constexpr float STEPS_PER_MM_X = 80.0f; constexpr float STEPS_PER_MM_Y = 80.0f; -constexpr float SPEED_DEFAULT_MM_S = 60.0f; +constexpr float SPEED_DEFAULT_MM_S = 50.0f; constexpr float SPEED_MIN_MM_S = 10.0f; constexpr float SPEED_MAX_MM_S = 150.0f; -constexpr float ACCEL_MM_S2 = 500.0f; +constexpr float ACCEL_MM_S2 = 500.0f; // Przyspieszenie łagodne, np. 2000, za szybko. // ── Homing ─────────────────────────────────────────────────────────────────── // Krańcówki wyzwalają się gdy głowica jest w PRAWYM GÓRNYM rogu KARTKI. @@ -47,7 +47,7 @@ constexpr int SERVO_SETTLE_MS = 150; // czas opadnięcia serwa [ms] constexpr uint8_t PIN_X_STEP = 26; constexpr uint8_t PIN_X_DIR = 27; constexpr uint8_t PIN_Y_STEP = 14; -constexpr uint8_t PIN_Y_DIR = 15; // GPIO15 — bezpieczny zamiennik (GPIO16 niedostępny) +constexpr uint8_t PIN_Y_DIR = 15; constexpr uint8_t PIN_STEPPER_EN = 13; // aktywny LOW // Wyłączniki krańcowe (INPUT_PULLUP, aktywny LOW — wyzwolony = LOW) @@ -81,8 +81,8 @@ constexpr uint8_t FILENAME_MAX_LEN = 64; // ── Timery UI ──────────────────────────────────────────────────────────────── constexpr uint32_t DISPLAY_UPDATE_MS = 200; constexpr uint32_t BUTTON_DEBOUNCE_MS = 50; -constexpr uint32_t BOOT_HOLD_MS = 2000; -constexpr uint32_t STEPPER_IDLE_TIMEOUT_MS = 5000; // auto-wyłączenie silników po bezczynności [ms], 0=wyłącz +constexpr uint32_t BOOT_HOLD_MS = 1000; +constexpr uint32_t STEPPER_IDLE_TIMEOUT_MS = 2000; // auto-wyłączenie silników po bezczynności [ms], 0=wyłącz // ── Stany aplikacji ─────────────────────────────────────────────────────────── enum class AppState : uint8_t { @@ -95,8 +95,9 @@ enum class AppState : uint8_t { ERROR }; -constexpr float TEST_STEP_MM = 1.0f; // krok na jedno naciśnięcie w trybie testowym [mm] -constexpr float TEST_SPEED_MM_S = 5.0f; // prędkość w trybie testowym [mm/s] +// Ustalenie prędkości dla trybu testowego +constexpr float TEST_STEP_MM = 10.0f; //1.0f; // krok na jedno naciśnięcie w trybie testowym [mm] +constexpr float TEST_SPEED_MM_S = 40.0f; //5.0f; // prędkość w trybie testowym [mm/s] // ── Komendy GCode ──────────────────────────────────────────────────────────── enum class GCodeCmd : uint8_t { diff --git a/src/display.cpp b/src/display.cpp index ad93253..c1622ce 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -81,19 +81,21 @@ void display_show_main_menu(uint8_t selected) { if (throttled()) return; markUpdated(); - static const char* const items[3] = {"Drukowanie", "Tryb UART", "Test osi"}; + static const char* const items[4] = { + "Drukowanie", "Tryb UART", "Test osi", "Kalibracja" + }; u8g2.clearBuffer(); u8g2.setFont(u8g2_font_6x10_tf); drawCentered(0, "CNC Draw - Menu"); - drawSep(12); + drawSep(11); - for (uint8_t i = 0; i < 3; i++) { - uint8_t yTop = 14 + i * 17; + for (uint8_t i = 0; i < 4; i++) { + uint8_t yTop = 13 + i * 13; if (i == selected) { u8g2.setDrawColor(1); - u8g2.drawBox(0, yTop - 1, OLED_W, 13); + u8g2.drawBox(0, yTop - 1, OLED_W, 12); u8g2.setDrawColor(0); u8g2.drawStr(4, yTop, items[i]); u8g2.setDrawColor(1); @@ -115,9 +117,10 @@ void display_show_axis_test(char axis, float posX, float posY, bool limitHit) { if (limitHit) { drawCentered(0, "! KRANC OW KA !"); drawSep(12); - drawCentered(20, "Cofanie..."); + drawCentered(22, "Ruch zatrzymany"); u8g2.setFont(u8g2_font_5x7_tf); - drawCentered(38, "czekaj"); + drawCentered(38, "Cofnij recznie" + ); } else { char hdr[22]; snprintf(hdr, sizeof(hdr), "Test osi: %c", axis); diff --git a/src/main.cpp b/src/main.cpp index a3030ac..af95762 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,9 +18,6 @@ void setup() { motion_init(); pen_init(); - display_show_homing(); - motion_home(); - ui_init(); // startuje w MAIN_MENU } diff --git a/src/motion.cpp b/src/motion.cpp index 94c6845..5330645 100644 --- a/src/motion.cpp +++ b/src/motion.cpp @@ -68,11 +68,12 @@ void motion_move_to(float x_mm, float y_mm, float feedrate_mm_per_min) { s_lastBaseFeedMmS = feedrate_mm_per_min / 60.0f; applySpeed(s_lastBaseFeedMmS); - // Kartezjański: każdy silnik steruje tylko swoją osią - long sx = (long)(x_mm * STEPS_PER_MM_X) * -HOMING_DIR_X; - long sy = (long)(y_mm * STEPS_PER_MM_Y) * -HOMING_DIR_Y; - s_stepperX.moveTo(sx); - s_stepperY.moveTo(sy); + // CoreXY: X = oba silniki ten sam kierunek, Y = oba silniki przeciwne kierunki + // motorA.moveTo(X + Y), motorB.moveTo(X - Y) + long ax = (long)(x_mm * STEPS_PER_MM_X) * -HOMING_DIR_X; + long ay = (long)(y_mm * STEPS_PER_MM_Y) * -HOMING_DIR_Y; + s_stepperX.moveTo(ax + ay); + s_stepperY.moveTo(ax - ay); } bool motion_run() { @@ -125,10 +126,8 @@ void motion_home() { s_stepperY.setMaxSpeed(HOMING_SPEED_MM_S * STEPS_PER_MM_Y); s_stepperY.setAcceleration(ACCEL_MM_S2 * STEPS_PER_MM_Y); - homeAxisSingle(s_stepperX, HOMING_DIR_X, PIN_LIMIT_X_MIN, - WORKSPACE_WIDTH_MM, STEPS_PER_MM_X); - homeAxisSingle(s_stepperY, HOMING_DIR_Y, PIN_LIMIT_Y_MIN, - WORKSPACE_HEIGHT_MM, STEPS_PER_MM_Y); + homeAxisSingle(s_stepperX, HOMING_DIR_X, PIN_LIMIT_X_MIN, WORKSPACE_WIDTH_MM, STEPS_PER_MM_X); + homeAxisSingle(s_stepperY, HOMING_DIR_Y, PIN_LIMIT_Y_MIN, WORKSPACE_HEIGHT_MM, STEPS_PER_MM_Y); applySpeed(s_lastBaseFeedMmS > 0 ? s_lastBaseFeedMmS : SPEED_DEFAULT_MM_S); s_stepperX.setAcceleration(ACCEL_MM_S2 * STEPS_PER_MM_X); @@ -154,12 +153,17 @@ void motion_disable() { digitalWrite(PIN_STEPPER_EN, HIGH); } +// CoreXY odwrotność: X = (A+B)/2, Y = (A-B)/2 float motion_get_x_mm() { - return (float)s_stepperX.currentPosition() / STEPS_PER_MM_X * (-HOMING_DIR_X); + long a = s_stepperX.currentPosition(); + long b = s_stepperY.currentPosition(); + return (float)(a + b) / 2.0f / STEPS_PER_MM_X * (-HOMING_DIR_X); } float motion_get_y_mm() { - return (float)s_stepperY.currentPosition() / STEPS_PER_MM_Y * (-HOMING_DIR_Y); + long a = s_stepperX.currentPosition(); + long b = s_stepperY.currentPosition(); + return (float)(a - b) / 2.0f / STEPS_PER_MM_Y * (-HOMING_DIR_Y); } bool motion_any_limit_triggered() { diff --git a/src/ui.cpp b/src/ui.cpp index bed4cf8..1b192b0 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -152,14 +152,14 @@ void ui_update() { // ── Menu startowe ────────────────────────────────────────────────────────── case AppState::MAIN_MENU: if (ev == ButtonEvent::UP_PRESS) { - s_menuSelected = (s_menuSelected + 2) % 3; + s_menuSelected = (s_menuSelected + 3) % 4; display_force_update(); } else if (ev == ButtonEvent::DOWN_PRESS) { - s_menuSelected = (s_menuSelected + 1) % 3; + s_menuSelected = (s_menuSelected + 1) % 4; display_force_update(); } else if (ev == ButtonEvent::OK_PRESS) { if (s_menuSelected == 0) { - // Drukowanie → inicjuj SD jeśli potrzeba, przejdź do listy + // Drukowanie if (!s_sdInited) { if (!sd_init()) { ui_set_error("Blad SD!", "Wloz karte SD"); @@ -176,13 +176,20 @@ void ui_update() { uart_mode_init(); s_state = AppState::UART_MODE; display_force_update(); - } else { + } else if (s_menuSelected == 2) { // Test osi s_testAxis = 'X'; motion_enable(); pen_up(); s_state = AppState::AXIS_TEST; display_force_update(); + } else { + // Kalibracja + display_show_homing(); + motion_enable(); + motion_home(); + display_force_update(); + display_show_main_menu(s_menuSelected); } } display_show_main_menu(s_menuSelected); @@ -272,27 +279,8 @@ void ui_update() { logAxisTest(lm ? "KRANC!" : "OK ", s_testAxis, posX, posY); } - // Automatyczne cofanie gdy krańcówka wyzwolona i silniki stoją - if (limitActive && motion_is_idle()) { - const float backoff = 3.0f; - float nx = posX; - float ny = posY; - if (digitalRead(PIN_LIMIT_X_MIN) == LOW) nx += backoff; - if (digitalRead(PIN_LIMIT_X_MAX) == LOW) nx -= backoff; - if (digitalRead(PIN_LIMIT_Y_MIN) == LOW) ny += backoff; - if (digitalRead(PIN_LIMIT_Y_MAX) == LOW) ny -= backoff; - if (nx < 0.0f) nx = 0.0f; - if (nx > WORKSPACE_WIDTH_MM) nx = WORKSPACE_WIDTH_MM; - if (ny < 0.0f) ny = 0.0f; - if (ny > WORKSPACE_HEIGHT_MM) ny = WORKSPACE_HEIGHT_MM; - motion_enable(); - motion_move_to(nx, ny, TEST_SPEED_MM_S * 60.0f); - logAxisTest("COFANIE", s_testAxis, nx, ny); - display_force_update(); - } - - // Przyciski aktywne tylko gdy silniki stoją i żadna krańcówka nie wyzwolona - if (!limitActive && motion_is_idle()) { + // Przyciski aktywne gdy silniki stoją (motion_run blokuje ruch ku krańcówce) + if (motion_is_idle()) { if (ev == ButtonEvent::UP_PRESS) { float nx = posX + (s_testAxis == 'X' ? TEST_STEP_MM : 0.0f); float ny = posY + (s_testAxis == 'Y' ? TEST_STEP_MM : 0.0f); diff --git a/test/test/test.ino b/test/test/test.ino new file mode 100644 index 0000000..78e0bde --- /dev/null +++ b/test/test/test.ino @@ -0,0 +1,192 @@ +#include + +// ── Pole robocze i Ruch (Zgodnie z plikiem config.h) ──────────────────── +constexpr float STEPS_PER_MM_X = 80.0f; // +constexpr float STEPS_PER_MM_Y = 80.0f; // +constexpr float TEST_SPEED_MM_S = 150.0f; // Wolna prędkość testowa z config.h +constexpr float ACCEL_MM_S2 = 200.0f; //100.0f; // Łagodne przyspieszenie + +// ── Piny GPIO (Zaktualizowane na podstawie pliku config.h) ───────────────────── +constexpr uint8_t PIN_X_STEP = 26; // +constexpr uint8_t PIN_X_DIR = 27; // +constexpr uint8_t PIN_Y_STEP = 14; // +constexpr uint8_t PIN_Y_DIR = 15; // +constexpr uint8_t PIN_STEPPER_EN = 13; // + +// Wyłączniki krańcowe (Zaktualizowane piny z config.h) +constexpr uint8_t PIN_LIMIT_X_MIN = 36; // LEWA STRONA (DOM X) +constexpr uint8_t PIN_LIMIT_X_MAX = 34; // PRAWA STRONA (KONIEC X) +constexpr uint8_t PIN_LIMIT_Y_MIN = 39; // +constexpr uint8_t PIN_LIMIT_Y_MAX = 35; // + +// Serwo i Przyciski (Zgodnie z config.h) +constexpr uint8_t PIN_SERVO = 25; // +constexpr int SERVO_ANGLE_UP = 90; // +constexpr int SERVO_ANGLE_DOWN = 50; // +constexpr uint8_t PIN_BTN_UP = 32; // +constexpr uint8_t PIN_BTN_DOWN = 33; // +constexpr uint8_t PIN_BTN_OK = 4; // + +// ── Konfiguracja Timera Oszczędzania Energii ───────────────────────────────── +constexpr uint32_t STEPPER_TIMEOUT_MS = 2000; // 2 sekundy bezczynności przed odłączeniem prądu + +// Inicjalizacja obiektów dla silników CoreXY / H-Bot +AccelStepper motorA(AccelStepper::DRIVER, PIN_X_STEP, PIN_X_DIR); +AccelStepper motorB(AccelStepper::DRIVER, PIN_Y_STEP, PIN_Y_DIR); + +// Zmienne pomocnicze +bool isPenDown = false; +bool lastBtnOkState = HIGH; +uint32_t lastActivityTime = 0; +bool steppersEnabled = false; + +// Funkcja włączająca prąd w silnikach +void enableSteppers() { + if (!steppersEnabled) { + digitalWrite(PIN_STEPPER_EN, LOW); // LOW = Włączone + steppersEnabled = true; + Serial.println("[ZASILANIE] Silniki włączone"); + } + lastActivityTime = millis(); // Resetowanie licznika czasu aktywności +} + +// Funkcja odcinająca prąd w silnikach (ochrona przed grzaniem) +void disableSteppers() { + if (steppersEnabled) { + motorA.stop(); + motorB.stop(); + digitalWrite(PIN_STEPPER_EN, HIGH); // HIGH = Odłączone + steppersEnabled = false; + Serial.println("[ZASILANIE] Bezczynność 4s -> Odcięcie prądu (Silniki chłodne)"); + } +} + +// Sterowanie serwem (natywne PWM dla ESP32) +void setServoAngle(uint8_t pin, int angle) { + int dutyCycle = map(angle, 0, 180, 13, 26); + analogWrite(pin, dutyCycle); +} + +// Sprawdzenie stanu krańcówki (LOW = wciśnięta) +bool isLimitTriggered(uint8_t pin) { + return digitalRead(pin) == LOW; +} + +void setup() { + Serial.begin(115200); + //while (!Serial) delay(10); + delay(500); + + // Konfiguracja wejść + pinMode(PIN_LIMIT_X_MIN, INPUT); + pinMode(PIN_LIMIT_X_MAX, INPUT); + pinMode(PIN_LIMIT_Y_MIN, INPUT); + pinMode(PIN_LIMIT_Y_MAX, INPUT); + + pinMode(PIN_BTN_UP, INPUT_PULLUP); + pinMode(PIN_BTN_DOWN, INPUT_PULLUP); + pinMode(PIN_BTN_OK, INPUT_PULLUP); + + // Konfiguracja pinu Enable silników + pinMode(PIN_STEPPER_EN, OUTPUT); + steppersEnabled = false; + enableSteppers(); + + // Konfiguracja PWM dla Serwa (50Hz) + analogWriteFrequency(PIN_SERVO, 50); + setServoAngle(PIN_SERVO, SERVO_ANGLE_UP); // Pisak w górze na starcie + + // Maksymalne prędkości silników + motorA.setMaxSpeed(TEST_SPEED_MM_S * STEPS_PER_MM_X); + motorB.setMaxSpeed(TEST_SPEED_MM_S * STEPS_PER_MM_Y); +} + +void loop() { + uint32_t currentTime = millis(); + + // ─── 1. OBSŁUGA SERWA (PRZYCISK OK) ────────────────────────────────────── + // bool currentBtnOkState = digitalRead(PIN_BTN_OK); + + // if (currentBtnOkState == LOW && lastBtnOkState == HIGH) { + // delay(50); // Debounce + // if (digitalRead(PIN_BTN_OK) == LOW) { + // enableSteppers(); // Aktywność użytkownika wybudza silniki + // isPenDown = !isPenDown; + + // if (isPenDown) { + // setServoAngle(PIN_SERVO, SERVO_ANGLE_DOWN); // + // Serial.println("[SERWO] Pisak opuszczony (DOWN)"); + // } else { + // setServoAngle(PIN_SERVO, SERVO_ANGLE_UP); // + // Serial.println("[SERWO] Pisak podniesiony (UP)"); + // } + // } + // } + // lastBtnOkState = currentBtnOkState; + + // ─── 2. OBSŁUGA RUCHU OSI X (PRZYCISKI UP / DOWN) ──────────────────────── + bool upPressed = (digitalRead(PIN_BTN_UP) == LOW); + bool downPressed = (digitalRead(PIN_BTN_DOWN) == LOW); + + // if (isLimitTriggered(PIN_LIMIT_X_MAX)){ + // Serial.println("Krańcówka X_MAX (PRAWA) aktywna!"); + // } + + // if (isLimitTriggered(PIN_LIMIT_X_MIN)){ + // Serial.println("Krańcówka X_MIN (LEWA) aktywna!"); + // } + + // if (isLimitTriggered(PIN_LIMIT_Y_MIN)){ + // Serial.println("Krańcówka Y_MIN (GORNA) aktywna!"); + // } + + // if (isLimitTriggered(PIN_LIMIT_Y_MAX)){ + // Serial.println("Krańcówka X_MAX (DOLNA) aktywna!"); + // } + + + if (upPressed && !downPressed) { // Wciśnięty UP + enableSteppers(); + + // --- RUCH W PRAWO --- + // Sprawdzamy, czy nie została osiągnięta prawa krańcówka (X_MAX) + if (isLimitTriggered(PIN_LIMIT_X_MAX)) { + Serial.println("[STOP] Krańcówka X_MAX (PRAWA) aktywna! Ruch zablokowany."); + motorA.stop(); + motorB.stop(); + } else { + // Oba silniki kręcą się w tę samą stronę (np. wartości dodatnie) + motorA.setSpeed(TEST_SPEED_MM_S * STEPS_PER_MM_X); + motorB.setSpeed(TEST_SPEED_MM_S * STEPS_PER_MM_Y); + motorA.runSpeed(); + motorB.runSpeed(); + } + } + else if (downPressed && !upPressed) { // Przycisk DOWN + enableSteppers(); + + // --- RUCH W LEWO --- + // Sprawdzamy, czy nie została osiągnięta lewa krańcówka (X_MIN) + if (isLimitTriggered(PIN_LIMIT_X_MIN)) { + Serial.println("[STOP] Krańcówka X_MIN (LEWA) aktywna! Ruch zablokowany."); + motorA.stop(); + motorB.stop(); + } else { + // Oba silniki kręcą się w tę samą stronę, ale w przeciwnym kierunku (ujemne prędkości) + motorA.setSpeed(-(TEST_SPEED_MM_S * STEPS_PER_MM_X)); + motorB.setSpeed(-(TEST_SPEED_MM_S * STEPS_PER_MM_Y)); + motorA.runSpeed(); + motorB.runSpeed(); + } + } + // else { + // // Gdy żaden przycisk nie jest wciśnięty -> wyhamuj silniki + // motorA.stop(); + // motorB.stop(); + // } + + // ─── 3. AUTOMATYCZNY TIMER USYPIANIA SILNIKÓW ──────────────────────────── + if (steppersEnabled && (currentTime - lastActivityTime >= STEPPER_TIMEOUT_MS)) { + disableSteppers(); + } +} \ No newline at end of file