¡Bienvenido visitante ! ¡Apúntate al foro ! ¡Es gratuito !
Aquí encontrarás respuesta a muchas de tus preguntas sobre modelismo ferroviario.
¡Puedes preguntar a tus anchas y aportar tus conocimientos!

Lenguaje de programación Arduino

Un sitio específico para que cada cual hable de sus proyectos. Así no queda difuminado según el tema por los talleres.
Avatar de Usuario
fmco
Forero
Mensajes: 945
Registrado: Vie Ene 20, 2006 4:09 pm
20
Ubicación: Reus
Contactar:

Re: Lenguaje de programación Arduino

Mensaje por fmco »

hola,
Lo que no se ver, es como modificar la dirección DCC del decoder?

como en todos los decodificadores de accesorios, programando la CV1 y la CV9. :D

Como alumno avezado también puedes definir un pin con un pulsador, y en la función notifyDccAccTurnoutOutput() ver si esta pulsado y si lo está actualizar las CV1 y CV9 y la variable dccBase

saludos,

Paco
Avatar de Usuario
sls_h0e
Forero
Mensajes: 2328
Registrado: Sab Ene 21, 2006 2:27 pm
20
Ubicación: Madrid

Re: Lenguaje de programación Arduino

Mensaje por sls_h0e »

Hola
Al inicializar la EEPROM y cargar de nuevo el programa, los valores quedan asi:
add 1 = 0, add 7=1, add 8 = 13, el valor de dccBase = -3
image.png
image.png (8.49 KiB) Visto 2398 veces
-------------------
Para inicializar la dirrección DCC, lo que suelo hacer mediante un pulsador conectado a A6 o A7, miro si está pulsado al arranque y paso a la espera de 10s/15s de una dirección DCC desde el mando. mediante un flag hago que al leer esa dirección en notifyDccAccTurnoutOutput, la guardo en la EEPROM
Lenz + 2C + K.Bay
Avatar de Usuario
fmco
Forero
Mensajes: 945
Registrado: Vie Ene 20, 2006 4:09 pm
20
Ubicación: Reus
Contactar:

Re: Lenguaje de programación Arduino

Mensaje por fmco »

hola,

La función Dcc.initAccessoryDecoder() de la librería escribe la CV29, CV7 y CV8, como la llamada a leeDireccionDcc() esta después siempre encuentra CV8 a 13 y no inicializa las CV1/CV9, lo hice así para no llamar a funciones de la librería antes de inicializarla. (Si leeDireccionDcc() la colocas antes te funcionará como quieres.)

Tal como esta sólo tienes que resetear el decodificador con CV8 = 8 (o lo que quieras) y ya se pondrá la dirección 1 al arrancar de nuevo.

Otra cosa, hablando de CV1 y CV9, para no complicar la explicación en el ejemplo no comenté que estas CV definen la dirección como un decodificador clásico de 4 salidas ya que he usado la formula:

dccBase = (CV9 * 64 + CV1) * 4 - 3

O sea CV1 = 1, CV9 = 0 las direcciones empezarán por la 1 y con CV1 = 2, CV9=0 las direcciones empezaran por la 5.

NOTA: Tal como lo tienes en el listado que pasas con el Multimaus empieza con la dirección 1, pero con Lenz no, será la 256, por el tema del desplazamiento de las 4 direcciones entre Roco y Lenz.

Si deseas que se pueda elegir la dirección de 1 en 1 en lugar de 4 en 4 has de cambiar la formula a

dccBase = CV9 * 256 + CV1

Imagen

La librería es bastante completa y compleja y cada vez que la miro encuentro algo.

Por ejemplo, contempla la opción de usar direccionamiento por salidas con FLAGS_OUTPUT_ADDRESS_MODE en Dcc.init() que es la que he usado con lo que la formula debería haber sido dccBase = CV9 * 256 + CV1 y ademas tiene una función que programa las CV1 y CV9 con el próximo paquete de accesorios que le llegue, o sea que si detectas la pulsación del botón puedes llamar a la función de la librería Dcc.setAccDecDCCAddrNextReceived(true) y automáticamente guardará CV1 y CV9 con el próximo paquete de accesorios, no hay que hacer nada más.

Si luego quieres que a los 15 segundos deje de estar activo solo tienes que llamar Dcc.setAccDecDCCAddrNextReceived(false) y pasará a modo normal.

