[SOLUCIONADO] millis() para varios motores servo y stepper

IgnoranteAbsoluto: estuve estudiando principios y codificación de la máquina de estados y luego pude entender en cómo funciona la idea de la reprogramación que me enviaste del funicular.

Utilicé otro motor y el driver L298N por lo que cambié la secuencia a AccelStepper stepper = AccelStepper(4, MOTOR_PIN_1, MOTOR_PIN_3, MOTOR_PIN_2, MOTOR_PIN_4); y funcionó sin inconvenientes.

Por el momento pienso continuar en forma sencilla, agregando sólo un cambio/desvío de vía con un servo.

Muchas gracias nuevamente por tu ayuda.

Armando

Disculpas por la errata.
Debe decir:

AccelStepper stepper = AccelStepper(4, MOTOR_PIN_1, MOTOR_PIN_2, MOTOR_PIN_3, MOTOR_PIN_4);

Lo siento.
Armando

Buenos días.
Implementé el movimiento del primer servo utilizando millis() tal como me sugirió y orientó Ignorante_Absoluto. Antes de combinar este sketch con el del funicular solicito por favor que me orienten acerca de cómo disminuir la velocidad del movimiento del servo sin usar delay(), for o while. Este es el código con el que muevo un microservo SG90.

//Desvío simple con millis() 4/5/2020
//Servo con dos botones y LEDs. Resistencias Pull Up.

#include <Servo.h>

Servo servoUno;                              //motor en PIN ~3

//Botones
const uint8_t PIN_DESVIO_UNO = 4;            // Botón para desvío, mueve a izquierda
const uint8_t PIN_RECTA_UNO = 5;             // Botón para vía recta, mueve a derecha (0°)
const uint8_t VALOR_BOTON_DETECTADO = LOW;   //Valor que deberá tener el pin cuando se detecta el botón

//Para arranque
int gradosUno = 0;                           // Inicializa en la posición 0° vía recta

//LEDs
const uint8_t VERDE_RECTA_UNO = 6;           // Led de vía recta
const uint8_t AZUL_DESVIO_UNO = 7;           // LED de desvío

enum estado_t {
    ESTADO_INICIAL_UNO,                       
    ESTADO_DESVIANDO_UNO,
    ESTADO_DESVIO_UNO,
    ESTADO_RECTIFICANDO_UNO,
    ESTADO_RECTA_UNO,
}
estado = ESTADO_INICIAL_UNO;
const unsigned long TIEMPO_ESPERA_DESVIO = 500UL;
const unsigned long TIEMPO_ESPERA_UNO = 500UL;
unsigned long instanteAnterior = TIEMPO_ESPERA_UNO;

void setup() {
  pinMode(PIN_DESVIO_UNO, INPUT_PULLUP);     // resistencia PULL UP       
  pinMode(PIN_RECTA_UNO, INPUT_PULLUP);
  unsigned long instanteAnterior = TIEMPO_ESPERA_UNO;
  pinMode(PIN_RECTA_UNO, INPUT);             //Configura pines de entrada para botones
  pinMode(PIN_DESVIO_UNO, INPUT);
  pinMode(VERDE_RECTA_UNO, OUTPUT);          //Configura pines de salida para LEDs
  pinMode(AZUL_DESVIO_UNO, OUTPUT);
  servoUno.attach(3,750,1800);               //Configura el Servo, 750 ms = 0° y 1800 ms = 180°
  servoUno.write(gradosUno);                 //Al arrancar mueve el motor hasta 0°, vía recta
  digitalWrite(VERDE_RECTA_UNO, HIGH);       //Enciende LED verde, vía recta
}

void loop() {
  unsigned long instanteActual = millis();

  switch (estado) {
        case ESTADO_INICIAL_UNO:             // Encendido Arduino, vía recta 0°. 
            estado = ESTADO_RECTA_UNO;  
          
        case ESTADO_DESVIANDO_UNO:           //Apretado el botón DESVIO_UNO
          if (digitalRead(PIN_RECTA_UNO) == VALOR_BOTON_DETECTADO) {
            digitalWrite (AZUL_DESVIO_UNO,HIGH);
            digitalWrite (VERDE_RECTA_UNO,LOW);
            instanteAnterior = instanteActual;
            servoUno.write(45);              //Mueve a izquierda
            estado = ESTADO_DESVIO_UNO;
            }
          else {
            estado = ESTADO_DESVIO_UNO;
            }
          break;
            
        case ESTADO_DESVIO_UNO:
          if ((instanteActual - instanteAnterior) >= TIEMPO_ESPERA_UNO) {
            estado = ESTADO_RECTIFICANDO_UNO;
            }
          break;

        case ESTADO_RECTIFICANDO_UNO:       //Apretado el botón RECTA_UNO
          if (digitalRead(PIN_DESVIO_UNO) == VALOR_BOTON_DETECTADO) {
            digitalWrite (VERDE_RECTA_UNO,HIGH);
            digitalWrite (AZUL_DESVIO_UNO,LOW);
            instanteAnterior = instanteActual;
            estado = ESTADO_RECTA_UNO;
            servoUno.write(0);              //Mueve a 0°
            }
          else {
            estado = ESTADO_RECTA_UNO;
            }
        break;
        
        case ESTADO_RECTA_UNO:
          if ((instanteActual - instanteAnterior) >= TIEMPO_ESPERA_UNO) {
            estado = ESTADO_DESVIANDO_UNO;
          }
        break;
    }
  }

