diff --git a/CODE/include/README b/CODE/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/CODE/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/CODE/lib/README b/CODE/lib/README new file mode 100644 index 0000000..6debab1 --- /dev/null +++ b/CODE/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in a an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/CODE/platformio.ini b/CODE/platformio.ini new file mode 100644 index 0000000..bc66ac6 --- /dev/null +++ b/CODE/platformio.ini @@ -0,0 +1,30 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:PCB-v01a] +platform = espressif32 +board = featheresp32 +; change microcontroller to ESP32M4 +board_build.mcu = esp32 +; change MCU frequency +board_build.f_cpu = 240000000L +framework = arduino +;upload_protocol = esp-prog + +monitor_speed = 115200 +monitor_filters = esp32_exception_decoder +; debug_tool = esp-prog +; debug_init_break = tbreak setup +; build_type=debug +lib_deps = + https://github.com/someweisguy/esp_dmx + https://github.com/madhephaestus/ESP32Encoder + pkerspe/ESP-FlexyStepper @ ^1.4.3 + diff --git a/CODE/src/PIN_mapping.h b/CODE/src/PIN_mapping.h new file mode 100644 index 0000000..655222c --- /dev/null +++ b/CODE/src/PIN_mapping.h @@ -0,0 +1,32 @@ +/// ********************** PIN ASSIGNEMENTS ********************** +// DMX +const int tx_pin = 16; // define the IO pin the DMX TX pin is connected to +const int rx_pin = 17; // define the IO pin the DMX RX pin is connected to +const int rts_pin = 4; // define the IO pin the DMX RTS pin is connected to + +// capteur BCD pour adressage DMX +#define q1 21 // define the IO pin for the thumbwheel '1' is connected to +#define q2 19 // define the IO pin for the thumbwheel '2' is connected to +#define q4 18 // define the IO pin for the thumbwheel '4' is connected to +#define q8 5 // define the IO pin for the thumbwheel '8' is connected to + +// driver moteur +const int MOTOR_ENABLE_PIN = 27; // define the IO pin the motor enable pin is connected to +const int MOTOR_STEP_PIN = 26; // define the IO pin the motor step pin is connected to +const int MOTOR_DIRECTION_PIN = 25; // define the IO pin the motor direction pin is connected to +#ifndef dev_mode +const int EMERGENCY_STOP_PIN = 13; // define the IO pin the emergency stop switch is connected to +#endif +const int HOME_SWITCH_PIN = 35; // define the IO pin where the home switches are connected to (switches in series in normally closed setup against ground) +const int LIMIT_SWITCH_PIN = 34; // define the IO pin where the limit switches are connected to (switches in series in normally closed setup against ground) + +// encoder moteur pas à pas +#define EB_plus 22 // define the IO pin for the encoder B is connected to +#define EA_plus 23 // define the IO pin for the encoder A is connected to + +// potentiomètre pour le réglage des fins de course +#define home_set_pot 36 // define the IO pin for the home set potentiometer is connected to +#define limit_set_pot 39 // define the IO pin for the limit set potentiometer is connected to +#define divider_pot 1 // define the divider potentiometer value + + diff --git a/CODE/src/dmx_data.h b/CODE/src/dmx_data.h new file mode 100644 index 0000000..313b737 --- /dev/null +++ b/CODE/src/dmx_data.h @@ -0,0 +1,143 @@ +#define nb_dmx_channel 2 // nombre de canaux DMX utilisés +#define pos_array 0 // position de la position dans le tableau DMX_data +#define speed_array 1 // position de la vitesse dans le tableau DMX_data + +int DMX_start_channel; // numéro de la première adresse DMX +int pos_channel; // numéro de l'adresse DMX de la position +int speed_channel; // numéro de l'adresse DMX de la vitesse +const dmx_port_t dmx_num = DMX_NUM_2; // numéro du port DMX +byte data[DMX_PACKET_SIZE]; // tableau de données DMX + +bool dmxIsConnected = false; // état de la connexion DMX +unsigned long lastUpdate = millis(); // temps de la dernière mise à jour des données DMX +uint8_t DMX_data[nb_dmx_channel + 1]; // tableau de données DMX +uint8_t DMX_data_old[nb_dmx_channel + 1]; // tableau de données DMX précédentes +bool dataChanged[nb_dmx_channel + 1]; // tableau de drapeau pour donnée DMX ayant changées +unsigned long last_connected_Time = 0; // temps de la dernière connexion DMX +unsigned long last_disconnected_Time = 0; // temps de la dernière déconnexion DMX + +#define DMX_Connected_Time 1000 // temps en ms entre chaque vérification de la connexion DMX +#define DMX_Read_Time 200 // temps en ms entre chaque mise à jour des données DMX +#define dmx_debounce_try 0 // nombre de boucle pour le debounce DMX afin d'éviter les valeurs de 0 ou 255 erronées +int dmx_count[nb_dmx_channel + 1]; // compteur de boucle pour le debounce DMX afin d'éviter les valeurs de 0 ou 255 erronées + +// fonction +int readSwitch(); // fonction pour lire le codeur d'adresse DMX +void reset_dmx_counter(); // fonction pour réinitialiser le compteur de boucle pour le debounce DMX afin d'éviter les valeurs de 0 ou 255 erronées +void receiveDMX(); // fonction pour recevoir les données DMX + +int readSwitch() // fonction pour lire le codeur d'adresse DMX +{ + int total = 0; + if (digitalRead(q1) == HIGH) + { + total += 1; + } + if (digitalRead(q2) == HIGH) + { + total += 2; + } + if (digitalRead(q4) == HIGH) + { + total += 4; + } + if (digitalRead(q8) == HIGH) + { + total += 8; + } + total = ((total + 1) * 2) - 1; // correction battarde de l'adresse DMX + + return total; +} + +void reset_dmx_counter() // fonction pour réinitialiser le compteur de boucle pour le debounce DMX afin d'éviter les valeurs de 0 ou 255 erronées +{ + for (int i = 0; i < nb_dmx_channel + 1; i++) + { + dmx_count[i] = 0; + } +} + +void receiveDMX() // fonction pour recevoir les données DMX +{ + dmx_packet_t packet; + if (dmx_receive(dmx_num, &packet, DMX_TIMEOUT_TICK)) + { + + if (!packet.err) + { + if (!dmxIsConnected) + { + Serial.println("DMX is connected!"); + last_connected_Time = millis(); + dmxIsConnected = true; + } + dmx_read(dmx_num, data, packet.size); + // mise à jour des données DMX si elles ont changé et si le temps écoulé depuis la dernière mise à jour est supérieur à DMX_Read_Time + if (millis() - lastUpdate > DMX_Read_Time) + { + lastUpdate = millis(); // réinitialisation du temps de la dernière mise à jour des données DMX + last_connected_Time = millis(); // réinitialisation du temps de la dernière connexion DMX + // mise à jour de la position + if (DMX_data[pos_array] != data[pos_channel]) + { + if (DMX_data[pos_array] == 0 || DMX_data[pos_array] == 255) + { + dmx_count[pos_array]++; + if (dmx_count[pos_array] > dmx_debounce_try) + { + DMX_data[pos_array] = data[pos_channel]; + reset_dmx_counter(); + (dataChanged[pos_array]) = true; + } + } + else + { + DMX_data[pos_array] = data[pos_channel]; + reset_dmx_counter(); + (dataChanged[pos_array]) = true; + } + } + + // mise à jour de la vitesse + if (DMX_data[speed_array] != data[speed_channel]) + { + if (DMX_data[speed_array] == 0 || DMX_data[speed_array] == 255) + { + dmx_count[speed_array]++; + if (dmx_count[speed_array] > dmx_debounce_try) + { + DMX_data[speed_array] = data[speed_channel]; + reset_dmx_counter(); + (dataChanged[speed_array]) = true; + } + } + else + { + DMX_data[speed_array] = data[speed_channel]; + reset_dmx_counter(); + (dataChanged[speed_array]) = true; + } + } + } + } + else + { + reset_dmx_counter(); + Serial.println("A DMX error occurred."); + } + } + else if (dmxIsConnected) + { + reset_dmx_counter(); + // Serial.println("DMX was disconnected."); // affichage de la déconnexion DMX + if (millis() - last_connected_Time > DMX_Connected_Time) + { + dmxIsConnected = false; + last_disconnected_Time = millis(); // réinitialisation du temps de la dernière déconnexion DMX + Serial.println("DMX was halted."); // affichage de la déconnexion DMX + // emergencyStop = true; // arrêt du moteur en cas de déconnexion DMX + // emercency_stop_timer = millis(); // réinitialisation du timer d'arrêt d'urgence + } + } +} diff --git a/CODE/src/limit.h b/CODE/src/limit.h new file mode 100644 index 0000000..8d531bf --- /dev/null +++ b/CODE/src/limit.h @@ -0,0 +1,67 @@ + +// ********************** variables for software debouncing of the limit switches ********************** +#define switch_active 1 // active low switch configuration (NO connection with internal pull up) or HIGH for active high switch configuration (NC connection with internal pull up) + +volatile unsigned long lastDebounceTime = 0; // the last time the limit switch input pin was toggled +volatile unsigned long debounceDelay = 100; // the minimum delay in milliseconds to check for bouncing of the switch. Increase this slighlty if you switches tend to bounce a lot +bool buttonStateChangeDetected = false; // flag to indicate that the button state has changed +volatile byte limitSwitchState = !switch_active; // the current reading from the limit switch input pin +byte ConfirmedLimitSwitchState = !switch_active; // the last confirmed reading from the limit switch input pin +volatile byte homeSwitchState = !switch_active; // the current reading from the home switch input pin +byte ConfirmedHomeSwitchState = !switch_active; // the last confirmed reading from the home switch input pin + +volatile int home_steps; // nombre de pas pour bien positionner le moteur à la position de départ +volatile int limit_steps; // nombre de pas pour bien positionner le moteur à la position de fin + +void homeReachedCallback(); // callback function that will be called when the home position is reached +void limitReachedCallback(); // callback function that will be called when the limit position is reached +void IRAM_ATTR limitSwitchHandler(); // interrupt handler for the limit switch +void IRAM_ATTR homeSwitchHandler(); // interrupt handler for the home switch +void limit_sw_check(); // fonction pour la sécurité si une limite est détectée par le bouton poussoir +void home_sw_check(); // fonction pour la sécurité si une limite est détectée par le bouton poussoir + +void homeReachedCallback() // callback function that will be called when the home position is reached +{ + Serial.println("Home reached"); +} + +void limitReachedCallback() // callback function that will be called when the limit position is reached +{ + Serial.println("Limit reached"); +} + +void IRAM_ATTR limitSwitchHandler() // interrupt handler for the limit switch +{ + lastDebounceTime = millis(); + limitSwitchState = digitalRead(LIMIT_SWITCH_PIN); +} + +void IRAM_ATTR homeSwitchHandler() // interrupt handler for the home switch +{ + lastDebounceTime = millis(); + homeSwitchState = digitalRead(HOME_SWITCH_PIN); +} + +void limit_sw_check() // fonction pour la sécurité si une limite est détectée par le bouton poussoir +{ + + if (limitSwitchState != ConfirmedLimitSwitchState && (millis() - lastDebounceTime) > debounceDelay) + { + ConfirmedLimitSwitchState = limitSwitchState; + Serial.printf("Limit switch change detected. New state is %i\n", limitSwitchState); + Serial.printf("Limit setting is %i\n", limit_steps); + buttonStateChangeDetected = true; + } +} + +void home_sw_check() // fonction pour la sécurité si une limite est détectée par le bouton poussoir +{ + if (homeSwitchState != ConfirmedHomeSwitchState && (millis() - lastDebounceTime) > debounceDelay) + { + + ConfirmedHomeSwitchState = homeSwitchState; + Serial.printf("Home switch change detected. New state is %i\n", homeSwitchState); + Serial.printf("Home setting is %i\n", home_steps); + buttonStateChangeDetected = true; + } +} diff --git a/CODE/src/main.cpp b/CODE/src/main.cpp new file mode 100644 index 0000000..9048dbf --- /dev/null +++ b/CODE/src/main.cpp @@ -0,0 +1,169 @@ + +/* +controle de moteur pas à pas selon commande DMX + +code par Clément SAILLANT pour Hémisphère - 2023 +c.saillant@gmail.com +0625334420 + +Un canal DMX est utilisé pour la commande de la position du moteur +Un autre canal DMX est utilisé pour la commande de la vitesse du moteur +Une fonction affine est utilisée pour convertir la valeur DMX en position ou vitesse +La position et la vitesse sont stockées dans des variables globales +Une fonction est utilisée pour la sécurité si un obstacle est détecté par la perte de pas du moteur + +moteur : 34HS59-6004D-E1000 +Angle de pas: 1.8 deg +Résolution de l'encodeur: 1000PPR(4000CPR) +https://www.omc-stepperonline.com/download/34HS59-6004D-E1000_Torque_Curve.pdf +https://www.omc-stepperonline.com/download/34HS59-6004D-E1000.pdf + +Controleur CL86T V4.0 +https://www.omc-stepperonline.com/index.php?route=product/product/get_file&file=389/CL86T%20(V4.0).pdf +*/ + +#include "PIN_mapping.h" // fichier contenant les variables globales et le mapping des pins +bool emergencyStop = false; // variable pour stocker l'état de l'arrêt d'urgence + +#include +#include +#include // librairie pour le controle des moteurs pas à pas +#include // librairie pour le controle des encodeurs + +ESP_FlexyStepper stepper; // création de l'ojet motor +ESP32Encoder encoder; // création de l'objet encoder + +#include "limit.h" // fichier contenant les fonctions de limites +#include "dmx_data.h" // fichier contenant les fonctions dmx +#include "moving.h" // fichier contenant les fonctions de mouvement +#include "security.h" // fichier contenant les fonctions de sécurité type emergency stop + +void setup() +{ + // delay(1000); + Serial.begin(115200); + Serial.println("started"); + + dmx_set_pin(dmx_num, tx_pin, rx_pin, rts_pin); + dmx_driver_install(dmx_num, DMX_DEFAULT_INTR_FLAGS); + + // configuration encoder pour suivre la position du moteur et la perte de pas + encoder.attachHalfQuad(EB_plus, EA_plus); + encoder.setCount(0); + + // configuration ENABLE motor + pinMode(MOTOR_ENABLE_PIN, OUTPUT); + + // configuration BCD Coder + pinMode(q1, INPUT); // thumbwheel '1' + pinMode(q2, INPUT); // thumbwheel '2' + pinMode(q4, INPUT); // thumbwheel '4' + pinMode(q8, INPUT); // thumbwheel '8' + + /* + // attach an interrupt to the IO pin of the ermegency stop switch and specify the handler function + pinMode(EMERGENCY_STOP_PIN, INPUT_PULLUP); + attachInterrupt(digitalPinToInterrupt(EMERGENCY_STOP_PIN), emergencySwitchHandler, RISING); + */ + + // lecture des potentiomètres pour définir les offset de pas a ajouter aux positions home et limit + limit_steps = map((analogRead(limit_set_pot)), 4095, 0, min_offset, max_offset); + home_steps = map((analogRead(home_set_pot)), 4095, 0, min_offset, max_offset); + + // attach an interrupt to the IO pin of the home switch and specify the handler function + attachInterrupt(digitalPinToInterrupt(HOME_SWITCH_PIN), homeSwitchHandler, CHANGE); + stepper.registerHomeReachedCallback(homeReachedCallback); + + // attach an interrupt to the IO pin of the limit switch and specify the handler function + attachInterrupt(digitalPinToInterrupt(LIMIT_SWITCH_PIN), limitSwitchHandler, CHANGE); + stepper.registerLimitReachedCallback(limitReachedCallback); + + // connect and configure the stepper motor to its IO pins + stepper.connectToPins(MOTOR_STEP_PIN, MOTOR_DIRECTION_PIN); + // set the speed and acceleration rates for the stepper motor + stepper.setSpeedInStepsPerSecond(SPEED_IN_STEPS_PER_SECOND); + stepper.setAccelerationInStepsPerSecondPerSecond(ACCELERATION_IN_STEPS_PER_SECOND); + stepper.setDecelerationInStepsPerSecondPerSecond(DECELERATION_IN_STEPS_PER_SECOND); + stepper.setStepsPerRevolution(STEP_PER_REV); + stepper.registerTargetPositionReachedCallback(targetPositionReachedCallback); + + // start the stepper instance as a service in the "background" as a separate task + stepper.startAsService(1); // core 1 sinon bug avec le dmx et déplacement aléatoire du moteur + + init_range(); // set home and limit position of the motor + + DMX_start_channel = readSwitch(); // lecture de la position du codeurs BCD pour définir le canal DMX de départ + // DMX_start_channel = 2; // pour test avec adresse fixe + pos_channel = DMX_start_channel; + speed_channel = DMX_start_channel + 1; + + Serial.println("----------------------------------------"); + Serial.printf("number of tasks is %i\n", uxTaskGetNumberOfTasks()); + Serial.printf("home position is %i\n", min_steps); + Serial.printf("limit position is %i\n", max_steps); + Serial.printf("Limit setting is %i\n", limit_steps); + Serial.printf("Home setting is %i\n", home_steps); + Serial.printf("DMX start channel is %i\n", DMX_start_channel); + Serial.printf("DMX position channel is %i\n", pos_channel); + Serial.printf("DMX speed channel is %i\n", speed_channel); + Serial.println("----------------------------------------"); +} + +void loop() +{ + receiveDMX(); // reception des données DMX + + // mise à jour de la vitesse du moteur si la valeur DMX a changée + if (dataChanged[speed_array] == true) + { + dataChanged[speed_array] = false; + speed_map(); // fonction pour convertir la valeur DMX en vitesse/acceleration/deceleration + } + + // mise à jour de la position du moteur si la valeur DMX a changée + // if (dataChanged[pos_array] == true && emergencyStop == false && digitalRead(MOTOR_ENABLE_PIN) == LOW) + if (dataChanged[pos_array] == true) + { + // stepper.setTargetPositionToStop(); + dataChanged[pos_array] = false; + pos_in_step = map(DMX_data[pos_array], 255, 0, min_steps, max_steps); + // pos_map(); // fonction pour convertir la valeur DMX en position + stepper.setTargetPositionInSteps(pos_in_step); + DMX_data_old[pos_array] = DMX_data[pos_array]; + last_dmx_change_pos = millis(); + Serial.printf("dmx pos to step => %ld\n", pos_in_step); + } + // gestion de la position du moteur si la valeur DMX n'a pas changée mais que le moteur n'est pas à la position demandée + if (millis() - last_dmx_change_pos > 1000) + { + int codeur_pos = map(encoder.getCount(), min_count, max_count, min_steps, max_steps); + if (pos_in_step <= min_steps && ConfirmedHomeSwitchState != switch_active && stepper.getDirectionOfMotion() == 0) + { + Serial.println("!!!!!!!!!!!!! home not reached"); + Serial.printf("codeur pos => %i\n", codeur_pos); + // stepper.setTargetPositionInSteps(min_steps - codeur_pos); // home_steps + int step_add = nb_microstep * 2; + stepper.setTargetPositionRelativeInSteps(-step_add); + delay(100); + } + else if (pos_in_step >= max_steps && ConfirmedLimitSwitchState != switch_active && stepper.getDirectionOfMotion() == 0) + { + Serial.println("!!!!!!!!!!!!! limit not reached"); + Serial.printf("codeur pos => %i\n", codeur_pos); + // stepper.setTargetPositionInSteps(max_steps + (max_steps - codeur_pos)); // limit_steps + int step_add = nb_microstep * 2; + stepper.setTargetPositionRelativeInSteps(step_add); + delay(100); + } + else if (abs(codeur_pos - pos_in_step) > 100 && stepper.getDirectionOfMotion() == 0) + { + Serial.println("!!!!!!!!!!!!! Not in position"); + Serial.printf("codeur pos => %i\n", codeur_pos); + Serial.printf("pos in step => %i\n", pos_in_step); + stepper.setTargetPositionInSteps(pos_in_step); + } + } + // motor_follower(emergency_stop_loss_step); // fonction pour suivre la position du moteur et la perte de pas + emergency_check(); // fonction pour la sécurité si un obstacle est détecté par la perte de pas du moteur + limit_check(); // fonction pour la sécurité si un obstacle est détecté par le bouton poussoir +} \ No newline at end of file diff --git a/CODE/src/moving.h b/CODE/src/moving.h new file mode 100644 index 0000000..2d71f90 --- /dev/null +++ b/CODE/src/moving.h @@ -0,0 +1,165 @@ + +#define nb_microstep 8 // nombre de microstep configuré sur le driver moteur +#define STEP_PER_REV_MOTOR 200 // nombre de pas par tour du moteur +#define CPR_ENCODER 4000 // nombre de pas par tour du codeur optique +/* +Nb SW1 SW2 SW3 SW4 +1 200 on on on on +4 800 off on on on +8 1600 on off on on +16 3200 off off on on +32 6400 on on off on +64 12800 off on off on +128 25600 on off off on +256 51200 off off off on +5 1000 on on on off +10 2000 off on on off +20 4000 on off on off +25 5000 off off on off +40 8000 on on off off +50 10000 off on off off +100 20000 on off off off +200 40000 off off off off +*/ +#define SECOND_PER_REV 2 // nombre de seconde pour faire un tour +const int STEP_PER_REV = nb_microstep * STEP_PER_REV_MOTOR; // nombre de pas par tour du moteur +const int MIN_SPEED = 1; // vitesse minimum du moteur +const int MAX_SPEED = ((STEP_PER_REV / 2) / SECOND_PER_REV) * nb_microstep; // vitesse maximum du moteur (1/2 révolution par SECOND_PER_REV seconde) +//const int MAX_SPEED = ((STEP_PER_REV / 2) / SECOND_PER_REV) * nb_microstep; // vitesse maximum du moteur (1/2 révolution par SECOND_PER_REV seconde) + +const int SPEED_IN_STEPS_PER_SECOND = STEP_PER_REV; // vitesse initiale de déplacement du moteur = 1/2 de la vitesse maximum +const int ACCELERATION_IN_STEPS_PER_SECOND = nb_microstep * 2; // accélération initiale du moteur +const int DECELERATION_IN_STEPS_PER_SECOND = nb_microstep; // décélération initiale du moteur +int ACC_DEC_REMOVE_STEP = ACCELERATION_IN_STEPS_PER_SECOND + DECELERATION_IN_STEPS_PER_SECOND; // nombre de pas à enlever à la position pour l'accélération et la décélération ? +int min_steps = 0; // position de départ du moteur +int max_steps = STEP_PER_REV / 2; // position de fin du moteur (1600 en 16 microstep) (400 en 1 microstep) +int pos_in_step = max_steps / 2; // position du moteur en pas pour le mappage de la position + +int min_count = 0; // valeur minimum du compteur du codeur optique +int max_count = CPR_ENCODER / 4; // valeur maximum du compteur du codeur optique avec uniquement les fronts montants du codeur +const int min_offset = 0; // min offset pour le potentiomètre de réglage de la position min/max +const int max_offset = nb_microstep * 5; // max offset pour le potentiomètre de réglage de la position min/max + +volatile unsigned long last_dmx_change_pos; + +volatile int speed_in_accel = MAX_SPEED / 10; // variable pour la vitesse d'acceleration +volatile int speed_in_decel = DECELERATION_IN_STEPS_PER_SECOND; // variable pour la vitesse de deceleration + +// fonction +void targetPositionReachedCallback(long position); // callback function as the target position is reached +void speed_map(); // fonction de mapping de la vitesse du moteur avec accélération et décélération +void pos_map(); // fonction de mapping de la position du moteur +void set_min(); // fonction pour position minimum de la plage de déplacement du moteur +void set_max(); // fonction pour position maximum de la plage de déplacement du moteurF +void init_range(); // fonction d'initialisation de la plage de déplacement du moteur + +void targetPositionReachedCallback(long position) // callback function as the target position is reached +{ + Serial.printf("Stepper reached target position %ld\n", position); + Serial.printf("Encoder map count is %ld\n", map(encoder.getCount(), min_count, max_count, min_steps, max_steps)); + Serial.printf("Encoder count is %ld\n", encoder.getCount()); +} + +void speed_map() // fonction de mapping de la vitesse du moteur avec accélération et décélération +{ + int speed_in_step = map(DMX_data[speed_array], 0, 255, MIN_SPEED, MAX_SPEED); + stepper.setSpeedInStepsPerSecond(speed_in_step); + Serial.printf("dmx speed receive => %ld\n", DMX_data[speed_array]); + Serial.printf("speed in step => %ld\n", speed_in_step); +} + +void pos_map() // fonction de mapping de la position du moteur +{ + //int pos_in_step = map(DMX_data[pos_array], 255, 0, min_steps, max_steps); + //int pos_in_step = map(DMX_data[pos_array], 0, 255, min_steps - home_steps, max_steps + limit_steps); // fait dans la fonction limit_check + //stepper.setTargetPositionInSteps(pos_in_step); + Serial.printf("dmx pos to step => %ld\n", pos_in_step); +} + +void set_min() // fonction pour position minimum de la plage de déplacement du moteur +{ + home_steps = map((analogRead(home_set_pot)), 4095, 0, min_offset, max_offset); + stepper.setCurrentPositionAsHomeAndStop(); // set the current position as the home position and stop the stepper + // min_steps = stepper.getCurrentPositionInSteps() - home_steps; // position min du moteur en pas + min_steps = stepper.getCurrentPositionInSteps(); + encoder.setCount(0); // remise à zéro de l'encodeur + min_count = encoder.getCount(); // valeur minimum du compteur du codeur en position min +} + +void set_max() // fonction pour position maximum de la plage de déplacement du moteur +{ + limit_steps = map((analogRead(limit_set_pot)), 4095, 0, min_offset, max_offset); + // max_steps = stepper.getCurrentPositionInSteps() + limit_steps; // position max du moteur en pas + max_steps = stepper.getCurrentPositionInSteps(); + max_count = encoder.getCount(); // valeur maximum du compteur du codeur en position max +} + +void init_range() // fonction d'initialisation de la plage de déplacement du moteur +{ + int direction = -1; + bool limit_OK = false; + int position_init = max_steps / 2; + Serial.println("starting homing"); + digitalWrite(MOTOR_ENABLE_PIN, LOW); // enable motor + stepper.setDirectionToHome(direction); + if (digitalRead(HOME_SWITCH_PIN) == switch_active) // si position est déjà à home + { + Serial.println("already in home position"); + stepper.setCurrentPositionAsHomeAndStop(); // set the current position as the home position and stop the stepper + stepper.setTargetPositionInSteps(position_init); + delay(2000); + } + stepper.goToLimitAndSetAsHome(); // go to the limit switch and set the current position as the home position + + while (!limit_OK) + { + home_sw_check(); + if (ConfirmedHomeSwitchState == switch_active) + { + stepper.setLimitSwitchActive(stepper.LIMIT_SWITCH_BEGIN); // this will cause to stop any motion that is currently going on and block further movement in the same direction as long as the switch is active + stepper.setTargetPositionToStop(); + limit_OK = true; + } + else + { + stepper.clearLimitSwitchActive(); // clear the limit switch flag to allow movement in both directions again + } + } + set_min(); + Serial.println("home OK"); + stepper.clearLimitSwitchActive(); // clear the limit switch flag to allow movement in both directions again + limit_OK = false; + Serial.println("waiting for limit switch"); + stepper.setTargetPositionInSteps(position_init); // set the target position to the middle of the range + while (!limit_OK) + { + limit_sw_check(); + if (ConfirmedLimitSwitchState == switch_active) + { + stepper.setLimitSwitchActive(stepper.LIMIT_SWITCH_END); // this will cause to stop any motion that is currently going on and block further movement in the same direction as long as the switch is active + stepper.setTargetPositionToStop(); + limit_OK = true; + } + else + { + stepper.clearLimitSwitchActive(); // clear the limit switch flag to allow movement in both directions again + if (stepper.getCurrentPositionInSteps() < (max_steps - (max_steps / 4))) + { + position_init = stepper.getCurrentPositionInSteps() + (max_steps / 8); + } + else + { + position_init = stepper.getCurrentPositionInSteps() + nb_microstep; + } + stepper.setTargetPositionInSteps(position_init); + } + } + Serial.println("limit init OK"); + stepper.setTargetPositionToStop(); + position_init = max_steps / 2; + stepper.clearLimitSwitchActive(); // clear the limit switch flag to allow movement in both directions again + set_max(); + stepper.setTargetPositionInSteps(position_init); // set the target position to the middle of the range + delay(500); + buttonStateChangeDetected = false; // remise à zéro du flag de changement d'état des switchs +} \ No newline at end of file diff --git a/CODE/src/notepad.txt b/CODE/src/notepad.txt new file mode 100644 index 0000000..3914abe --- /dev/null +++ b/CODE/src/notepad.txt @@ -0,0 +1,8 @@ +https://github.com/arttupii/CLSMB Closed loop step motor controller + +https://www.switchdoc.com/2018/04/esp32-tutorial-debouncing-a-button-press-using-interrupts/ +avec vtask pour plus d'efficacité + +voir pour mieur gérer le homming avec les fonctions explicité dans la lib felxystepper + +rajouter plus de délai à la lecture dmx pour mieux éviter les problème de chanement de valeurs trop rapide \ No newline at end of file diff --git a/CODE/src/security.h b/CODE/src/security.h new file mode 100644 index 0000000..58f4d07 --- /dev/null +++ b/CODE/src/security.h @@ -0,0 +1,167 @@ + +#define emergency_stop_bounce_time 1000 // time in ms to wait before disabling the motor in emergency when step as been lost +#define emergency_release_time 2000 // time in ms to wait before releasing the emergency stop +#define emergency_stop_loss_step 5 // number of step to wait before disabling the motor in emergency when step as been lost +unsigned long emercency_stop_timer; // temps en ms depuis lequel le moteur est en arrêt d'urgence +unsigned long lastStepTime; // variable pour stocker le temps de la dernière mise à jour des données encoder +long old_count; // variable des précedentes données de l'encoder +long newPosition; // variable des données de l'encoder + +// fonction : +void motor_follower(int loss_step); // fonction pour suivre la position du moteur et la perte de pas +void emergency_check(); // fonction pour la sécurité si un obstacle est détecté par la perte de pas du moteur +void limit_check(); // fonction pour la sécurité si une limite est détectée par le bouton poussoir + +/* +void IRAM_ATTR emergencySwitchHandler() // interrupt handler for the emergency stop switch +{ + + // we do not realy need to debounce here, since we only want to trigger a stop, no matter what. + // So even triggering mutliple times does not realy matter at the end + if (digitalRead(EMERGENCY_STOP_PIN) == LOW) // Switch is configured in active low configuration + { + // the boolean true in the following command tells the stepper to hold the emergency stop until reaseEmergencyStop() is called explicitly. + // If ommitted or "false" is given, the function call would only stop the current motion and then instanlty would allow for new motion commands to be accepted + stepper.emergencyStop(true); + } + else + { + // release a previously enganed emergency stop when the emergency stop button is released + stepper.releaseEmergencyStop(); + } + +} + */ + +void motor_follower(int loss_step) // fonction pour suivre la position du moteur et la perte de pas +{ + // Quand le moteur est en mouvement, on vérifie si la position a changé de plus de 5 pas + // mise à jour de la position de l'encodeur + newPosition = encoder.getCount(); + if (stepper.getDirectionOfMotion() != 0) + { + if (newPosition > old_count || newPosition < old_count) + { + lastStepTime = millis(); + emergencyStop = false; + } + if (newPosition >= old_count + loss_step || newPosition <= old_count - loss_step) + { + old_count = newPosition; + } + else if (millis() - lastStepTime > emergency_stop_bounce_time / (DMX_data[speed_array] + 1) && emergencyStop == false) + { + Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!! emergency stop"); + stepper.setTargetPositionToStop(); + emercency_stop_timer = millis(); + emergencyStop = true; + } + } +} + +void emergency_check() // fonction pour la sécurité si un obstacle est détecté par la perte de pas du moteur +{ + if (emergencyStop) + { + Serial.printf("emergency stop timer : %i\n", millis() - emercency_stop_timer); + if (millis() - emercency_stop_timer > emergency_release_time && emergencyStop == true) + { + digitalWrite(MOTOR_ENABLE_PIN, LOW); // enable motor + Serial.println("======================> emergency stop released"); + lastStepTime = millis(); + emergencyStop = false; + last_dmx_change_pos = millis(); + // stepper.setTargetPositionInSteps(pos_in_step); + } + else if (digitalRead(MOTOR_ENABLE_PIN) == LOW && emergencyStop == true) + { + Serial.println("======================> emergency stop engaged"); + digitalWrite(MOTOR_ENABLE_PIN, HIGH); // disable and free motor + } + } + else if (digitalRead(MOTOR_ENABLE_PIN) == HIGH && emergencyStop == false) + { + digitalWrite(MOTOR_ENABLE_PIN, LOW); // enable motor + Serial.println("======================> emergency HARD stop released"); + lastStepTime = millis(); + // last_dmx_change_pos = millis(); + // stepper.setTargetPositionInSteps(pos_in_step); + } +} + +void limit_check() // fonction pour la sécurité si une limite est détectée par le bouton poussoir +{ + const int limit_step_lost = nb_microstep; // nombre de pas à perdre avant de déclencher l'arrêt d'urgence en cas de détection de limite + limit_sw_check(); // check limit switch + home_sw_check(); // check home switch + + // si aucun bouton n'est appuyé + if (ConfirmedHomeSwitchState != switch_active && ConfirmedLimitSwitchState != switch_active && buttonStateChangeDetected == true) + { + buttonStateChangeDetected = false; + stepper.clearLimitSwitchActive(); // clear the limit switch flag to allow movement in both directions again + return; + } + + // si les deux boutons sont appuyés il y a un problème + if (ConfirmedHomeSwitchState == switch_active && ConfirmedLimitSwitchState == switch_active) + { + Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ERROOOOORRRRRREEE !!!!!!!!"); + stepper.emergencyStop(true); + emergencyStop = true; + } + + // si le bouton de début de course est appuyé + if (ConfirmedHomeSwitchState == switch_active) + { + home_steps = map((analogRead(home_set_pot)), 4095, 0, min_offset, max_offset); // mise à jour de la valeur de l'offset de la position de départ + // motor_follower(limit_step_lost); + if (emergencyStop) + { + stepper.setLimitSwitchActive(stepper.LIMIT_SWITCH_BEGIN); // this will cause to stop any motion that is currently going on and block further movement in the same direction as long as the switch is agtive + stepper.setCurrentPositionAsHomeAndStop(); + } + // si le bouton vient d'être appuyé on va à la position de départ selon la config d'offset par le potentiomètre + else if (buttonStateChangeDetected == true) + { + Serial.printf("===========> Home <===========\n"); + buttonStateChangeDetected = false; + home_steps = map((analogRead(home_set_pot)), 4095, 0, min_offset, max_offset); + // set_min(); // on enregistre la position actuelle comme position de départ + min_count = encoder.getCount(); // valeur minimum du compteur du codeur en position min + stepper.setCurrentPositionAsHomeAndStop(); // set the current position as the home position and stop the stepper + stepper.setCurrentPositionInSteps(min_steps); // mise à jour de la position pour compenser un décalage éventuel + stepper.setTargetPositionInSteps(min_steps - home_steps); // on va à la position de départ selon la config d'offset par le potentiomètre + Serial.printf("go to Home set : %i\n", min_steps - home_steps); + Serial.printf("min_steps : %i\n", min_steps); + Serial.printf("add config step : %i\n", home_steps); + Serial.printf("pos_in_step : %i\n", pos_in_step); + } + } + + // si le bouton de fin de course est appuyé + if (ConfirmedLimitSwitchState == switch_active) + { + limit_steps = map((analogRead(limit_set_pot)), 4095, 0, min_offset, max_offset); // mise à jour de la valeur de l'offset de la position de fin + // motor_follower(limit_step_lost); + if (emergencyStop) + { + stepper.setLimitSwitchActive(stepper.LIMIT_SWITCH_END); // this will cause to stop any motion that is currently going on and block further movement in the same direction as long as the switch is agtive + } + // si le bouton vient d'être appuyé on va à la position de fin selon la config d'offset par le potentiomètre + else if (buttonStateChangeDetected == true) + { + Serial.printf("===========> Limit <===========\n"); + buttonStateChangeDetected = false; + limit_steps = map((analogRead(limit_set_pot)), 4095, 0, min_offset, max_offset); + // set_max(); // on enregistre la position actuelle comme position de fin + max_count = encoder.getCount(); // valeur maximum du compteur du codeur en position max + stepper.setCurrentPositionInSteps(max_steps); // mise à jour de la position pour compenser un décalage éventuel + stepper.setTargetPositionInSteps(max_steps); // mise à jour de la position pour compenser un décalage éventuel + stepper.setTargetPositionInSteps(max_steps + limit_steps); // on va à la position de fin selon la config d'offset par le potentiomètre + Serial.printf("go to Limit set : %i\n", max_steps + limit_steps); + Serial.printf("max_steps : %i\n", max_steps); + Serial.printf("add config step : %i\n", limit_steps); + } + } +} diff --git a/CODE/test/README b/CODE/test/README new file mode 100644 index 0000000..9b1e87b --- /dev/null +++ b/CODE/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Test Runner and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html