diff --git a/function-diagram.drawio b/function-diagram.drawio
index 496684e..2612b8a 100644
--- a/function-diagram.drawio
+++ b/function-diagram.drawio
@@ -1 +1,1055 @@
-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
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt
index d2b02cb..1fd9e2c 100644
--- a/main/CMakeLists.txt
+++ b/main/CMakeLists.txt
@@ -8,6 +8,7 @@ idf_component_register(
"display.cpp"
"cutter.cpp"
"switchesAnalog.cpp"
+ "stepper.cpp"
"guide-stepper.cpp"
"encoder.cpp"
INCLUDE_DIRS
diff --git a/main/config.hpp b/main/config.hpp
index dcd474b..95ca28b 100644
--- a/main/config.hpp
+++ b/main/config.hpp
@@ -85,7 +85,7 @@ extern "C" {
//----- stepper config -----
//--------------------------
//enable stepper test mode (dont start control and encoder task)
-//#define STEPPER_TEST
+#define STEPPER_TEST
#define STEPPER_STEP_PIN GPIO_NUM_18 //mos1
#define STEPPER_DIR_PIN GPIO_NUM_16 //ST3
#define STEPPER_EN_PIN GPIO_NUM_0 //not connected (-> stepper always on)
diff --git a/main/guide-stepper.cpp b/main/guide-stepper.cpp
index 8dbd96b..87d7ffa 100644
--- a/main/guide-stepper.cpp
+++ b/main/guide-stepper.cpp
@@ -7,7 +7,7 @@ extern "C"
#include "driver/adc.h"
}
-#include "DendoStepper.h"
+#include "stepper.hpp"
#include "config.hpp"
#include "guide-stepper.hpp"
#include "encoder.hpp"
@@ -46,11 +46,9 @@ extern "C"
//----------------------
//----- variables ------
//----------------------
-static DendoStepper step;
static const char *TAG = "stepper"; //tag for logging
static bool stepp_direction = true;
-static bool dir = true, dirPrev; //TODO local variables in travelSteps?
static uint32_t posNow = 0;
@@ -58,114 +56,98 @@ static uint32_t posNow = 0;
//----------------------
//----- functions ------
//----------------------
-//move axis certain Steps (relative) between left and right or reverse when negative
-void travelSteps(int stepsTarget){
- //posNow = step.getPositionMm(); //not otherwise controlled, so no update necessary
- int stepsToGo, remaining;
-
- stepsToGo = abs(stepsTarget);
- if(stepsTarget < 0) stepp_direction = !stepp_direction; //invert direction in reverse mode
-
-
- while (stepsToGo != 0){
-
- //--- wait if direction changed ---
- if (dirPrev != dir){
- ESP_LOGI(TAG, " dir-change detected - waiting for move to finish \n ");
- while(step.getState() != 1) vTaskDelay(1); //wait for move to finish
- }
-
- //--- currently moving right ---
- if (stepp_direction == true){ //currently moving right
- remaining = POS_MAX_STEPS - posNow; //calc remaining distance fom current position to limit
- if (stepsToGo > remaining){ //new distance will exceed limit
- step.runAbs (POS_MAX_STEPS); //move to limit
- dirPrev = dir;
- dir = 1;
- //while(step.getState() != 1) vTaskDelay(1); //wait for move to finish
- posNow = POS_MAX_STEPS;
- stepp_direction = false; //change current direction for next iteration
- stepsToGo = stepsToGo - remaining; //decrease target length by already traveled distance
- ESP_LOGI(TAG, " --- moved to max -> change direction (L) --- \n ");
- }
- else { //target distance does not reach the limit
- step.runAbs (posNow + stepsToGo); //move by (remaining) distance to reach target length
- dirPrev = dir;
- dir = 1;
- //-- dont wait for move to finish since moves in same direction get merged --
- //while(step.getState() != 1) vTaskDelay(1); //wait for move to finish
- ESP_LOGD(TAG, "moving to %d\n", posNow+stepsToGo);
- posNow += stepsToGo;
- stepsToGo = 0; //finished, reset target length (could as well exit loop/break)
- }
- }
-
- //--- currently moving left ---
- else {
- remaining = posNow - POS_MIN_STEPS;
- if (stepsToGo > remaining){
- step.runAbs (POS_MIN_STEPS);
- dirPrev = dir;
- dir = 0;
- //while(step.getState() != 1) vTaskDelay(2); //wait for move to finish
- posNow = POS_MIN_STEPS;
- stepp_direction = true;
- stepsToGo = stepsToGo - remaining;
- ESP_LOGI(TAG, " --- moved to min -> change direction (R) --- \n ");
- }
- else {
- step.runAbs (posNow - stepsToGo); //when moving left the coordinate has to be decreased
- dirPrev = dir;
- dir = 0;
- //-- dont wait for move to finish since moves in same direction get merged --
- //while(step.getState() != 1) vTaskDelay(2); //wait for move to finish
- ESP_LOGD(TAG, "moving to %d\n", posNow - stepsToGo);
- posNow -= stepsToGo;
- stepsToGo = 0;
- }
- }
- }
- if(stepsTarget < 0) stepp_direction = !stepp_direction; //undo inversion of stepp_direction after reverse mode is finished
- return;
-}
-
-
-//move axis certain Mm (relative) between left and right or reverse when negative
-void travelMm(int length){
- travelSteps(length * STEPPER_STEPS_PER_MM);
-}
-
-
-//define zero/start position
-//currently crashes into hardware limitation for certain time
-//TODO: limit switch
-void home() {
- ESP_LOGW(TAG, "auto-home...");
- step.setSpeedMm(100, 500, 10);
- step.runInf(1);
- vTaskDelay(1500 / portTICK_PERIOD_MS);
- step.stop();
- step.resetAbsolute();
- ESP_LOGW(TAG, "auto-home finished");
-}
+////move axis certain Steps (relative) between left and right or reverse when negative
+//void travelSteps(int stepsTarget){
+// //posNow = step.getPositionMm(); //not otherwise controlled, so no update necessary
+// int stepsToGo, remaining;
+//
+// stepsToGo = abs(stepsTarget);
+// if(stepsTarget < 0) stepp_direction = !stepp_direction; //invert direction in reverse mode
+//
+// while (stepsToGo != 0){
+// //--- currently moving right ---
+// if (stepp_direction == true){ //currently moving right
+// remaining = POS_MAX_STEPS - posNow; //calc remaining distance fom current position to limit
+// if (stepsToGo > remaining){ //new distance will exceed limit
+// //....step.runAbs (POS_MAX_STEPS); //move to limit
+// //....while(step.getState() != 1) vTaskDelay(1); //wait for move to finish
+// posNow = POS_MAX_STEPS;
+// stepp_direction = false; //change current direction for next iteration
+// stepsToGo = stepsToGo - remaining; //decrease target length by already traveled distance
+// ESP_LOGI(TAG, " --- moved to max -> change direction (L) --- \n ");
+// }
+// else { //target distance does not reach the limit
+// //....step.runAbs (posNow + stepsToGo); //move by (remaining) distance to reach target length
+// //....while(step.getState() != 1) vTaskDelay(1); //wait for move to finish
+// ESP_LOGD(TAG, "moving to %d\n", posNow+stepsToGo);
+// posNow += stepsToGo;
+// stepsToGo = 0; //finished, reset target length (could as well exit loop/break)
+// }
+// }
+//
+// //--- currently moving left ---
+// else {
+// remaining = posNow - POS_MIN_STEPS;
+// if (stepsToGo > remaining){
+// //....step.runAbs (POS_MIN_STEPS);
+// //....while(step.getState() != 1) vTaskDelay(2); //wait for move to finish
+// posNow = POS_MIN_STEPS;
+// stepp_direction = true;
+// stepsToGo = stepsToGo - remaining;
+// ESP_LOGI(TAG, " --- moved to min -> change direction (R) --- \n ");
+// }
+// else {
+// //....step.runAbs (posNow - stepsToGo); //when moving left the coordinate has to be decreased
+// while(step.getState() != 1) vTaskDelay(2); //wait for move to finish
+// ESP_LOGD(TAG, "moving to %d\n", posNow - stepsToGo);
+// posNow -= stepsToGo;
+// stepsToGo = 0;
+// }
+// }
+// }
+// if(stepsTarget < 0) stepp_direction = !stepp_direction; //undo inversion of stepp_direction after reverse mode is finished
+// return;
+//}
+//
+//
+////move axis certain Mm (relative) between left and right or reverse when negative
+//void travelMm(int length){
+// travelSteps(length * STEPPER_STEPS_PER_MM);
+//}
+//
+//
+////define zero/start position
+////currently crashes into hardware limitation for certain time
+////TODO: limit switch
+//void home() {
+// ESP_LOGW(TAG, "auto-home...");
+// //....step.setSpeedMm(100, 500, 10);
+// //....step.runInf(1);
+// vTaskDelay(1500 / portTICK_PERIOD_MS);
+// //....step.stop();
+// //....step.resetAbsolute();
+// ESP_LOGW(TAG, "auto-home finished");
+//}
//initialize/configure stepper instance
void init_stepper() {
- ESP_LOGW(TAG, "initializing stepper...");
- DendoStepper_config_t step_cfg = {
- .stepPin = STEPPER_STEP_PIN,
- .dirPin = STEPPER_DIR_PIN,
- .enPin = STEPPER_EN_PIN,
- .timer_group = TIMER_GROUP_0,
- .timer_idx = TIMER_0,
- .miStep = MICROSTEP_32,
- .stepAngle = 1.8};
- step.config(&step_cfg);
- step.init();
+// ESP_LOGW(TAG, "initializing stepper...");
+// DendoStepper_config_t step_cfg = {
+// .stepPin = STEPPER_STEP_PIN,
+// .dirPin = STEPPER_DIR_PIN,
+// .enPin = STEPPER_EN_PIN,
+// .timer_group = TIMER_GROUP_0,
+// .timer_idx = TIMER_0,
+// .miStep = MICROSTEP_32,
+// .stepAngle = 1.8};
+// //....step.config(&step_cfg);
+// //....step.init();
+//
+// //....step.setSpeed(1000, 1000, 1000); //random default speed
+// //....step.setStepsPerMm(STEPPER_STEPS_PER_MM); //guide: 4mm/rot
- step.setSpeed(1000, 1000, 1000); //random default speed
- step.setStepsPerMm(STEPPER_STEPS_PER_MM); //guide: 4mm/rot
+ stepper_init();
}
@@ -174,7 +156,7 @@ void updateSpeedFromAdc() {
int potiRead = gpio_readAdc(ADC_CHANNEL_POTI); //0-4095 GPIO34
double poti = potiRead/4095.0;
int speed = poti*(SPEED_MAX-SPEED_MIN) + SPEED_MIN;
- step.setSpeedMm(speed, ACCEL_MS, DECEL_MS);
+ //....step.setSpeedMm(speed, ACCEL_MS, DECEL_MS);
ESP_LOGW(TAG, "poti: %d (%.2lf%%), set speed to: %d", potiRead, poti*100, speed);
}
@@ -185,80 +167,121 @@ void updateSpeedFromAdc() {
//----------------------------
void task_stepper_test(void *pvParameter)
{
- init_stepper();
- home();
+ stepper_init();
+ int state = 0;
+ while(1){
+ vTaskDelay(20 / portTICK_PERIOD_MS);
- while (1) {
- updateSpeedFromAdc();
- step.runPosMm(STEPPER_TEST_TRAVEL);
- while(step.getState() != 1) vTaskDelay(2);
- ESP_LOGI(TAG, "finished moving right => moving left");
+ //------ handle switches ------
+ //run handle functions for all switches
+ SW_START.handle();
+ SW_RESET.handle();
+ SW_SET.handle();
+ SW_PRESET1.handle();
+ SW_PRESET2.handle();
+ SW_PRESET3.handle();
+ SW_CUT.handle();
+ SW_AUTO_CUT.handle();
- updateSpeedFromAdc();
- step.runPosMm(-STEPPER_TEST_TRAVEL);
- while(step.getState() != 1) vTaskDelay(2); //1=idle
- ESP_LOGI(TAG, "finished moving left => moving right");
- }
+ //cycle through test commands with one button
+ if (SW_RESET.risingEdge) {
+ switch (state){
+ case 0:
+ stepper_setTargetPosMm(50);
+ //stepper_setTargetPosSteps(1000);
+ state++;
+ break;
+ case 1:
+ stepper_setTargetPosMm(80);
+ //stepper_setTargetPosSteps(100);
+ state++;
+ break;
+ case 2:
+ stepper_setTargetPosMm(20);
+ //stepper_setTargetPosSteps(100);
+ state++;
+ break;
+ case 3:
+ stepper_setTargetPosMm(60);
+ //stepper_setTargetPosSteps(2000);
+ state = 0;
+ break;
+ }
+ }
+ }
+ // if (SW_PRESET1.risingEdge) {
+ // buzzer.beep(2, 300, 100);
+ // stepperSw_setTargetSteps(1000);
+ // }
+ // if (SW_PRESET2.risingEdge) {
+ // buzzer.beep(1, 500, 100);
+ // stepperSw_setTargetSteps(10000);
+ // }
+ // if (SW_PRESET3.risingEdge) {
+ // buzzer.beep(1, 100, 100);
+ // stepperSw_setTargetSteps(30000);
+ // }
}
+
//----------------------------
//----- TASK stepper-ctl -----
//----------------------------
void task_stepper_ctl(void *pvParameter)
{
- //variables
- int encStepsNow = 0; //get curret steps of encoder
- int encStepsPrev = 0; //steps at last check
- int encStepsDelta = 0; //steps changed since last iteration
-
- double cableLen = 0;
- double travelStepsExact = 0; //steps axis has to travel
- double travelStepsPartial = 0;
- int travelStepsFull = 0;
- double travelMm = 0;
- double turns = 0;
-
- float potiModifier;
-
- init_stepper();
- home();
-
- while(1){
- //get current length
- encStepsNow = encoder_getSteps();
-
- //calculate change
- encStepsDelta = encStepsNow - encStepsPrev; //FIXME MAJOR BUG: when resetting encoder/length in control task, diff will be huge!
-
- //read potentiometer and normalize (0-1) to get a variable for testing
- potiModifier = (float) gpio_readAdc(ADC_CHANNEL_POTI) / 4095; //0-4095 -> 0-1
- //ESP_LOGI(TAG, "current poti-modifier = %f", potiModifier);
-
- //calculate steps to move
- cableLen = (double)encStepsDelta * 1000 / ENCODER_STEPS_PER_METER;
- turns = cableLen / (PI * D_REEL);
- travelMm = turns * D_CABLE;
- travelStepsExact = travelMm * STEPPER_STEPS_PER_MM + travelStepsPartial; //convert mm to steps and add not moved partial steps
- travelStepsPartial = 0;
- travelStepsFull = (int)travelStepsExact;
-
- //move axis when ready to move at least 1 step
- if (abs(travelStepsFull) > 1){
- travelStepsPartial = fmod(travelStepsExact, 1); //save remaining partial steps to be added in the next iteration
- ESP_LOGD(TAG, "cablelen=%.2lf, turns=%.2lf, travelMm=%.3lf, travelStepsExact: %.3lf, travelStepsFull=%d, partialStep=%.3lf", cableLen, turns, travelMm, travelStepsExact, travelStepsFull, travelStepsPartial);
- ESP_LOGI(TAG, "MOVING %d steps", travelStepsFull);
- //TODO: calculate variable speed for smoother movement? for example intentionally lag behind and calculate speed according to buffered data
- step.setSpeedMm(35, 100, 50);
- //testing: get speed from poti
- //step.setSpeedMm(35, 1000*potiModifier+1, 1000*potiModifier+1);
- travelSteps(travelStepsExact);
- encStepsPrev = encStepsNow; //update previous length
- }
- else {
- //TODO use encoder queue to only run this check at encoder event?
- vTaskDelay(2);
- }
- }
+// //variables
+// int encStepsNow = 0; //get curret steps of encoder
+// int encStepsPrev = 0; //steps at last check
+// int encStepsDelta = 0; //steps changed since last iteration
+//
+// double cableLen = 0;
+// double travelStepsExact = 0; //steps axis has to travel
+// double travelStepsPartial = 0;
+// int travelStepsFull = 0;
+// double travelMm = 0;
+// double turns = 0;
+//
+// float potiModifier;
+//
+// init_stepper();
+// home();
+//
+// while(1){
+// //get current length
+// encStepsNow = encoder_getSteps();
+//
+// //calculate change
+// encStepsDelta = encStepsNow - encStepsPrev; //FIXME MAJOR BUG: when resetting encoder/length in control task, diff will be huge!
+//
+// //read potentiometer and normalize (0-1) to get a variable for testing
+// potiModifier = (float) gpio_readAdc(ADC_CHANNEL_POTI) / 4095; //0-4095 -> 0-1
+// //ESP_LOGI(TAG, "current poti-modifier = %f", potiModifier);
+//
+// //calculate steps to move
+// cableLen = (double)encStepsDelta * 1000 / ENCODER_STEPS_PER_METER;
+// turns = cableLen / (PI * D_REEL);
+// travelMm = turns * D_CABLE;
+// travelStepsExact = travelMm * STEPPER_STEPS_PER_MM + travelStepsPartial; //convert mm to steps and add not moved partial steps
+// travelStepsPartial = 0;
+// travelStepsFull = (int)travelStepsExact;
+//
+// //move axis when ready to move at least 1 step
+// if (abs(travelStepsFull) > 1){
+// travelStepsPartial = fmod(travelStepsExact, 1); //save remaining partial steps to be added in the next iteration
+// ESP_LOGD(TAG, "cablelen=%.2lf, turns=%.2lf, travelMm=%.3lf, travelStepsExact: %.3lf, travelStepsFull=%d, partialStep=%.3lf", cableLen, turns, travelMm, travelStepsExact, travelStepsFull, travelStepsPartial);
+// ESP_LOGI(TAG, "MOVING %d steps", travelStepsFull);
+// //TODO: calculate variable speed for smoother movement? for example intentionally lag behind and calculate speed according to buffered data
+// //....step.setSpeedMm(35, 100, 50);
+// //testing: get speed from poti
+// //step.setSpeedMm(35, 1000*potiModifier+1, 1000*potiModifier+1);
+// travelSteps(travelStepsExact);
+// encStepsPrev = encStepsNow; //update previous length
+// }
+// else {
+// //TODO use encoder queue to only run this check at encoder event?
+// vTaskDelay(2);
+// }
+// }
}
diff --git a/main/main.cpp b/main/main.cpp
index 7872214..b08f210 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -17,6 +17,8 @@ extern "C"
#include "guide-stepper.hpp"
#include "encoder.hpp"
+#include "stepper.hpp"
+
//=================================
//=========== functions ===========
@@ -92,17 +94,18 @@ extern "C" void app_main()
#ifdef STEPPER_TEST
//create task for stepper testing
- xTaskCreate(task_stepper_test, "task_stepper_test", configMINIMAL_STACK_SIZE * 3, NULL, 5, NULL);
+ xTaskCreate(task_stepper_test, "task_stepper_test", configMINIMAL_STACK_SIZE * 3, NULL, 2, NULL);
+ //xTaskCreate(task_stepper_debug, "task_stepper_test", configMINIMAL_STACK_SIZE * 3, NULL, 2, NULL);
#else
//create task for controlling the machine
- xTaskCreate(task_control, "task_control", configMINIMAL_STACK_SIZE * 3, NULL, 5, NULL);
+ xTaskCreate(task_control, "task_control", configMINIMAL_STACK_SIZE * 3, NULL, 4, NULL);
- //create task for controlling the machine
- xTaskCreate(task_stepper_ctl, "task_stepper_ctl", configMINIMAL_STACK_SIZE * 3, NULL, 5, NULL);
+ //create task for controlling the stepper
+ xTaskCreate(task_stepper_ctl, "task_stepper_ctl", configMINIMAL_STACK_SIZE * 3, NULL, 2, NULL);
+#endif
//create task for handling the buzzer
xTaskCreate(&task_buzzer, "task_buzzer", 2048, NULL, 2, NULL);
-#endif
//beep at startup
buzzer.beep(3, 70, 50);
diff --git a/main/stepper.cpp b/main/stepper.cpp
new file mode 100644
index 0000000..b586324
--- /dev/null
+++ b/main/stepper.cpp
@@ -0,0 +1,249 @@
+//custom driver for stepper motor
+#include "config.hpp"
+#include "hal/timer_types.h"
+#include
+#include
+
+extern "C" {
+#include "driver/timer.h"
+#include "driver/gpio.h"
+#include "esp_log.h"
+}
+
+//config from config.hpp
+//#define STEPPER_STEP_PIN GPIO_NUM_18 //mos1
+//#define STEPPER_DIR_PIN GPIO_NUM_16 //ST3
+
+#define STEPPER_STEPS_PER_MM 200/2 //steps/mm
+#define STEPPER_SPEED_DEFAULT 20 //mm/s
+#define STEPPER_SPEED_MIN 4 //mm/s - speed at which stepper immediately starts/stops
+#define STEPPER_ACCEL_INC 3 //steps/s per cycle
+#define STEPPER_DECEL_INC 8 //steps/s per cycle
+
+
+#define TIMER_F 1000000ULL
+#define TICK_PER_S TIMER_S
+#define NS_TO_T_TICKS(x) (x)
+
+
+
+//========================
+//=== global variables ===
+//========================
+static const char *TAG = "stepper-ctl"; //tag for logging
+
+bool direction = 1;
+bool directionTarget = 1;
+bool timerIsRunning = false;
+bool timer_isr(void *arg);
+
+static timer_group_t timerGroup = TIMER_GROUP_0;
+static timer_idx_t timerIdx = TIMER_0;
+
+//TODO the below variables can be moved to isr function once debug output is no longer needed
+static uint64_t posTarget = 0;
+static uint64_t posNow = 0;
+static uint64_t stepsToGo = 0;
+static uint32_t speedMin = STEPPER_SPEED_MIN * STEPPER_STEPS_PER_MM;
+static uint32_t speedNow = speedMin;
+static int debug = 0;
+static uint32_t speedTarget = STEPPER_SPEED_DEFAULT * STEPPER_STEPS_PER_MM;
+//TODO/NOTE increment actually has to be re-calculated every run to have linear accel (because also gets called faster/slower)
+static uint32_t decel_increment = STEPPER_DECEL_INC;
+static uint32_t accel_increment = STEPPER_ACCEL_INC;
+
+
+
+//======================
+//===== DEBUG task =====
+//======================
+void task_stepper_debug(void *pvParameter){
+ while (1){
+ ESP_LOGI("stepper-DEBUG",
+ "timer=%d "
+ "dir=%d "
+ "dirTarget=%d "
+ "posTarget=%llu "
+ "posNow=%llu "
+ "stepsToGo=%llu "
+ "speedNow=%u "
+ "speedTarget=%u "
+ "debug=%d ",
+
+ timerIsRunning,
+ direction,
+ directionTarget,
+ posTarget,
+ posNow,
+ stepsToGo,
+ speedNow,
+ speedTarget,
+ debug
+ );
+
+ vTaskDelay(300 / portTICK_PERIOD_MS);
+ }
+}
+
+
+
+//=====================
+//===== set speed =====
+//=====================
+void stepper_setSpeed(uint32_t speedMmPerS) {
+ ESP_LOGW(TAG, "set target speed from %u to %u mm/s (%u steps/s)",
+ speedTarget, speedMmPerS, speedMmPerS * STEPPER_STEPS_PER_MM);
+ speedTarget = speedMmPerS * STEPPER_STEPS_PER_MM;
+}
+
+
+
+//==========================
+//== set target pos STEPS ==
+//==========================
+void stepper_setTargetPosSteps(uint64_t target_steps) {
+ ESP_LOGW(TAG, "update target position from %llu to %llu steps (stepsNow: %llu", posTarget, target_steps, posNow);
+ posTarget = target_steps;
+
+ // Check if the timer is currently paused
+ if (!timerIsRunning){
+ // If the timer is paused, start it again with the updated targetSteps
+ timerIsRunning = true;
+ ESP_LOGW(TAG, "starting timer because did not run before");
+ ESP_ERROR_CHECK(timer_set_alarm_value(timerGroup, timerIdx, 1000));
+ //timer_set_counter_value(timerGroup, timerIdx, 1000);
+ ESP_ERROR_CHECK(timer_start(timerGroup, timerIdx));
+ }
+}
+
+
+
+//=========================
+//=== set target pos MM ===
+//=========================
+void stepper_setTargetPosMm(uint32_t posMm){
+ ESP_LOGW(TAG, "set target position to %u mm", posMm);
+ stepper_setTargetPosSteps(posMm * STEPPER_STEPS_PER_MM);
+}
+
+
+
+//========================
+//===== init stepper =====
+//========================
+void stepper_init(){
+ ESP_LOGW(TAG, "init - configure struct...");
+
+ // Configure pulse and direction pins as outputs
+ ESP_LOGW(TAG, "init - configure gpio pins...");
+ gpio_set_direction(STEPPER_DIR_PIN, GPIO_MODE_OUTPUT);
+ gpio_set_direction(STEPPER_STEP_PIN, GPIO_MODE_OUTPUT);
+
+ ESP_LOGW(TAG, "init - initialize/configure timer...");
+ timer_config_t timer_conf = {
+ .alarm_en = TIMER_ALARM_EN, // we need alarm
+ .counter_en = TIMER_PAUSE, // dont start now lol
+ .intr_type = TIMER_INTR_LEVEL, // interrupt
+ .counter_dir = TIMER_COUNT_UP, // count up duh
+ .auto_reload = TIMER_AUTORELOAD_EN, // reload pls
+ .divider = 80000000ULL / TIMER_F, // ns resolution
+ };
+ ESP_ERROR_CHECK(timer_init(timerGroup, timerIdx, &timer_conf)); // init the timer
+ ESP_ERROR_CHECK(timer_set_counter_value(timerGroup, timerIdx, 0)); // set it to 0
+ ESP_ERROR_CHECK(timer_isr_callback_add(timerGroup, timerIdx, timer_isr, (void *)timerIdx, 0));
+}
+
+
+
+//================================
+//=== timer interrupt function ===
+//================================
+bool timer_isr(void *arg) {
+
+ //-----------------
+ //--- variables ---
+ //-----------------
+ //TODO used (currently global) variables here
+
+ //-----------------------------------
+ //--- define direction, stepsToGo ---
+ //-----------------------------------
+ //Note: the idea is that the stepper has to decelerate to min speed first before changeing the direction
+ //define target direction depending on position difference
+ bool directionTarget = posTarget > posNow ? 1 : 0;
+ //DIRECTION DIFFERS (change)
+ if ( (direction != directionTarget) && (posTarget != posNow)) {
+ if (stepsToGo == 0){ //standstill
+ direction = directionTarget; //switch direction
+ gpio_set_level(STEPPER_DIR_PIN, direction);
+ stepsToGo = abs(int64_t(posTarget - posNow));
+ } else {
+ //set to minimun decel steps
+ stepsToGo = (speedNow - speedMin) / decel_increment;
+ }
+ }
+ //NORMAL (any direction 0/1)
+ else {
+ stepsToGo = abs(int64_t(posTarget - posNow));
+ }
+
+ //--------------------
+ //--- define speed ---
+ //--------------------
+ //FIXME noticed crash: division by 0 when min speed > target speed
+ uint64_t stepsDecelRemaining = (speedNow - speedMin) / decel_increment;
+ //DECELERATE
+ if (stepsToGo <= stepsDecelRemaining) {
+ //FIXME if stepsToGo gets updated (lowered) close to target while close to target, the stepper may stop too fast -> implement possibility to 'overshoot and reverse'?
+ if ((speedNow - speedMin) > decel_increment) {
+ speedNow -= decel_increment;
+ } else {
+ speedNow = speedMin; //PAUSE HERE??? / irrelevant?
+ }
+ }
+ //ACCELERATE
+ else if (speedNow < speedTarget) {
+ speedNow += accel_increment;
+ if (speedNow > speedTarget) speedNow = speedTarget;
+ }
+ //COASTING
+ else { //not relevant?
+ speedNow = speedTarget;
+ }
+
+ //-------------------------------
+ //--- update timer, increment ---
+ //-------------------------------
+ //AT TARGET -> STOP
+ if (stepsToGo == 0) {
+ timer_pause(timerGroup, timerIdx);
+ timerIsRunning = false;
+ speedNow = speedMin;
+ return 1;
+ }
+
+ //STEPS REMAINING -> NEXT STEP
+ //update timer with new speed
+ ESP_ERROR_CHECK(timer_set_alarm_value(timerGroup, timerIdx, TIMER_F / speedNow));
+
+ //generate pulse
+ GPIO.out_w1ts = (1ULL << STEPPER_STEP_PIN); //turn on (fast)
+ ets_delay_us(10);
+ GPIO.out_w1tc = (1ULL << STEPPER_STEP_PIN); //turn off (fast)
+
+ //increment pos
+ stepsToGo --;
+ if (direction == 1){
+ posNow ++;
+ } else {
+ //prevent underflow FIXME this case should not happen in the first place?
+ if (posNow != 0){
+ posNow --;
+ } else {
+ ESP_LOGE(TAG,"isr: posNow would be negative - ignoring decrement");
+ }
+ }
+ return 1;
+}
+
+
diff --git a/main/stepper.hpp b/main/stepper.hpp
new file mode 100644
index 0000000..3c1947a
--- /dev/null
+++ b/main/stepper.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+//init stepper pins and timer
+void stepper_init();
+//set absolute target position in steps
+void stepper_setTargetPosSteps(uint64_t steps);
+//set absolute target position in millimeters
+void stepper_setTargetPosMm(uint32_t posMm);
+//set target speed in millimeters per second
+void stepper_setSpeed(uint32_t speedMmPerS);
+
+//task that periodically logs variables for debugging stepper driver
+void task_stepper_debug(void *pvParameter);
diff --git a/testing/cnc-guide/main.c b/testing/cnc-guide/main.c
new file mode 100644
index 0000000..07c1dc3
--- /dev/null
+++ b/testing/cnc-guide/main.c
@@ -0,0 +1,104 @@
+#include
+#include
+#include
+#include
+#include
+
+#define MAX 100
+#define MIN 10
+#define TRAVEL MAX-MIN
+
+
+
+//==== VARIABLES ====
+bool direction = true;
+uint16_t posNow = 10;
+
+
+
+//==== FUNCTIONS ====
+
+//print position
+//print line that illustrates the position pos between 0 and MAX
+//e.g. "|----<=>--------|"
+void printPos(int pos){
+ int width = 50;
+ printf("(%d)|", pos);
+ for (int i = 0; i<(pos) * width/MAX; i++) printf("-");
+ printf("<=>");
+ for (int i = 0; i<(MAX-pos) * width/MAX; i++) printf("-");
+ printf("|\n");
+ return;
+}
+
+
+//move "virtual axis" to absolute coordinate in mm
+void moveToAbs(int d){
+ int posOld = posNow;
+ printf ("moving from %d %s to %d (%s)\n", posNow, direction ? "=>" : "<=", d, direction ? "RIGHT" : "LEFT");
+ posNow = d;
+ //illustrate movement (print position for each coordinate change)
+ for (int i=posOld; i!=posNow; i+=posNow>posOld?1:-1) printPos(i);
+ return;
+}
+
+
+//travel back and forth between MIN and MAX coordinate for a certain distance
+//negative values are processed reversed
+void travel(int length){
+ int d, remaining;
+ d = abs(length);
+ if(length < 0) direction = !direction; //invert direction in reverse mode
+
+ while (d != 0){
+ //--- currently moving right ---
+ if (direction == true){ //currently moving right
+ remaining = MAX - posNow; //calc remaining distance fom current position to limit
+ if (d > remaining){ //new distance will exceed limit
+ moveToAbs (MAX); //move to limit
+ direction = false; //change current direction for next iteration
+ d = d - remaining; //decrease target length by already traveled distance
+ printf(" --- changed direction (L) --- \n ");
+ }
+ else { //target distance does not reach the limit
+ moveToAbs (posNow + d); //move by (remaining) distance to reach target length
+ d = 0; //finished, reset target length (could as well exit loop/break)
+ }
+ }
+
+ //--- currently moving left ---
+ else {
+ remaining = posNow - MIN;
+ if (d > remaining){
+ moveToAbs (MIN);
+ direction = true;
+ d = d - remaining;
+ printf(" --- changed direction (R) --- \n ");
+ }
+ else {
+ moveToAbs (posNow - d); //when moving left the coordinate has to be decreased
+ d = 0;
+ }
+ }
+ }
+
+ if(length < 0) direction = !direction; //undo inversion of direction after reverse mode is finished
+ return;
+}
+
+
+
+//==== MAIN ====
+int main (void)
+{
+ int input;
+ printf("**cable-length-cutter testing cnc-guide**\n");
+ while(1){
+ printf("enter mm to travel:");
+ scanf("%d", &input);
+ travel(input);
+ printf("\n");
+ }
+ return 0;
+}
+
diff --git a/testing/cnc-guide/makefile b/testing/cnc-guide/makefile
new file mode 100644
index 0000000..9eabd10
--- /dev/null
+++ b/testing/cnc-guide/makefile
@@ -0,0 +1,7 @@
+default: program
+
+program:
+ gcc main.c -o a.out -lm
+
+clean:
+ -rm -f a.out