Serán bienvenidos correcciones/agregados/cambios que haya que hacer en el resto del sketch. Desde ya muchas gracias.
Armando

¿Qué significa "disminuir la velocidad del movimiento del servo"? ¿Significa que el servo se mueva de una posición a otra más lento de lo que lo hace "normalmente"? Si es así, ¿por qué? ¿Por estética (para que luzca más "bonito"), o por cuestiones mecánicas?

En principio se podría controlar que el servo se moviese de un punto a otro más despacio, salvo al inicializarse el Arduino porque el problema que tiene el servo es que se queda en la última posición en que se puso. Al reiniciar el Arduino (bien por reset o por apagado y encendido), no se sabe en qué posición está. Con lo que al empezar el programa se suele poner en una posición prefijada. Si no estaba en esa posición, se posicionará a "su velocidad normal".

“El truco" para que se mueva lentamente es ir posicionando el servo en grados intermedios poco a poco, a lo largo del tiempo, hasta que esté en la posición final deseada.

Viendo el código, a simple vista, no entiendo bien lo que se pretende hacer. Vendría bien una descripción de cómo se quiere que funcione y lo que ha de hacer. Lo que más me despista es el “tiempo de espera”. ¿Esperar qué?

Muchas gracias por tu pronta respuesta.

El servo mueve un cambio de vías en un tren con escala HO (1:87), el objetivo de hacer el movimiento más lento es para simular los cambios de vía en la realidad. De la manera que funciona ahora el movimiento (de alrededor de treinta grados) es, a mi entender, demasiado rápido.

Cuando empleaba delay(), previamente a que me indicaras los pasos a seguir para trabajar con millis(), utilizaba un for:

for (int i = 50; i > 0; i--){  //Para mover a baja velocidad
        servoUno.write(i);     //Mueve a derecha grado a grado
        delay(25);
        }

La posición prefijada es la vía recta, sin habilitar el desvío, por lo que alineo el eje a cero grados y lo adjudico a vía recta, salvo que en algún sector el default sea con desvío, con lo que colocaré en ese caso el eje del servo correspondiente en alrededor de treinta grados.

Como el objetivo avanzado es integrar en el sketch otros dos cambios de vía, el funicular y un ascensor (en un Arduino Mega) debo evitar interrupciones. ¿Cómo debo programar con millis() el avance en grados intermedios?

El "tiempo de espera" seguramente es un despiste mío :wink: Si lo debo quitar, ¿cómo queda el if?

Un cordial saludo.

Armando

Disculpa la tardanza en contestar. El trabajo me ha tenido prisionero. Sobre lo que tenías hecho del control de la aguja, he hecho unas cuantas modificaciones para que funcionara. En los comentarios del código explico algunos detalles.

//Desvío simple con millis() 4/5/2020
//Servo con dos botones y LEDs. Resistencias Pull Up.

#include <Servo.h>

Servo servoUno;                              //motor en PIN ~3

//Botones
const uint8_t PIN_DESVIO_UNO = 4;            // Botón para desvío, mueve a izquierda
const uint8_t PIN_RECTA_UNO = 5;             // Botón para vía recta, mueve a derecha (0°)
const uint8_t VALOR_BOTON_DETECTADO = LOW;   //Valor que deberá tener el pin cuando se detecta el botón

//Para arranque
int gradosUno = 0;                           // Inicializa en la posición 0° vía recta

//LEDs
const uint8_t VERDE_RECTA_UNO = 6;           // Led de vía recta
const uint8_t AZUL_DESVIO_UNO = 7;           // LED de desvío

enum estado_t {             // Estos son los estados de cualquier aguja, no importa qué aguja (no hace falta el _UNO)
    ESTADO_INICIAL,         // Para cuando se encienda el Arduino se posicione recta
    ESTADO_DESVIANDO,       // Cuando está cambiando de recta a desvío
    ESTADO_DESVIO,          // Cuando está en modo desvío
    ESTADO_RECTIFICANDO,    // Cuando está cambiando de desvío a recta
    ESTADO_RECTA,           // Cuando está en recta
} estado = ESTADO_INICIAL;  // Inicialmente tenemos que posicionarla recta, sí o sí

const unsigned long TIEMPO_DURACION_CAMBIO = 1500UL; // Este es el tiempo total que queremos que tarde la aguja en cambiar de posición
unsigned long instanteAnterior = 0;                 // En principio lo inicializamos a cero, sólo se usa cuando se está moviendo la aguja
unsigned long tiempoUnGrado = 0;                    // Tiempo que ha de pasar entre grado y grado de movimiento de la aguja
int anguloActual = 0;               // Ángulo en que se encuentra actualmente la aguja. Inicialmente lo pondremos a cero
int anguloDestino = 0;              // Ángulo en que queremoa que se posicione la aguja. Inicialmente lo pondremos a cero

