diff --git a/.github/workflows/test-build-app.yaml b/.github/workflows/test-build-app.yaml index c77f736..f1c4230 100644 --- a/.github/workflows/test-build-app.yaml +++ b/.github/workflows/test-build-app.yaml @@ -9,7 +9,7 @@ jobs: image: espressif/idf:release-v4.4 strategy: matrix: - test-apps: [bar_graph, lsa, motor_driver_normal, motor_driver_parallel, mpu6050, servos, switches, oled] + test-apps: [bar_graph, lsa, motor_driver_normal, motor_driver_parallel, mpu6050, servos, switches, oled, stepper_motor] steps: - name: Force Install GIT latest run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e5b80b..69b896c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -set(srcs "src/i2cdev.c" "src/adc.c" "src/bar_graph.c" "src/switches.c" "src/lsa.c" "src/motor_driver.c" "src/mpu6050.c" "src/servo.c" "src/utils.c") +set(srcs "src/i2cdev.c" "src/adc.c" "src/bar_graph.c" "src/switches.c" "src/lsa.c" "src/motor_driver.c" "src/mpu6050.c" "src/servo.c" "src/utils.c" "src/stepper.c") set(include_dirs include) if(CONFIG_ENABLE_OLED) diff --git a/README.md b/README.md index 05d2a9f..63ce544 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,7 @@ _For more examples, please refer to the [Documentation](https://sravjti.tech/sra | [Servos](https://github.com/SRA-VJTI/sra-board-component/tree/main/examples/servos) | Servos are used in the locomotion of bots like [Wall-E](https://github.com/SRA-VJTI/Wall-E_v2.1) . This example is for using the servo port on SRA Board to run servos | | [Switches](https://github.com/SRA-VJTI/sra-board-component/tree/main/examples/switches) | Manual Switches provide the traditional comfort to toggle the input variables. This example is for using four user switches present on SRA Board | | [OLED](https://github.com/SRA-VJTI/sra-board-component/tree/main/examples/oled) | Example for initialising the OLED and diplaying the "Hello World" on the screen | +| [Stepper Motor](https://github.com/SRA-VJTI/sra-board-component/tree/main/examples/stepper_motor) | Example for testing the stepper motor driver A4988 along with a bipolar stepper motor using ESP32 | ## Roadmap diff --git a/examples/README.md b/examples/README.md index 49951c2..ad00899 100644 --- a/examples/README.md +++ b/examples/README.md @@ -47,4 +47,10 @@ Example for using two motor drivers present on SRA Board in both parallel and no - Manual Switches provide the traditional comfort to toggle the input variables -- [Example](https://github.com/SRA-VJTI/sra-board-component/tree/main/examples/switches) for using four user switches present on SRA Board \ No newline at end of file +- [Example](https://github.com/SRA-VJTI/sra-board-component/tree/main/examples/switches) for using four user switches present on SRA Board + +### Stepper Motor + +- Stepper motors are used in application involving powerful and precise rotary movements + +- [Example](https://github.com/SRA-VJTI/sra-board-component/tree/main/examples/stepper_motor) \ No newline at end of file diff --git a/examples/stepper_motor/CMakeLists.txt b/examples/stepper_motor/CMakeLists.txt new file mode 100644 index 0000000..63894a0 --- /dev/null +++ b/examples/stepper_motor/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(EXTRA_COMPONENT_DIRS ../../.) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(stepper_motor) \ No newline at end of file diff --git a/examples/stepper_motor/LICENSE b/examples/stepper_motor/LICENSE new file mode 100644 index 0000000..4e84afd --- /dev/null +++ b/examples/stepper_motor/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Society of Robotics and Automation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/examples/stepper_motor/Makefile b/examples/stepper_motor/Makefile new file mode 100644 index 0000000..6c26a1f --- /dev/null +++ b/examples/stepper_motor/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := stepper_motor + +include $(IDF_PATH)/make/project.mk + diff --git a/examples/stepper_motor/main/CMakeLists.txt b/examples/stepper_motor/main/CMakeLists.txt new file mode 100644 index 0000000..e6c20ac --- /dev/null +++ b/examples/stepper_motor/main/CMakeLists.txt @@ -0,0 +1,8 @@ +# Edit following two lines to set component requirements (see docs) +set(COMPONENT_REQUIRES ) +set(COMPONENT_PRIV_REQUIRES ) + +set(COMPONENT_SRCS "main.c") +set(COMPONENT_ADD_INCLUDEDIRS "") + +register_component() diff --git a/examples/stepper_motor/main/component.mk b/examples/stepper_motor/main/component.mk new file mode 100644 index 0000000..61f8990 --- /dev/null +++ b/examples/stepper_motor/main/component.mk @@ -0,0 +1,8 @@ +# +# Main component makefile. +# +# This Makefile can be left empty. By default, it will take the sources in the +# src/ directory, compile them and link them into lib(subdirectory_name).a +# in the build directory. This behaviour is entirely configurable, +# please read the ESP-IDF documents if you need to do this. +# diff --git a/examples/stepper_motor/main/main.c b/examples/stepper_motor/main/main.c new file mode 100644 index 0000000..e6c11fe --- /dev/null +++ b/examples/stepper_motor/main/main.c @@ -0,0 +1,59 @@ +/* + * MIT License + * + * Copyright (c) 2023 Society of Robotics and Automation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "sra_board.h" + +static const char *TAG = "STEPPER_DEBUG"; + +static const stepper_config_t stepper_1 = { + .step_pin = STEP, + .dir_pin = DIR, + .accl_decel = 0.001, + .min_step_interval_us = MIN_STEP_INTERVAL, + .steps_per_revolution = 200, + .current_step_count = 0, +}; + +void run_motor(void *arg) +{ + while (1) + { + ESP_LOGI(TAG, "Inside task"); + move_to_step(&stepper_1, 10000); + ESP_LOGI(TAG, "Done 1"); + vTaskDelay(1000 / portTICK_PERIOD_MS); + move_to_step(&stepper_1, 0); + ESP_LOGI(TAG, "Done 2"); + vTaskDelay(1000 / portTICK_PERIOD_MS); + } +} + +void app_main(void) +{ + init_stepper(&stepper_1); + xTaskCreatePinnedToCore(&run_motor, "Homing Task", 4096, NULL, 1, NULL, 0); +} diff --git a/examples/stepper_motor/sdkconfigs.defaults b/examples/stepper_motor/sdkconfigs.defaults new file mode 100644 index 0000000..8f0a464 --- /dev/null +++ b/examples/stepper_motor/sdkconfigs.defaults @@ -0,0 +1 @@ +CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=n \ No newline at end of file diff --git a/include/pin_defs.h b/include/pin_defs.h index 4db8e06..7624545 100644 --- a/include/pin_defs.h +++ b/include/pin_defs.h @@ -105,4 +105,7 @@ #define SERVO_D 18 ///////////////////////////// +#define STEP 14 +#define DIR 27 + #endif \ No newline at end of file diff --git a/include/sra_board.h b/include/sra_board.h index 44a28d1..27fa4ca 100644 --- a/include/sra_board.h +++ b/include/sra_board.h @@ -34,6 +34,7 @@ #include "servo.h" #include "pin_defs.h" #include "utils.h" +#include "stepper.h" #ifdef CONFIG_ENABLE_OLED #include "oled.h" diff --git a/include/stepper.h b/include/stepper.h new file mode 100644 index 0000000..9015960 --- /dev/null +++ b/include/stepper.h @@ -0,0 +1,127 @@ +/* + * MIT License + * + * Copyright (c) 2023 Society of Robotics and Automation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef STEPPER_H +#define STEPPER_H + +#include +#include +#include +#include +#include "driver/gpio.h" +#include "esp_timer.h" +#include "esp32/rom/ets_sys.h" +#include "esp_log.h" +#include "esp_err.h" +#include "esp_sleep.h" +#include "pin_defs.h" + +#define MIN_STEP_INTERVAL 1000 + +typedef struct +{ + int step_pin; // Stepping Signal Pin of ESP32. + int dir_pin; // Direction signal Pin of ESP32. + int min_step_interval_us; // minimum step interval in microseconds + int steps_per_revolution; // Number of Step pulses per revolution. + int prev_delay; // Internal varaible to store previous pulse interval. + int dir; // Internal variable to store the direction of stepper. + float accl_decel; // Accelaration/Decelaration value. + bool movement_done; // To check if movement is done. + long current_step_count; // Internal variable to store current position of stepper motor. +} stepper_config_t; + +/** @struct stepper_config_t + * @brief This structure contains the configuration of stepper. + * @var stepper_config_t::step_pin + * Member 'step_pin' contains the gpio pin number to which step signal pin of stepper motor driver is connected + * @var stepper_config_t::dir_pin + * Member 'dir_pin' contains the gpio pin number to which direction signal pin of stepper motor driver is connected + * @var stepper_config_t::min_step_interval_us + * Member 'min_step_interval_us' contains the minimum pulse interval for the step signal of stepper motor + * @var stepper_config_t::steps_per_revolution + * Member 'steps_per_revolution' contains number of step pulses per revolution. + * @var stepper_config_t::prev_delay + * Member 'prev_delay' contains internal varaible to store previous pulse interval. + * @var stepper_config_t::dir + * Member 'dir' contains internal variable to store the direction of stepper. + * @var stepper_config_t::accl_decel + * Member 'accl_decel' contains accelaration/decelaration value. + * @var stepper_config_t::movement_done + * Member 'movement_done' contains variable to check if movement is done. + * @var stepper_config_t::current_step_count + * Member 'current_step_count' contains internal variable to store current position of stepper motor. + */ + +/** + * @brief Initiates and configures ESP32 for stepper control + + * @param stepper pointer to stepper_config_t struct + + * @return esp_err_t i.e it shows if pins are configured successfully or not. + **/ +esp_err_t init_stepper(stepper_config_t *stepper); + +/** + * @brief calculates first pulse interval delay as per the accelaration value. + + * @param stepper pointer to stepper_config_t struct + + * @return int containing first pulse interval delay as per the accelaration value. + **/ +static int calc_first_delay_in_millis(stepper_config_t *stepper); + +/** + * @brief calculates delay for next step interval for accelaration. + + * @param stepper pointer to stepper_config_t struct + + * @param n count of the previous step that was executed. + + * @return int containing delay for next step interval for accelaration. + **/ +static int cal_delay_accl(stepper_config_t *stepper, int n); + +/** + * @brief calculates delay for next step interval for decelaration. + + * @param stepper pointer to stepper_config_t struct + + * @param n count of the previous step that was executed. + + * @return int containing delay for next step interval for decelaration. + **/ +static int cal_delay_decel(stepper_config_t *stepper, int n); + +/** + * @brief moves stepper to a specified position as per number of steps. + + * @param stepper pointer to stepper_config_t struct. + * @param pos_step step position to which stepper should be moved. + + * @return esp_err_t i.e it shows if stepper has moved successfully or not. + **/ +esp_err_t move_to_step(stepper_config_t *stepper, float pos_step); + +#endif \ No newline at end of file diff --git a/src/stepper.c b/src/stepper.c new file mode 100644 index 0000000..c3df21f --- /dev/null +++ b/src/stepper.c @@ -0,0 +1,158 @@ +/* + * MIT License + * + * Copyright (c) 2023 Society of Robotics and Automation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "stepper.h" +#include +static const char *TAG_STEPPER = "stepper"; + +static esp_timer_handle_t oneshot_timer; +int ramp_up = 0; +int delay; +long steps_to_move = 0; +static unsigned long n = 0; + +static void oneshot_timer_callback(void *arg); + +esp_err_t init_stepper(stepper_config_t *stepper) +{ + gpio_config_t io_conf = {}; + // Configure Stepper motor control pins as output + + // disable interrupt + io_conf.intr_type = GPIO_INTR_DISABLE; + // set as output mode + io_conf.mode = GPIO_MODE_OUTPUT; + // bit mask of the pins that you want to set,e.g.GPIO18/19 + io_conf.pin_bit_mask = ((1ULL << stepper->dir_pin) | (1ULL << stepper->step_pin)); + // disable pull-down mode + io_conf.pull_down_en = 0; + // disable pull-up mode + io_conf.pull_up_en = 0; + // configure GPIO with the given settings + esp_err_t err = gpio_config(&io_conf); + + gpio_set_level(stepper->dir_pin, 0); + gpio_set_level(stepper->step_pin, 0); + + if (err == ESP_OK) + { + ESP_LOGI(TAG_STEPPER, "Stepper driver Configured"); + return ESP_OK; + } + else + { + return ESP_FAIL; + } +} + +static int calc_first_delay_in_millis(stepper_config_t *stepper) +{ + float delay_in_sec = 0.676 * sqrtf((4 * M_PI / stepper->steps_per_revolution) / stepper->accl_decel); + int delay = (int)((2000 * delay_in_sec) + 0.5); + return delay; +} + +static int cal_delay_accl(stepper_config_t *stepper, int n) +{ + int cn = stepper->prev_delay - (2 * stepper->prev_delay) / (4 * n + 1); + return abs(cn); +} + +static int cal_delay_decel(stepper_config_t *stepper, int n) +{ + int cn = (stepper->prev_delay * (4 * n + 1)) / (4 * n - 1); + return abs(cn); +} + +esp_err_t move_to_step(stepper_config_t *stepper, float pos_step) +{ + + const esp_timer_create_args_t oneshot_timer_args = { + .callback = &oneshot_timer_callback, + /* argument specified here will be passed to timer callback function */ + .arg = (void *)stepper, + .name = "step", + }; + + ESP_ERROR_CHECK(esp_timer_create(&oneshot_timer_args, &oneshot_timer)); + delay = calc_first_delay_in_millis(stepper); + n = 0; + ramp_up = 0; + steps_to_move = pos_step - stepper->current_step_count; + stepper->prev_delay = delay; + stepper->dir = (steps_to_move < 0) ? -1 : 1; + stepper->movement_done = false; + gpio_set_level(stepper->dir_pin, steps_to_move < 0 ? 1 : 0); + while (1) + { + if (!stepper->movement_done && !esp_timer_is_active(oneshot_timer)) + { + ESP_ERROR_CHECK(esp_timer_start_once(oneshot_timer, delay)); + stepper->prev_delay = delay; + } + if (stepper->movement_done && !esp_timer_is_active(oneshot_timer)) + { + ESP_ERROR_CHECK(esp_timer_delete(oneshot_timer)); + break; + } + // Loop takes the minimum amout of delay possible + } + + return ESP_OK; +} + +static void oneshot_timer_callback(void *arg) +{ + stepper_config_t *stepper = (stepper_config_t *)arg; + if (n < abs(steps_to_move)) + { + gpio_set_level(stepper->step_pin, 1); + ets_delay_us(5); + gpio_set_level(stepper->step_pin, 0); + stepper->current_step_count += stepper->dir; + n++; + } + else + { + stepper->movement_done = true; + } + + if (ramp_up == 0) + { + delay = cal_delay_accl(stepper, n); + if (delay <= stepper->min_step_interval_us) + { + delay = stepper->min_step_interval_us; + ramp_up = n; + } + if (n >= abs(steps_to_move) / 2) + { + ramp_up = n; + } + } + else if (n >= abs(steps_to_move) - ramp_up) + { + delay = cal_delay_decel(stepper, abs(steps_to_move) - n); + } +} \ No newline at end of file diff --git a/u8g2 b/u8g2 new file mode 160000 index 0000000..d9402d5 --- /dev/null +++ b/u8g2 @@ -0,0 +1 @@ +Subproject commit d9402d5e40ca1224a300c39d9baef4051eefb7b5