saludos,

Paco
Última edición por fmco el Dom Dic 27, 2020 8:58 am, editado 1 vez en total.
Avatar de Usuario
sls_h0e
Forero
Mensajes: 2328
Registrado: Sab Ene 21, 2006 2:27 pm
20
Ubicación: Madrid

Re: Lenguaje de programación Arduino

Mensaje por sls_h0e »

Hola
Gracias Paco, esto tiene más miga de la que aparenta, me pongo a ver las nuevas funciones
Saludos
Lenz + 2C + K.Bay
Avatar de Usuario
fmco
Forero
Mensajes: 945
Registrado: Vie Ene 20, 2006 4:09 pm
20
Ubicación: Reus
Contactar:

Re: Lenguaje de programación Arduino

Mensaje por fmco »

hola,

acabo de ver otro fallo, si se cambia una CV no leo nuevamente la dirección dccBase con leeDireccionDcc() por lo que no hay mas remedio que resetear el decodificador para que lea CV1 y CV9 y actualice la dirección.

la librería tiene la función notifyDccCVChange() que nos informa cuando una CV cambia de valor así que tendría que haber añadido al programa:

Código: Seleccionar todo

void    notifyDccCVChange( uint16_t CV, uint8_t Value) {
  dccBase = leeDireccionDcc();
}
saludos,

Paco
Avatar de Usuario
sls_h0e
Forero
Mensajes: 2328
Registrado: Sab Ene 21, 2006 2:27 pm
20
Ubicación: Madrid

Re: Lenguaje de programación Arduino

Mensaje por sls_h0e »

Hola

Funciona todo como tu has dicho, ahora se puede programar la dirección base enviando una orden de cambio desde la central
He añadido un control en notifyDccAccTurnoutOutput() para evitar rebotes, comparando la orden recibida con la anterior, si es igual a la primera no se ejecuta
Así queda el código

Código: Seleccionar todo

#include <Semaforo.h>        // Libreria Semaforo.h -- Paco Cañada 2020 -- https://usuaris.tinet.cat/fmco/
#include <NmraDcc.h>

#define ANALOG_PIN A7                                          // pulsador con pull-up para entrar en modo de programación
                                                               // pulsar al arranque hasta que se encienda el led del Arduino 
#define ESPERA_DIR_DCC 10000                                   // tiempo de espera a una nueva dirección DCC, em milisegundos

NmraDcc Dcc;                                                   // Objetos librerias

Semaforo Sem2Luces;
Semaforo Sem3Luces;
Semaforo Sem4Luces;
Semaforo Sem2LucesManiobras;
Semaforo Sem2LucesPasoNivel;

const byte DCC_PIN = 2;                                        // pin señal DCC
int dccBase;                                                   // direccion base DCC
int oldAddr;
int oldDirection;

boolean newDCC = false;
void setup() {  
  dccBase = leeDireccionDcc();                                 // Lee direccion base DCC
  Dcc.pin (digitalPinToInterrupt (DCC_PIN), DCC_PIN, 1);       // Inicializa DCC
  Dcc.initAccessoryDecoder (MAN_ID_DIY, 1, FLAGS_OUTPUT_ADDRESS_MODE, 0); 
  if(analogRead(ANALOG_PIN) == 0){                             // si tecla pulsada, pedimos nueva dirección DCC
    pinMode(LED_BUILTIN, OUTPUT);                              // encendemos LED
    digitalWrite(LED_BUILTIN, HIGH);
    newDCC = true;                                             // flag de control, pasara a false en notifyDccCVChange() o terminar el tiempo de espera
    Dcc.setAccDecDCCAddrNextReceived(true);                    // activamos espera de una nueva dirección                 
    int oldMillis = millis();                                  // esperamos 
    while(newDCC == true){                                     // esperamos mientras mientras estemos a la espera de una nueva direccion DCC
      Dcc.process();                                           // Procesa paquetes DCC 
      if ((millis() - oldMillis) > ESPERA_DIR_DCC) {           // salimos si han pasado el tiempo definido en ESPERA_DIR_DCC
        newDCC = false;                                        // ponemos a false el flag de control si pasamos del tiempo de espara
        break;                                                 // salimos del bucle de espera
      }
    }   
    Dcc.setAccDecDCCAddrNextReceived(false);                   // desactivamos espera de una nueva dirección 
    digitalWrite(LED_BUILTIN, LOW);                            // apagamos LED
  }
  
  // definicion de los semaforos y sus conexiones, ejemplo para semaforo de 2 luces:
  // xxxxxx.init(R, V) define un semaforo de dos luces llamado "xxxxxx", R = pin LED rojo, V = pin LED verde
  Sem2Luces.init (12, 3);       // pins R, V                                  
  Sem3Luces.init (14, 15, 16);  // pins R, A, V
  Sem4Luces.init (6, 7, 8, 9);  // pins R, A, V, Blanco
  Sem2LucesManiobras.initManiobra(10, 11);
  Sem2LucesPasoNivel.initPasoNivelVehiculos (13, 12);
}