void setup() {
    pinMode(PIN_DESVIO_UNO, INPUT_PULLUP);     // resistencia PULL UP
    pinMode(PIN_RECTA_UNO, INPUT_PULLUP);
    pinMode(VERDE_RECTA_UNO, OUTPUT);          //Configura pines de salida para LEDs
    pinMode(AZUL_DESVIO_UNO, OUTPUT);
    servoUno.attach(3,750,1800);               //Configura el Servo, 750 ms = 0° y 1800 ms = 180°
    servoUno.write(gradosUno);                 //Al arrancar mueve el motor hasta 0°, vía recta
}

void loop() {
    unsigned long instanteActual = millis();

    // La variable estado nos indica en qué estado se encuentra la máquina de estados.
    // Tenemos que controlar qué ha de hacerse mientras se está en ese "estado".
    // Según el estado y las "condiciones" se mantiene en el estado en que está o cambia de estado.
    switch (estado) {
	case ESTADO_INICIAL:    // Encendido Arduino, posicionamos la vía recta 0°
            servoUno.write(0);                      // Mueve a 0°
            // Indicamos vía recta
            digitalWrite (AZUL_DESVIO_UNO, LOW);    // Apagar el LED de desvío
            digitalWrite(VERDE_RECTA_UNO, HIGH);    // Enciende LED verde, vía recta
	    estado = ESTADO_RECTA;                  // Cambiamos de estado
            break; // Si no ponemos este break, el programa continúa ejecutando las siguientes líneas

	case ESTADO_RECTA:      // La vía está recta y permanecerá en este estado hasta que se pulse el botón de desvío
	    if (digitalRead(PIN_DESVIO_UNO) == VALOR_BOTON_DETECTADO) { // Si se pulsa el botón de desvío
                // Indicamos desvío
		digitalWrite (VERDE_RECTA_UNO, LOW);
		digitalWrite (AZUL_DESVIO_UNO, HIGH);
                anguloDestino = 45;                     // Ángulo en que queremoa que se posicione la aguja
                tiempoUnGrado = TIEMPO_DURACION_CAMBIO / abs(anguloDestino - anguloActual); // Tiempo de "salto" entre grado y grado de movimento
                anguloActual++;                         // Incrementamos un grado el ángulo actual
		servoUno.write(anguloActual);           // Posisionamos el servo en el nuevo ángulo
		instanteAnterior = instanteActual;      // Guardamos el instante en que se ha movido el servo
		estado = ESTADO_DESVIANDO;              // Cambiamos de estado
	    }
	    break;

	case ESTADO_DESVIANDO:  // Se está moviendo la aguja para desvío
            if ((instanteActual - instanteAnterior) >= tiempoUnGrado) { // Si ya ha pasado el tiempo que hay que esperar entre cada moviento de un grado
                anguloActual++;                         // Incrementamos un grado el ángulo actual
		servoUno.write(anguloActual);           // Posisionamos el servo en el nuevo ángulo
		instanteAnterior = instanteActual;      // Guardamos el instante en que se ha movido el servo
                if (anguloActual == anguloDestino) {    // Si ya la aguja está en su "destino", cambiamos de estado
                    estado = ESTADO_DESVIO;             // Cambiamos de estado
                }
            }
            break;

	case ESTADO_DESVIO:     // La vía está desvíada y permanecerá en este estado hasta que se pulse el botón de recta
	    if (digitalRead(PIN_RECTA_UNO) == VALOR_BOTON_DETECTADO) {  // Si se pulsa el botón de reta
                // Indicamos vía recta
		digitalWrite (VERDE_RECTA_UNO, HIGH);
		digitalWrite (AZUL_DESVIO_UNO, LOW);
                anguloDestino = 0;                      // Ángulo en que queremoa que se posicione la aguja
                tiempoUnGrado = TIEMPO_DURACION_CAMBIO / abs(anguloDestino - anguloActual); // Tiempo de "salto" entre grado y grado de movimento
                anguloActual--;                         // Decrementamos un grado el ángulo actual
		servoUno.write(anguloActual);           // Posisionamos el servo en el nuevo ángulo
		instanteAnterior = instanteActual;      // Guardamos el instante en que se ha movido el servo
		estado = ESTADO_RECTIFICANDO;           // Cambiamos de estado
	    }
	    break;

	case ESTADO_RECTIFICANDO:   // Se está moviendo la aguja para vía recta
            if ((instanteActual - instanteAnterior) >= tiempoUnGrado) { // Si ya ha pasado el tiempo que hay que esperar entre cada moviento de un grado
                anguloActual--;                         // Decrementamos un grado el ángulo actual
		servoUno.write(anguloActual);           // Posisionamos el servo en el nuevo ángulo
		instanteAnterior = instanteActual;      // Guardamos el instante en que se ha movido el servo
                if (anguloActual == anguloDestino) {    // Si ya la aguja está en su "destino", cambiamos de estado
                    estado = ESTADO_RECTA;              // Cambiamos de estado
                }
            }
            break;
    }
}

