COMPONENTES

 

THE COMPONENTS

     

 

VIDEO

 

THE VIDEO

     
Arriba  

On top

EL CÓDIGO FUENTE

 

THE MAIN CODE

     

 

/* Arduino Nano Pro as
  - WAV sound files player (24 kHz - 8 bit - mono)
  - 1 servo controller - variable speed -
  - 2 alternating common cathode led controller
 Using:
  - 2 current sensing occupancy detectors
  - 1 contact occupancy detector
  - 1 infrared occupancy detector
  - SD card reader
 
  Code by Norber, February 2016

  Freely distributable for private, non commercial, use.

  Connections:
  SD card  GND   to  GND common
  SD card  MISO  to  ARD pin 12
  SD card  SCK   to  ARD pin 13
  SD card  MOSI  to  ARD pin 11
  SD card  CS    to  ARD pin 10
  SD card  5Vcc  to  +5 V cc

  ARD pin 3      to  transistor driven 8 ohms 5V loudspeaker

*/


#include 
#include 
#include 

#define INIT         10
#define DETECTING    20
#define ALARMS_ON    25
#define LOWING_BAR   30
#define BAR_DOWN     40
#define LIFTING_BAR  50
#define ALARMS_OFF   60
#define ERROR_UNKN   90

#define SENSIB       10

#define PIN_K1       8
#define PIN_K2       6
#define PIN_K3       7
#define PIN_IR       2
#define PIN_SERVO    5
#define PIN_LED1     A2
#define PIN_LED2     A3
#define PIN_LED3     A4
#define PIN_LED_PCB  A1
#define PIN_SOUND    3   // Must be 3: OC2B is pin3 - used for playing 8-bit WAV

#define HYSTER       30000  // Interval between alarms and barriers
#define DOWN_POS     2400
#define UP_POS       4000
#define INCR_SV      5
#define VEL_SER      1
#define BLINK_DEL    15  // Led blinking rate

byte sysEst = INIT;
boolean k1St, k2St, k3St, irSt, bellsOn, ledsOn;
volatile int posObj, posNow;
volatile boolean servMoving;

TMRpcm player;

void setup() {
  pinMode(PIN_K1, INPUT_PULLUP);
  pinMode(PIN_K2, INPUT_PULLUP);
  pinMode(PIN_K3, INPUT_PULLUP);
  pinMode(PIN_IR, INPUT_PULLUP);
  pinMode(PIN_LED1, OUTPUT);
  pinMode(PIN_LED2, OUTPUT);
  pinMode(PIN_SERVO, OUTPUT);
  pinMode(10, OUTPUT);
  //Serial.begin(9600);
  if (!SD.begin(10)) {
    // Serial.println("SD card error");
    return;
  }
  //Serial.println("SD card OK");

  cli();
  TCCR0A = 0;
  TCCR0B = 0;
  TCCR0B |= (1 << CS02) | (1 << CS00);      // Set CS00 & CS02 bits for 1024 prescaler on Timer0 - LEDs
  OCR0A = 234;                              // Comp Match every 15 ms  (66.67 Hz)
  TIMSK0 = 0;
  TIMSK0 |= (1 << OCIE0A);                   // Enable Timer0 interrupts for Compare Match A

  TCCR1A = 0;
  TCCR1B = 0;
  TCCR1A |= (1 << WGM11);                    // Turn on Fast PWM mode
  TCCR1B |= (1 << WGM13) | (1 << WGM12);     // Turn on Fast PWM mode
  TCCR1B |= (1 << CS11);                     // Set CS11 bit for 8 prescaler (0.5 µs) on Timer1 - Servos
  ICR1 = 40000;                              // Set PWM fixed frequency (overflow every 20 ms)
  TIMSK1 = 0;
  TIMSK1 |= (1 << OCIE1A) | (1 << TOIE1);    // Enable Timer1 interrupts for Compare Match A & Overflow mode
  sei();                                     // Activar interrupciones

  player.speakerPin = PIN_SOUND;
  player.setVolume(1);
  player.volume(1);
  player.loop(1);
  player.play("Campo3.wav");
  sysEst = DETECTING;
  posNow = UP_POS;
}

boolean readSensors() {
  static int cntr, k1Count, k2Count, k3Count, irCount;
  static boolean result;
  k1Count += digitalRead(PIN_K1);
  k2Count += digitalRead(PIN_K2);
  k3Count += digitalRead(PIN_K3);
  irCount += digitalRead(PIN_IR);
  if (++cntr == SENSIB) {
    if ((k1Count == 0)||(k2Count == 0)||(k3Count)||(irCount == 0)) result = true;
    else result = false;
    cntr = 0;
    k1Count = 0;
    k2Count = 0;
    k3Count = 0;
  }
  return result;
}

void loop() {
  static unsigned int cntr;
  static boolean detection = readSensors();
  switch (sysEst) {
    case DETECTING:
      if (detection) {
        sysEst = ALARMS_ON;
      }
      break;
    case ALARMS_ON:
      if (bellsOn == false) {
        player.play("Bells1.wav");
        bellsOn = true;
        ledsOn = true;
      }
      if (++cntr == HYSTER) {
        sysEst = LOWING_BAR;
        posObj = DOWN_POS;
        servMoving = true;
        cntr = 0;
      }
      break;
    case LOWING_BAR:
      if (servMoving == false) {
        sysEst = BAR_DOWN;
      }
      break;
    case BAR_DOWN:
      if (detection == false) {
        if (++cntr == HYSTER) {
          sysEst = LIFTING_BAR;
          posObj = UP_POS;
          servMoving = true;
          cntr = 0;
        }
      }
      break;
    case LIFTING_BAR:
      if (servMoving == false) {
        sysEst = ALARMS_OFF;
      }
      break;
    case ALARMS_OFF:
      if (++cntr == HYSTER) {
        player.play("Campo3.wav");
        ledsOn = false;
        bellsOn = false;
        PORTC &= 0xF3;
        cntr = 0;
        sysEst = DETECTING;
      }
      break;
    case ERROR_UNKN:
      ledsOn = true;
      break;
  }
}

ISR(TIMER0_COMPA_vect) {                     // Timer0 Compare Match interrupt every 15 ms
  static int cntr;
  if (ledsOn && (++cntr == BLINK_DEL)) {
    if (PINC & 0x04) (PORTC &= 0xF3) |= 0x08;  // Toggle pinA2 and pinA3 alternatively
    else (PORTC &= 0xF3) |= 0x04;
    cntr = 0;
  }
}

ISR(TIMER1_OVF_vect) {                       // Timer1 Overflow interrupt for servo every 20 ms
  static int dif;
  if (servMoving == true) {
    dif = posNow - posObj;
    if (dif < 0) posNow += INCR_SV;
    else if (dif > 0) posNow -= INCR_SV;
    else servMoving = false;
    PORTD |= (1 << 5);                       // Servo is pin5
    OCR1A = posNow;
  }
}

ISR(TIMER1_COMPA_vect) {                     // Timer1 Match Compare A interrupt for servos
  PORTD &= ~(1 << 5);
}

 

ADD-ONS (LIBRERIAS Y OTROS ARCHIVOS)

 

THE ADD-ONS (LIBRARIES AND OTHER FILES)

     

Este artículo muestra cómo convertir un simple Arduino Nano en un eficiente descodificador DCC para controlar hasta 8 señales de dos aspectos. El programa proporciona un bonito efecto de conmutación gradual, que apaga poco a poco la luz roja antes de encender progresivamente la verde, y al revés.

 

This page shows how to convert a simple Arduino Nano into an efficient DCC decoder for eight two aspect signals. The code is also able to perform a nice dimming effect, so red light fades slowly off before green light goes progressively on, and viceversa.

La lista de componentes es muy reducida, y todos ellos pueden conseguirse fácilmente:

 

The part list is quite reduced, and all components are easily found everywhere:

Lista de componentes

  • 1 x Arduino Nano 16 MHz 5V con USB
  • 1 x Optoacoplador 6N137
  • 1 x Led cualquiera
  • 3 x 10 kΩ resistencias de 1/8 W
  • Los conectores que se ven en las fotos (28 total)
  • 1 x placa de circuito impreso
   

Part list

  • 1 x Arduino Nano 16 MHz 5V with USB
  • 1 x Optocoupler 6N137
  • 1 x Led (any)
  • 3 x 10 kΩ resistor 1/8 W
  • Connectors shown in pictures (28 total)
  • 1 x printed circuit board

 

 

/* 2 x 8 dimming LEDs with Arduino Nano DCC decoder
   By German Trinidad and Norber, Spain, December 2015
   Freely distributable for private, non commercial, use

   Attach led anodes (+) to #1 to #8 outputs
   Attach all led catodes (-) to GNDs
   Don't forget to add serial resistors! */

#define ADR_MMAUS_01  129  // Assign here your addresses
#define ADR_MMAUS_02  130
#define ADR_MMAUS_03  131
#define ADR_MMAUS_04  132
#define ADR_MMAUS_05  133
#define ADR_MMAUS_06  134
#define ADR_MMAUS_07  135
#define ADR_MMAUS_08  136
#define ADR_MMAUS_09  137

byte comb[][4] = {
  //Address                      , Port                    , Pin-√ , Pin--
  {((ADR_MMAUS_01 - 1) / 4) + 128, ((ADR_MMAUS_01 - 1) % 4), 4     , 5   },
  {((ADR_MMAUS_02 - 1) / 4) + 128, ((ADR_MMAUS_02 - 1) % 4), 6     , 7   },
  {((ADR_MMAUS_03 - 1) / 4) + 128, ((ADR_MMAUS_03 - 1) % 4), 8     , 9   },
  {((ADR_MMAUS_04 - 1) / 4) + 128, ((ADR_MMAUS_04 - 1) % 4), 10    , 11  },
  {((ADR_MMAUS_05 - 1) / 4) + 128, ((ADR_MMAUS_05 - 1) % 4), 12    , 13  },
  {((ADR_MMAUS_06 - 1) / 4) + 128, ((ADR_MMAUS_06 - 1) % 4), 14    , 15  },
  {((ADR_MMAUS_07 - 1) / 4) + 128, ((ADR_MMAUS_07 - 1) % 4), 16    , 17  },
  {((ADR_MMAUS_08 - 1) / 4) + 128, ((ADR_MMAUS_08 - 1) % 4), 18    , 19  },
  {((ADR_MMAUS_09 - 1) / 4) + 128, ((ADR_MMAUS_09 - 1) % 4), 20    , 21  },
};

#define DCC_INT   2
#define SI        0
#define NO        1
#define TICKS    215           // Prescaler 8 -> Timer pulse every 0.5 µs -> 200 ticks are 100 µs
#define FREQ     120           // 15 ms aprox 
#define DELTA     3            // PWM
#define TO_OFF   20
#define PAUSE    30
#define TO_ON    40

volatile byte buf_dcc[6];
volatile byte pinPWM_1, pinPWM_2;
volatile boolean varyingLed = false;
volatile int duty = FREQ - 1;
volatile byte state = TO_OFF;

void setup() {
  for (int i = 4; i < 20; i++) {
    pinMode(i, OUTPUT);
    if (i & 0x01) digitalWrite(i, LOW);      // Green leds at odd pins start off
    else digitalWrite(i, HIGH);              // Red leds at even pins start on
  }
  pinMode(DCC_INT, INPUT);
  Serial.begin(9600);
  Serial.print("Iniciando...");
  delay(500);
  attachInterrupt(0, dcc_int, CHANGE);       // pin2 = DCC      externalInterrupt 0
  cli();
  TCCR0A = 0;
  TCCR0B = 0;
  TCCR0B |= (1 << CS01);                     // Set CS01 bit for 8 prescaler (0.5 µs) on Timer0 - DCC
  TCCR2A = 0;
  TCCR2B = 0;
  TCCR2B |= (1 << CS21);                     // Set CS21 bit for 8 prescaler (0.5 µs) on Timer2 - Dimming
  TIMSK2 = 0;
  TIMSK2 |= (1 << TOIE2);                    // Enable Timer2 Overflow interrupt
  sei();
}

void dcc_int() {                               // ISR(INT0_vect) External interrupt routine for signal on pin2
  static int tmp_pulse;                        // German is a genius!
  static byte bit_dcc;
  static byte preamb;
  static byte aux_dcc, x_or, idx, num_bits;

  if (PIND & (0x04)) TCNT0 = 0;
  else  {
    tmp_pulse = TCNT0;
    if (tmp_pulse > TICKS) bit_dcc = 0;
    else bit_dcc = 1;
    if (preamb == SI) {
      if (bit_dcc) {
        if (num_bits) num_bits--;
      }
      else {
        if (!num_bits) {
          preamb = NO;
          num_bits = 9;
          x_or = 0;
          idx = 0;
        }
        else num_bits = 10;
      }
    }
    else {
      if (--num_bits) aux_dcc = aux_dcc * 2 + bit_dcc;
      else {
        if (!bit_dcc) {
          buf_dcc [idx++] = aux_dcc;
          x_or ^= aux_dcc;
          num_bits = 9;
        }
        else {
          preamb = SI;
          num_bits = 10;
          if (x_or == aux_dcc) {                        // Data received OK!
           Serial.print(buf_dcc[0]);
            // buf_dcc[0] is Address
            // buf_dcc[1] is Port and command
            if (!varyingLed) {                          // While commuting, ignore commands
              byte port = (buf_dcc[1] & 0x06) >> 1;     // 0x06 = B0000 0110
              for (int i = 0; i < 9; i++) {
                if ((buf_dcc[0] == comb[i][0]) && (port == comb[i][1])) { // It's ours
                  if (buf_dcc[1] & 0x01) {
                    pinPWM_1 = comb[i][2];              // Pin to dim off
                    pinPWM_2 = comb[i][3];              // Pin to dim on
                  }
                  else {
                    pinPWM_1 = comb[i][3];
                    pinPWM_2 = comb[i][2];
                  }
                  if (digitalRead(pinPWM_1)) {          // Needs to be dimmed off indeed
                    varyingLed = true;
                    duty = 255;
                    state = TO_OFF;
                  }
                  return;
                }
              }
            }
          }
        }
      }
    }
  }
}

ISR(TIMER2_OVF_vect) {                         // Timer2 Overflow interrupt every 128 µs for dimmming leds
  if (varyingLed == true) {
    static int cont;
    cont++;
    switch (state) {
      case TO_OFF:
        if (cont == FREQ) {                    // High pulse starts when 'cont' reaches FREQ
          digitalWrite(pinPWM_1, HIGH);
          cont = 0;
          duty = duty - DELTA;
        }
        if (cont == duty) {
          digitalWrite(pinPWM_1, LOW);         // And ends when 'cont' reaches 'duty'
          if (duty < DELTA) state = PAUSE;
        }
        break;
      case PAUSE:
        if (cont == 5 * FREQ) {                 // Small dramatic pausae
          state = TO_ON;
          duty = DELTA;
          cont = 0;
        }
        break;
      case TO_ON:
        if (cont == FREQ) {
          digitalWrite(pinPWM_2, HIGH);
          cont = 0;
          duty = duty + DELTA;
          if (duty > 255 - DELTA) varyingLed = false;  // We're done
        }
        if (cont == duty) {
          digitalWrite(pinPWM_2, LOW);
        }
        break;
    }
  }
}

void loop() {
}

 

Prepare la placa de circuito impreso, suelde los componentes y programe el Arduino con el código de arriba (copiar y pegar en el Arduino IDE bastará).

 

Prepare the PCB, sold the components in place and program the Arduino whit the code below (just copy from this page and paste on Arduino IDE).

IMPORTANTE:

 

IMPORTANT:

No olvide añadir resistencias en serie con cada LED. Recomendamos resistencias de 1/8 W entre 2.2 kΩ y 33 kΩ, dependiendo de la eficiencia de los LEDs elegidos. Si no las coloca puede destruir para siempre el microcontrolador.

   

Don't forget to add resistors in series with each LED. We use 1/8 W resistors ranging from 2.2 kΩ to 33 kΩ, depending on the efficacity of the chosen LEDs. If you fail to add these resistors you may destroy the microcontroller for ever.

Este descodificador responderá a las direcciones de accesorios que escriba al principio del programa. Por ejemplo,  #define ADR_MMAUS_01 129 significa que la señal conectada en la posición #1 obedecerá a las órdenes dadas para el accesorio 129 y así.

  The accessory addresses this decoder will respond to are written at the beginning of the code. For instance, #define ADR_MMAUS_01 129 means that the signals attached to #1 will be controlled by address number 129 and so on.

Este programa ha sido diseñado para las centrales DCC de tipo Lenz / Roco Multimaus. Con otros sistemas las direcciones a programar pueden variar (pueden estar como adelantadas 4 posiciones, de forma que la #129 sería la #125 -o quizá es al revés... Nunca me acuerdo, lo siento!). Haga sus pruebas. Es facilísimo.

   

This code has been prepared for Lenz / Roco Multimaus type DCC command stations. With other systems the addresses may vary (they may be shifted 4 places down, so #129 is #125 -or maybe the opposite... I never remember, sorry!) . Do your tests. It's easy!

Las cookies facilitan la prestación de nuestros servicios. Al utilizar nuestros servicios, usted acepta que utilizamos cookies.
Más información De acuerdo