void loop() {
  Dcc.process();                                            // Procesa paquetes DCC
  Sem2Luces.process();                                      // Procesa semaforos
  Sem3Luces.process();
  Sem4Luces.process();
  Sem2LucesManiobras.process();
  Sem2LucesPasoNivel.process();
}

int leeDireccionDcc() {
  int direccion, cv1, cv9;
  if (Dcc.getCV (CV_MANUFACTURER_ID) != MAN_ID_DIY) {       // Si CV8 no es 13, resetea CV
    Dcc.setCV (CV_ACCESSORY_DECODER_ADDRESS_LSB, 1);
    Dcc.setCV (CV_ACCESSORY_DECODER_ADDRESS_MSB, 0);
    Dcc.setCV (CV_MANUFACTURER_ID, MAN_ID_DIY);
  }
  cv1 = Dcc.getCV (CV_ACCESSORY_DECODER_ADDRESS_LSB);
  cv9 = Dcc.getCV (CV_ACCESSORY_DECODER_ADDRESS_MSB);
  //direccion =  (( cv9 << 8) + (cv1 << 2)) - 3 ;
  direccion = (cv9 << 8) + cv1;
  return (direccion);
}

// informa cuando una CV cambia de valor
void  notifyDccCVChange( uint16_t CV, uint8_t Value) {
  dccBase = leeDireccionDcc();
  newDCC = false;
}

void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint8_t OutputPower ) { 
  if((Addr == oldAddr) && (Direction == oldDirection) && OutputPower) return;        // si se recibe la misma orden no se ejecuta
  byte orden;
  if ((Addr >= dccBase) && (Addr <= dccBase + 11) && OutputPower) {                  // comprueba direccion dentro del rango
    orden = (((Addr - dccBase) << 1) + Direction);                                   // orden de cambio de aspecto entre 0 y 21
    switch (orden) {                                                                 // actua segun orden
      case 0:                                                                        // Direccion base: Semaforo dos luces
        Sem2Luces.aspecto (PARADA);
        break;
      case 1:
        Sem2Luces.aspecto (VIA_LIBRE);
        break;
      
      case 2:                                               // Direccion base + 1/2: Semaforo 3 luces
        Sem3Luces.aspecto (PARADA);
        break;
      case 3:
        Sem3Luces.aspecto (VIA_LIBRE);
        break;      
      case 4:
        Sem3Luces.aspecto (ANUNCIO_PRECAUCION);
        break; 
      case 5:
        Sem3Luces.aspecto (ANUNCIO_PARADA);
        break; 

      case 6:                                               // Direccion base + 3/4/5/6/7: Semaforo 4 luces
        Sem4Luces.aspecto (PARADA);
        break;
      case 7:
        Sem4Luces.aspecto (VIA_LIBRE);
        break;      
      case 8:
        Sem4Luces.aspecto (ANUNCIO_PRECAUCION);
        break; 
      case 9:
        Sem4Luces.aspecto (ANUNCIO_PARADA);
        break; 
      case 10:
        Sem4Luces.aspecto (VIA_LIBRE_CONDICIONAL);
        break;       
      case 11:
        Sem4Luces.aspecto (ANUNCIO_PARADA_INMEDIATA);
        break;       
      case 12:
        Sem4Luces.aspecto (-1);                             // apagado de todas las luces
        break;
      case 13:
        Sem4Luces.aspecto (MOVIMIENTO_AUTORIZADO);
        break; 
      case 14: 
        Sem4Luces.aspecto (REBASE_AUTORIZADO);
        break;
      case 15:
        Sem4Luces.aspecto (REBASE_AUTORIZADO_NO_PARAR);
        break;      

      case 16:                                                // Direccion base + 8/9: Semaforo 2 luces Maniobras
        Sem2LucesManiobras.aspecto (-1);                      // apagado de todas las luces  
        break;
      case 17:
        Sem2LucesManiobras.aspecto (MOVIMIENTO_AUTORIZADO);
        break;
      case 18:
        Sem2LucesManiobras.aspecto (REBASE_AUTORIZADO);
        break;
      case 19:
        Sem2LucesManiobras.aspecto (REBASE_AUTORIZADO_NO_PARAR);
        break;      
 
       case 20:                                                // Direccion base + 10/11: Semaforo 2 paso a nivel
        Sem2LucesPasoNivel.aspecto (PASO_NIVEL_CERRADO);
        break;
       case 21:
        Sem2LucesPasoNivel.aspecto (PASO_NIVEL_ABIERTO);
        break;         
    }
    oldAddr = Addr;
    oldDirection = Direction;
  }  
}