Se puede cambiar la velocidad del movimiento de la aguja aumentando o disminuyendo el valor de TIEMPO_DURACION_CAMBIO.

Es una solución rápida, porque me imagino que querrás controlar más de una aguja con el mismo Arduino. Para ello lo más "cómodo" es convertir este código en una clase y trabajar con tantos objetos como agujas se quiera controlar. En cuanto tenga algo de tiempo procuraré hacer la clase.

Un par de dudas que me surgen de cómo sería deseable que funcione.

  • ¿Qué luz debería estar encendida, mientras se está moviendo la aguja? Ninguna, las dos, parpadeando, la de "destino", la de "origen".
  • ¿Qué se ha de hacer si se pulsa "el otro botón" durante el movimiento de la aguja? Por ejemplo: si pulsamos el botón de desvío; la aguja se mueve a la posición de desvío. Si antes de llegar a la posición de desvío se pulsa el botón de vía recta, ¿no hacemos caso hasta que se termine de mover la aguja o inmediatamente movemos la aguja en sentido contrario para ponerla en vía recta?

Nota: no sé si se debería de crear un nuevo post o continuar con este, tal vez con el nombre cambiado. Quizás un moderador podría aconsejar sobre ello.

Aquí tienes la "adaptación rápida" del código convertido en la clase Aguja. Esto nos permite definir más agujas rápidamente. Como ejemplo, he puesto que controle dos agujas.

//Desvío simple con millis() 4/5/2020
//Servo con dos botones y LEDs. Resistencias Pull Up.

#include <Servo.h>

const uint8_t PIN_SERVO_UNO = 3;    // motor en PIN ~3
const uint8_t PIN_SERVO_DOS = 10;   // motor en PIN ~10

//Botones
const uint8_t PIN_DESVIO_UNO = 4;            // Botón para desvío, mueve a izquierda
const uint8_t PIN_RECTA_UNO = 5;             // Botón para vía recta, mueve a derecha (0°)
const uint8_t PIN_DESVIO_DOS = 8;            // Botón para desvío, mueve a izquierda
const uint8_t PIN_RECTA_DOS = 9;             // Botón para vía recta, mueve a derecha (0°)
const uint8_t VALOR_BOTON_DETECTADO = LOW;   //Valor que deberá tener el pin cuando se detecta el botón

//LEDs
const uint8_t VERDE_RECTA_UNO = 6;           // Led de vía recta
const uint8_t AZUL_DESVIO_UNO = 7;           // LED de desvío
const uint8_t VERDE_RECTA_DOS = 11;           // Led de vía recta
const uint8_t AZUL_DESVIO_DOS = 12;           // LED de desvío

const unsigned long TIEMPO_DURACION_CAMBIO = 1500UL; // Este es el tiempo total que queremos que tarde la aguja en cambiar de posición


// Prototipado de la clase Aguja
class Aguja {
    public:     // Las variables y funciones declaradas como públicas son accesibles por todos
        // Constructor
        Aguja(Servo &servo, unsigned long duracionCambio, uint8_t pinRecta, uint8_t pinDesvio, uint8_t pinLuzVerde, uint8_t pinLuzAzul);
        void loop();    // Lo que normalmente pondríamos en el loop() principal. Se ha de ejecutar contínuamente
    private:    // Las variables y funciones declaradas como privada no son accesibles fuera de la clase
        Servo &servo;                   // Referencia al servo
        unsigned long duracionCambio;   // Este es el tiempo total que queremos que tarde la aguja en cambiar de posición
        uint8_t pinRecta;               // Botón para vía recta, mueve a derecha (0°)
        uint8_t pinDesvio;              // Botón para desvío, mueve a izquierda
        uint8_t pinLuzVerde;
        uint8_t pinLuzAzul;
        int anguloActual;               // Ángulo en que se encuentra actualmente la aguja. Inicialmente lo pondremos a cero
        int anguloDestino;              // Ángulo en que queremoa que se posicione la aguja. Inicialmente lo pondremos a cero
        unsigned long instanteAnterior; // En principio lo inicializamos a cero, sólo se usa cuando se está moviendo la aguja
        unsigned long tiempoUnGrado;    // Tiempo que ha de pasar entre grado y grado de movimiento de la aguja
        enum estado_t {             // Estos son los estados de cualquier aguja, no importa qué aguja (no hace falta el _UNO)
            ESTADO_INICIAL,         // Para cuando se encienda el Arduino se posicione recta
            ESTADO_DESVIANDO,       // Cuando está cambiando de recta a desvío
            ESTADO_DESVIO,          // Cuando está en modo desvío
            ESTADO_RECTIFICANDO,    // Cuando está cambiando de desvío a recta
            ESTADO_RECTA,           // Cuando está en recta
        } estado;                   // Inicialmente tenemos que posicionarla recta, sí o sí
};

// Implementación de las funciones de la clase Aguja
Aguja::Aguja(Servo &servo, unsigned long duracionCambio, uint8_t pinRecta, uint8_t pinDesvio, uint8_t pinLuzVerde, uint8_t pinLuzAzul)
    : servo(servo),
    duracionCambio(duracionCambio),
    pinRecta(pinRecta),
    pinDesvio(pinDesvio),
    pinLuzVerde(pinLuzVerde),
    pinLuzAzul(pinLuzAzul),
    anguloActual(0),
    anguloDestino(0),
    instanteAnterior(0),
    tiempoUnGrado(0),
    estado(ESTADO_INICIAL)
{
    pinMode(pinRecta, INPUT_PULLUP);    // resistencia PULL UP
    pinMode(pinDesvio, INPUT_PULLUP);
    pinMode(pinLuzVerde, OUTPUT);       //Configura pines de salida para LEDs
    pinMode(pinLuzAzul, OUTPUT);
}

void Aguja::loop() {
    unsigned long instanteActual = millis();

    // La variable estado nos indica en qué estado se encuentra la máquina de estados.
    // Tenemos que controlar qué ha de hacerse mientras se está en ese "estado".
    // Según el estado y las "condiciones" se mantiene en el estado en que está o cambia de estado.
    switch (estado) {
	case ESTADO_INICIAL:    // Encendido Arduino, posicionamos la vía recta 0°
            servo.write(0);                     // Mueve a 0°
            // Indicamos vía recta
            digitalWrite (pinLuzAzul, LOW);     // Apagar el LED de desvío
            digitalWrite(pinLuzVerde, HIGH);    // Enciende LED verde, vía recta
	    estado = ESTADO_RECTA;              // Cambiamos de estado
            break; // Si no ponemos este break, el programa continúa ejecutando las siguientes líneas

	case ESTADO_RECTA:      // La vía está recta y permanecerá en este estado hasta que se pulse el botón de desvío
	    if (digitalRead(pinDesvio) == VALOR_BOTON_DETECTADO) {  // Si se pulsa el botón de desvío
                // Indicamos desvío
		digitalWrite (pinLuzVerde, LOW);
		digitalWrite (pinLuzAzul, HIGH);
                anguloDestino = 45;                     // Ángulo en que queremoa que se posicione la aguja
                tiempoUnGrado = duracionCambio / abs(anguloDestino - anguloActual); // Tiempo de "salto" entre grado y grado de movimento
                anguloActual++;                         // Incrementamos un grado el ángulo actual
		servo.write(anguloActual);              // Posisionamos el servo en el nuevo ángulo
		instanteAnterior = instanteActual;      // Guardamos el instante en que se ha movido el servo
		estado = ESTADO_DESVIANDO;              // Cambiamos de estado
	    }
	    break;

	case ESTADO_DESVIANDO:  // Se está moviendo la aguja para desvío
            if ((instanteActual - instanteAnterior) >= tiempoUnGrado) { // Si ya ha pasado el tiempo que hay que esperar entre cada moviento de un grado
                anguloActual++;                         // Incrementamos un grado el ángulo actual
		servo.write(anguloActual);              // Posisionamos el servo en el nuevo ángulo
		instanteAnterior = instanteActual;      // Guardamos el instante en que se ha movido el servo
                if (anguloActual == anguloDestino) {    // Si ya la aguja está en su "destino", cambiamos de estado
                    estado = ESTADO_DESVIO;             // Cambiamos de estado
                }
            }
            break;

	case ESTADO_DESVIO:     // La vía está desvíada y permanecerá en este estado hasta que se pulse el botón de recta
	    if (digitalRead(pinRecta) == VALOR_BOTON_DETECTADO) {   // Si se pulsa el botón de reta
                // Indicamos vía recta
		digitalWrite (pinLuzVerde, HIGH);
		digitalWrite (pinLuzAzul, LOW);
                anguloDestino = 0;                      // Ángulo en que queremoa que se posicione la aguja
                tiempoUnGrado = duracionCambio / abs(anguloDestino - anguloActual); // Tiempo de "salto" entre grado y grado de movimento
                anguloActual--;                         // Decrementamos un grado el ángulo actual
		servo.write(anguloActual);              // Posisionamos el servo en el nuevo ángulo
		instanteAnterior = instanteActual;      // Guardamos el instante en que se ha movido el servo
		estado = ESTADO_RECTIFICANDO;           // Cambiamos de estado
	    }
	    break;

	case ESTADO_RECTIFICANDO:   // Se está moviendo la aguja para vía recta
            if ((instanteActual - instanteAnterior) >= tiempoUnGrado) { // Si ya ha pasado el tiempo que hay que esperar entre cada moviento de un grado
                anguloActual--;                         // Decrementamos un grado el ángulo actual
		servo.write(anguloActual);              // Posisionamos el servo en el nuevo ángulo
		instanteAnterior = instanteActual;      // Guardamos el instante en que se ha movido el servo
                if (anguloActual == anguloDestino) {    // Si ya la aguja está en su "destino", cambiamos de estado
                    estado = ESTADO_RECTA;              // Cambiamos de estado
                }
            }
            break;
    }
}


Servo servoUno;
Servo servoDos;
Aguja agujaUno(servoUno, TIEMPO_DURACION_CAMBIO, PIN_RECTA_UNO, PIN_DESVIO_UNO, VERDE_RECTA_UNO, AZUL_DESVIO_UNO);
Aguja agujaDos(servoDos, TIEMPO_DURACION_CAMBIO, PIN_RECTA_DOS, PIN_DESVIO_DOS, VERDE_RECTA_DOS, AZUL_DESVIO_DOS);

void setup() {
    servoUno.attach(PIN_SERVO_UNO, 750, 1800);    //Configura el Servo, 750 ms = 0° y 1800 ms = 180°
    servoDos.attach(PIN_SERVO_DOS, 750, 1800);    //Configura el Servo, 750 ms = 0° y 1800 ms = 180°
}

void loop() {
    agujaUno.loop();  // Llamamos al loop() de Aguja
    agujaDos.loop();  // Llamamos al loop() de Aguja
}

Muchas gracias por tu nuevo mensaje! No te preocupes por favor por la demora en responder, tus aportes y soluciones bien valen una cuarentena!

Estaba trabajando con dos desvíos (el proyecto es manipular cuatro) y no sabía cómo hacerlo sin utilizar "clase", que no alcanzaba a comprender su uso. Voy a estudiar para entender y aplicar lo que me enviaste.

Respondo ahora a tus preguntas:

"- ¿Qué luz debería estar encendida, mientras se está moviendo la aguja? Ninguna, las dos, parpadeando, la de "destino", la de "origen"."
Si fuera posible, apagada la de "destino" y parpadeando la de "origen".

"- ¿Qué se ha de hacer si se pulsa "el otro botón" durante el movimiento de la aguja? Por ejemplo: si pulsamos el botón de desvío; la aguja se mueve a la posición de desvío. Si antes de llegar a la posición de desvío se pulsa el botón de vía recta, ¿no hacemos caso hasta que se termine de mover la aguja o inmediatamente movemos la aguja en sentido contrario para ponerla en vía recta?"
Sería adecuado no hacer caso, ¿te parece?

"no sé si se debería de crear un nuevo post o continuar con este, tal vez con el nombre cambiado. Quizás un moderador podría aconsejar sobre ello."
Podría editar el nombre y escribir algo así como: 'millis() para varios motores servo y stepper'. ¿Quedaría explicitado el amplio contenido del post?

Un cordial saludo.

Armando

El título propuesto me gusta.

Un par de preguntas más.

¿Tienes pensado poner algo más a parte de los dos motores paso a paso y los cuatro sevos? Con todo lo que conlleva de LED y pulsadores.

¿Lo piensas controlar todo con un Arduino MEGA o con dos Arduino UNO (uno para los paso a paso y otro para los servos o un motor paso a paso y dos servos cada uno)?

¿Para el cambio de ajuga te valdría con hacerlo con un único pulsador por aguja en lugar de dos? Con uno solo, lo pulsas y cambia la aguja a la "otra posición".

Muchas gracias, ya cambié el título del post por: "millis() para varios motores servo y stepper".

Respodo a tus preguntas:

"¿Tienes pensado poner algo más a parte de los dos motores paso a paso y los cuatro sevos? Con todo lo que conlleva de LED y pulsadores."
Será sólo para los dos PAPs y los cuatro servos.

"¿Lo piensas controlar todo con un Arduino MEGA o con dos Arduino UNO (uno para los paso a paso y otro para los servos o un motor paso a paso y dos servos cada uno)?"
Pienso controlarlo con un Arduino Mega, ¿te parece posible?

"¿Para el cambio de ajuga te valdría con hacerlo con un único pulsador por aguja en lugar de dos? Con uno solo, lo pulsas y cambia la aguja a la "otra posición"."
Sería ideal hacerlo con un único pulsador para cada aguja, siendo así replicaría las luces de las vías en el tablero indicando "Recta" o "Desvío". Pero no sé cómo hacerlo y temo cargarte con otro trabajo.

Un cordial saludo

Armando

No tenía conocimiento de la POO-OOP por lo que no entendía la lógica de la construcción de clases con variables y funciones de los objetos. Por ello te dije que no sabía cómo hacer para un único pulsador para cada aguja. Me puse a estudiar el tema, felizmente pude seguir paso a paso tu programa y conclui que la rutina de cambios de estado de un pulsador es lo mismo esté o no el objeto dentro de una clase.
Con tus indicaciones me siento como que he ascendido a una división superior (me hiciste ganar un campeonato!).
Muchas gracias nuevamente.
Un cordial saludo
Armando

Me alegra, Armando, tu “ascenso de división”. La programación orientada a objeto tiene más cosas de las que he empleado en el programa, cosas como la herencia, pero sólo con el encapsulamiento y el empleo de los constructores ya son herramientas muy valiosas para la programación.

Para que funcione con un solo botón basta con poner el mismo número del pin para pinRecta y pinDesvio.

Otra forma es cambiar los inicializadores pinRecta y pinDesvio en constructor de la clase Aguja. Para que si se le pasa un -1 en uno de los pines, tome como valor el del otro pin. Es lo mismo, pero “más elegante”. Para ello el cambio que hay que hacer es que en:

// Implementación de las funciones de la clase Aguja
Aguja::Aguja(Servo &servo, unsigned long duracionCambio, uint8_t pinRecta, uint8_t pinDesvio, uint8_t pinLuzVerde, uint8_t pinLuzAzul)
    : servo(servo),
    duracionCambio(duracionCambio),
    pinRecta(pinRecta),
    pinDesvio(pinDesvio),

Cambiamos las dos últimas líneas para que queden así:

// Implementación de las funciones de la clase Aguja
Aguja::Aguja(Servo &servo, unsigned long duracionCambio, uint8_t pinRecta, uint8_t pinDesvio, uint8_t pinLuzVerde, uint8_t pinLuzAzul)
    : servo(servo),
    duracionCambio(duracionCambio),
    pinRecta((pinRecta != 0xFF) ? pinRecta : pinDesvio),     // Si se pasa un -1 en pinRecta, tendrá como valor el mismo que pinDesvio
    pinDesvio((pinDesvio != 0xFF) ? pinDesvio : pinRecta),   // Si se pasa un -1 en pinDesvio, tendrá como valor el mismo que pinRecta

También se podría quitar el uso de uno de los dos pines en la clase, y así ahorrarnos un byte de memoria por cada servo. Pero el ahorro no es significativo y, además, siempre podemos cambiar de opinión a última hora y volver a usar dos pulsadores en lugar de uno, sin tener que volver a modificar la clase. O incluso tener unos con un pulsador y otros con dos.

Voy a ver si hago el montaje con un Arduino MEGA y pruebo a juntar todo el código. Con los cuatro servos y los dos motores paso a paso. Para que no tengas que estar modificando las constantes del código; no estaría de más, si lo tienes ya más o menos definido, que me dijeras los pines del Arduino MEGA a los que vas a conectar cada cosa.

Me he fijado en que parece que ajustas el inicio y fin de la posición del servo usando el segundo y tercer parámetro de servo.attach(). Creo que es mejor y más fácil no usar esos parámetros, sino configurar el ángulo de “recto” y “desvío” de cada servo. No tiene por qué se 0º y 45º, sino cualquier valor arbitrario entre 0 y 180 grados. Además, esto facilita el configurar desvíos en que “recto” ha de tener un ángulo mayor que “desvío”. Es decir, en lugar de 0º ser vía recta y 45º ser desviada, podría ser 50º vía recta y 10º desviada.

Además de poner lo de la luz parpadeando mientras se realiza el cambio de aguja. Tal vez, añadiendo dos pulsadores más, se podría tener la posibilidad de memorizar en la EEPROM las dos posiciones de cada servo y configurarlos con el propio programa. Por ejemplo: el programa arranca y funciona normal. Si se mantienen pulsado esos dos botones a la vez durante tres segundos, pasa al modo “configuración”. Todos las luces de los cambios de aguja parpadean. Si se pulsa el botón de uno de los cambios de agujas, la luz de la posición en que está parpadea más rápido y la otra se apaga. Ahora se puede ajustar la posición de la aguja grado a grado aumentando o disminuyendo un grado pulsando uno de los dos botones de configuración. Así no hay que estar modificando, recomplilando y cargando el código del Arduino para probar y ajustar los servos.

Cierto es que cada cambio/mejora lleva su tiempo y no se lo dedico muy a menudo. Pero, si no llevas prisa, creo que vale la pena.

Muchas gracias, analizaré lo que escribiste y definiré los pines del Mega.

Configuré con cero grados para que el servo quede quieto en el default de vía recta, independientemente de donde haya quedado cuando se apagó el Arduino, pensando que el movimiento de la vía es de pocos milímetros y que hay que evitar que el servo trate de ir más allá de los límites físicos que tiene el desvío. No alcanzo a entender lo que me propones, lo que hice fue determinar el cero (equivalente al "recto") de cada servo y que corresponda a la vía sin el desvío.

Un cordial saludo

Armando

Me refiero a quitar los valores 750 y 1800 de estas líneas de código:

    servoUno.attach(PIN_SERVO_UNO, 750, 1800);    //Configura el Servo, 750 ms = 0° y 1800 ms = 180°
    servoDos.attach(PIN_SERVO_DOS, 750, 1800);    //Configura el Servo, 750 ms = 0° y 1800 ms = 180°

Y que no tiene por qué ser 0º y 45º los ángulos en que se ha de posicionar el servo para que queden bien posicionados. ¿Ya tienes los servos montados físicamente en los cambios de aguja?

La verdad es que tardo menos en hacer los cambios que en escribir las explicaciones en el post. Junto con lo de poder definir con -1 si se utiliza un único pulsador para el cambio de aguja (que lo he definido así para los dos servos), he realizado las modificaciones para que parpadee durante el cambio de aguja. El tiempo del parpadeo se define con la constante TIEMPO_PARPADEO. Para ver los cambios del parpadeo basta con buscar esa constante y las variables instanteParpadeo y estadoParpadeo.

Las pruebas del código las he hecho con el Arduino UNO.

El código es demasiado grande como para postearlo directamente (más por los comentarios que por el código en sí). Así que lo adjunto.

foro_cambio_aguja.ino (10.4 KB)

Aún no están instalados los servos en los desvíos, ¿necesitas que lo haga para determinar los grados entre uno y otro estado?

Estoy ahora distribuyendo las conexiones en el Arduino Mega, ¿puedo cambiar los diferentes pines o los traslado tal cual están?

Indicame por favor si las líneas 11 y 12 y 58 y 59 deben quedar como en este último código si dejamos un solo botón para cada servo (que me parece la mejor decisión).

Olvidé responderte este punto del mensaje anterior a éste: no hay prisa! Hagamos por favor todas las mejoras posibles.

Un cordial saludo.

Armando

Te envío una propuesta de distribución de pines en el Arduino Mega:

  • pines 1 al 21: Motores paso a paso - steppers, switches y LEDs respectivos

  • pines 22 al 53: Servos, botones y LEDs respectivos

  • una vez conectados, accesorios como speakers y otros a distribuir entre los pines libres.

Espero por favor tu confirmación o correcciones para realizar las reconexiones.

Un cordial saludo

Armando

En principio he configurado los pines del Arduino MEGA para cuatro cambios de aguja con un solo pulsador y sus dos luces. Estos son los pines:

const uint8_t PIN_SERVO_UNO     = 5;
const uint8_t PIN_SERVO_DOS     = 6;
const uint8_t PIN_SERVO_TRES    = 7;
const uint8_t PIN_SERVO_CUATRO  = 8;

//Botones
const uint8_t PIN_DESVIO_UNO    = 22;   // Botón para desvío, mueve a izquierda
const uint8_t PIN_DESVIO_DOS    = 23;   // Botón para desvío, mueve a izquierda
const uint8_t PIN_DESVIO_TRES   = 24;   // Botón para desvío, mueve a izquierda
const uint8_t PIN_DESVIO_CUATRO = 25;   // Botón para desvío, mueve a izquierda

const uint8_t VALOR_BOTON_DETECTADO = LOW;  //Valor que deberá tener el pin cuando se detecta el botón

//LEDs
const uint8_t VERDE_RECTA_UNO       = 26;   // Led de vía recta
const uint8_t AZUL_DESVIO_UNO       = 27;   // LED de desvío
const uint8_t VERDE_RECTA_DOS       = 28;   // Led de vía recta
const uint8_t AZUL_DESVIO_DOS       = 29;   // LED de desvío
const uint8_t VERDE_RECTA_TRES      = 30;   // Led de vía recta
const uint8_t AZUL_DESVIO_TRES      = 31;   // LED de desvío
const uint8_t VERDE_RECTA_CUATRO    = 32;   // Led de vía recta
const uint8_t AZUL_DESVIO_CUATRO    = 33;   // LED de desvío

Faltaría definir los pines de los motores paso a paso, sus finales de carrera y sus luces.

Por ahora, para las pruebas, creo que es mejor que no ubiques aún los servos en los cambios de aguja. Así no "sufren" los servos durante las pruebas.

Otra cosa. Los servos, si se ven forzados, cuando tratan de mantener la posición hacen ruido y consumen corriente. Así que voy a hacer que el programa los "desactive" cuando no se han de mover. Pero aún no está implementado.

Adjunto código para el MEGA con los pines definidos y para ya "manejar" los cuatro servos.

foro_cambio_aguja_mega.ino (10.9 KB)

Muchas gracias, bajé el código y pasaré los componentes de los servos al Mega.

¿Defino ahora los pines de los motores paso a paso, sus finales de carrera y sus luces y paso los componentes al Mega o espero tus indicaciones?

Un cordial saludo

Armando

Buenos días.

Pasé dos servos al A Mega 2650 con sus respectivos botones y luces y funcionan perfecto! Excelente el comportamiento de las luces, parpadeando mientras gira e indicando luego la "vía" habilitada.

Para poder practicar los grados del moviento de los servos estoy armando un pequeño "banquito de pruebas" con un sector de vía con desvío y un montaje para el servo.

Nuevamente muchas gracias!

Cordialmente.

Armando

Estuve regulando la extensión del movimiento con el servo montado en el "banquito de pruebas". La conclusión es que con "anguloDestino" fijado en 20 grados hace perfecto el cambio en estas vías de escala HO (1:87).

Comencé las pruebas sin fijar el servo en el montaje, con lo que se logró evitar que "sufra", como me indicaste.

Vamos muy bien!

Un cordial saludo.

Armando