Por otra parte, me pasa que cuando uso el pin 12 en un semaforo, el LED parpadea "tiembla", pero no pasa en otros pins, lo he probado con dos arduinos diferentes

Ahora voy a intentar hacerme una librería propia

Gracias Paco
Lenz + 2C + K.Bay
Avatar de Usuario
fmco
Forero
Mensajes: 945
Registrado: Vie Ene 20, 2006 4:09 pm
20
Ubicación: Reus
Contactar:

Re: Lenguaje de programación Arduino

Mensaje por fmco »

hola,

tienes definido el pin 12 en dos semaforos diferentes por eso 'tiembla'

Código: Seleccionar todo

  Sem2Luces.init (12, 3);       // pins R, V                                  
  Sem2LucesPasoNivel.initPasoNivelVehiculos (13, 12);
saludos,

Paco
Avatar de Usuario
sls_h0e
Forero
Mensajes: 2328
Registrado: Sab Ene 21, 2006 2:27 pm
20
Ubicación: Madrid

Re: Lenguaje de programación Arduino

Mensaje por sls_h0e »

:oops: :oops: :oops: Cagada de novato...............

Saludos
Lenz + 2C + K.Bay
Avatar de Usuario
fmco
Forero
Mensajes: 945
Registrado: Vie Ene 20, 2006 4:09 pm
20
Ubicación: Reus
Contactar:

Re: Lenguaje de programación Arduino

Mensaje por fmco »

hola,

Solo comentarte que las entradas analógicas también pueden ser usadas como entradas digitales (incluso tienen pull-up), por lo que nos es necesario que recurras a analogRead() para leerlas.

https://www.arduino.cc/en/Tutorial/Foun ... gInputPins

Veo que como lo has puesto solo puedes pulsar el botón al resetear ya que la comprobación esta en el setup()

Creo que seria mas práctico que se pudiera hacer en cualquier momento por lo que al final del loop() yo añadiría una función compruebaBoton() que comprobase el botón y diera esos 10 segundos para enviar la nueva dirección (haciendo parpadear el LED si quieres) activando un flag.

Algo así:

Código: Seleccionar todo

const byte BOTON_PIN = A0;
bool dccEsperaDireccion = false;
unsigned long timeFlash;
int timeout;

void setup() {
  pinMode (LED_BUILTIN, OUTPUT);
  pinMode (BOTON_PIN, INPUT_PULLUP);
 ...
}

void loop() {
...
  compruebaBoton();
}
En la función si se pulsa el botón se activa el flag y se pone Dcc.setAccDecDCCAddrNextReceived(true), luego si el flag esta activado se hace parpadear el LED durante 10s y al final del tiempo desactivas el flag.

Código: Seleccionar todo

void compruebaBoton () {
  if (digitalRead (BOTON_PIN) == LOW) {
    dccEsperaDireccion = true;
    Dcc.setAccDecDCCAddrNextReceived(true);
    timeFlash = millis();
    timeout = 0;
  }
  if (dccEsperaDireccion) {
    if (millis() - timeFlash > 500) {
      timeFlash = millis();
      digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); //change led state
      timeout++;
      if (timeout > 20)                   // Espera 10s (500ms * 20)
        dccEsperaDireccion = false;
    }
  }
  else {
    Dcc.setAccDecDCCAddrNextReceived(false);
  }
}
También puedes desactivar el flag cuando se cambien la CV.

Código: Seleccionar todo

void notifyDccCVChange( uint16_t CV, uint8_t Value) {
  dccBase = leeDireccionDcc();
  dccEsperaDireccion = false;
}
saludos,

Paco
Avatar de Usuario
sls_h0e
Forero
Mensajes: 2328
Registrado: Sab Ene 21, 2006 2:27 pm
20
Ubicación: Madrid

Re: Lenguaje de programación Arduino

Mensaje por sls_h0e »

Me lo apunto

Saludos
Lenz + 2C + K.Bay
Avatar de Usuario
sls_h0e
Forero
Mensajes: 2328
Registrado: Sab Ene 21, 2006 2:27 pm
20
Ubicación: Madrid

Re: Lenguaje de programación Arduino

Mensaje por sls_h0e »

Hola

Estoy haciendo un mix del "Decodificador de accesorios multiusos", en lo referido al control de servos y el "Decodificador de señales”, para controlar servos y semaforos desde una misma placa.
Para enseñar en pantalla la configuración de los semaforos, quiero acceder a la structura "Leds" de la libreria desde el sketch, pero no me salgo, creia que moviendo la definición "struct” a la parte donde se se define las variables publicas del ficheo .h sería suficiente, pero no, da un error "Leds not defined in this scope" al compilar.

Como puedo hacerlo?

Saludos
Lenz + 2C + K.Bay
Avatar de Usuario
fmco
Forero
Mensajes: 945
Registrado: Vie Ene 20, 2006 4:09 pm
20
Ubicación: Reus
Contactar:

Re: Lenguaje de programación Arduino

Mensaje por fmco »

hola,

la struct LED Leds[NUMLEDS] esta dentro de la clase así que ademas de ponerla en la parte publica creo que deberías llamarla como lo haces con una función de la clase. Aunque no es buena práctica dejar a la vista estructuras de control internas que luego te pueden hacer volverte loco si algo falla.

Que dato necesitas? Quizás seria más simple añadir a la librería una función publica que te devuelva el valor que necesites, por ejemplo:

Código: Seleccionar todo

int Semaforo::pin (int color) {
return (Leds[color].pinLED);
}
saludos,

Paco
Avatar de Usuario
sls_h0e
Forero
Mensajes: 2328
Registrado: Sab Ene 21, 2006 2:27 pm
20
Ubicación: Madrid

Re: Lenguaje de programación Arduino

Mensaje por sls_h0e »

Me lo miro
Gracias Paco
Lenz + 2C + K.Bay
Avatar de Usuario
raulm
Forero
Mensajes: 77
Registrado: Jue Sep 14, 2017 8:54 am
9
Ubicación: Navaluenga

Re: Lenguaje de programación Arduino

Mensaje por raulm »

Buenos días,

llevo siguiendo este tutorial desde que lo empezaste, Paco. Muchas gracias por tu tiempo y paciencia, es de una ayuda inestimable para usar el arduino para nuestra afición.

Ahora me estoy metiendo yo con ello y como mañana veré a Salus en el Espinar, le daré la lata con estas cosas.

Un abrazo a todos.
Raúl.
LZV100 + LH100 + LS150 + PpP RS8DC + PpP GenLI + PpP SEM3

Escala N: Lenz Silver mini+ y Gold mini, Zimo MX620, D&H DH10C/DH05C/DH18A/SD18A, Kuehn N025, ESU Lokpilot micro V2/V3/V4, ESU Loksound micro V3.5/V4, digitrax DN145K, TCS K0D8-E
Avatar de Usuario
fmco
Forero
Mensajes: 945
Registrado: Vie Ene 20, 2006 4:09 pm
20
Ubicación: Reus
Contactar:

Re: Lenguaje de programación Arduino

Mensaje por fmco »

hola,

me alegro de que pueda ser útil. :D

Da recuerdos a Salus y a los demás compañeros, creo que verás más de un PacoWagon :lol: .

saludos,

Paco
Responder
 
  • Contacto
  • Nombre de Usuario:
    Email:
    Asunto:
    Mensaje: