Grade Crossing...

So, if you have not been over to the real inventor of the original code, please do so now! ArduinoGate was built by using the avr-gcc compiler and a tool to upload the code into the Arduino, without using all the Arduino fancy tools, like the IDE, a Sketch and the Verify... and Upload... buttons. So, with a little bit of arm wrestling, or more a competition between two friends, we have it in an .ino sketch now. But, I think the second servo motor got hurt in the fight and neither did we bring the EEPROM (where you save the values for later) stuff over. But, since it is in a sketch now, you change the servo's start and stop positions and press Upload...

Ok, the SOUND problem first:

Getting your own sounds in the Arduino is easy, making the C data array with wav2c is also easy, the hard parts are a) getting your sound recorded or captured and b) getting it into a 8bit and 8kHz mono .wav file! Audacity might be your friend and before you File->Export Audio... (Ctrl+Shift+E), make sure the Project Rate is set to 8000 Hz in the bottom left of the screen:

And then Export it with a new filename as an "Other uncompressed files" type, but click on "Options..." first to use a WAV Header and an Unsigned 8 bit PCM Encoding (then OK and Save)

From here you run in a Command window: c:\>wav2c.exe WigWagDingDing.wav Something.c

With some code added to the .c file, you can compile and get the size of the array, or you can just go ahead and count all the numbers in the array. The WigWag's 2 seconds with two dings, was 16,025 data samples, which is is about right...8,000 Hz, 2 seconds, 16,000 data points!

#include <stdio.h>

const unsigned char pcm_samples[] = { 158,72,0,0,131,133,133,135,136,137,138,138,139,141,142,144,146,148,150,152,154,156,158,158, ...

int main() {

printf( "Size of your array:\n" );

printf( "WigWagDingDing: %d\n", sizeof( pcm_samples ) );

return 0;

} // int main

and then

c:\>gcc Something.c -o ShowSize.exe

and

c:\>ShowSize.exe

will show:

Size of your array:

WigWagDingDing: 16025

Now rename the Something.c to WigWagSound.h and you use the 16025 to add the #define SOUNDLEN line, also add the #define to pcm_samples and then remove ALL the newlines after pcm_samples, you want everything in the SOUNDDATA define to be on ONE line:

#define SOUNDLEN const int pcm_length=16025; // WigWagDingDing.wav

#define SOUNDDATA const unsigned char pcm_samples[] PROGMEM = { 239,61,0,0,135,76,113,176,124,...,};

Now you need to find your Arduino Library folder in your own [User] or [Home] folder and add a library name like "Sound" and put this .h file in there. If you have the Arduino IDE open, close it and start again.

Yes, that is a lot of work, for only 2 seconds of ding! Maybe we can rewrite the wav2c program to do exactly what we want, send me an email.

So, here is the program...use at your own risk:

/* * Arduino_Crossing_Gate_Controller.ino * * Created: 3/15/2012 4:25:31 PM, by Marty * Modified: 2015/08/06, by Gert * Authors: Gert Muller, Marty * Arduino Uno, ATMega328P 16MHz * * timer 0 = servo PWM OC0A, OC0B 1-2ms pulse every 24 ms * timer 1 = audio sample, realtime clock 8000Hz * timer 2 = audio PWM clock -- running at 16MHz/255 = 62kHz pwm period * * 9=pb1 = pulled low to indicate occupancy... * 10=pb2 = ... * 11=pb3 = OC2A audio PWM high bit... * 13=pb5 = LED on Arduino * * 14/A0=pc0 = gate2 led output * 15/A1=pc1 = flasher1-1 output... * 16/A2=pc2 = flasher1-2 output... * 17/A3=pc3 = gate1 led output * A4=pc4 = flasher2-1 output * A5=pc5 = flasher2-2 output * * 0=pd0 = serial input * 1=pd1 = serial output * 3=pd3 = OC2B audio PWM low bit... * 6=pd6 = OC0A servo1 pulse output .5 to 2.5ms every 24 ms * 5=pd5 = OC0B servo2 pulse output .5 to 2.5ms every 24 ms * * when pb1 or pb2 are pulled low, the servo moves the flashers start. 2 seconds later * the servo moves to max at the defined servo speed. * when pb0 goes high, the servo moves to min at the defined servo speed. Once the * servo is at min, the flashers stop. * * to enter program mode, ... sorry, we removed all that! (GM) */// In the process to put this into a .ino Sketch, we lost the EEPROM and programming of such by reading an Analog pin// We might also have lost the second servo motor, so please test before you ship! (GM)// The original code could be found doing search for ArduinoGate. // Since this code implements the ISRs and directly controls the timers 0, 1 and 2, you can not do a // #include <Servo.h>, you will get ...Servo.cpp:81: multiple definition of `__vector_11'// so, there goes most of your libraries that invloves timers.// This header file is inside a folder in the library locaiton, // example: [Drive:][User][Documents]\Arduino\libraries\WigWagSoundlibrary// First time you put the file there, you need to restart the Arduino IDE.// The file contains the sound data to output to the speaker://#define SOUNDLEN const int pcm_length=16025; // WigWagDingDing.wav//#define SOUNDDATA const unsigned char pcm_samples[] PROGMEM = { 239,61,0,0,135,76,113,176,124,133,118,173,128,102,190,105,108,... #include "WigWagSound.h"// and then we use the #define MARCOs here SOUNDLEN; // and here. This is the easiest to include and external file and not deal with the "scope" problem. SOUNDDATA; // inputsconst int PB01 = 9; const int PIN = 10; // flashersconst int PC00 = 14; const int PC01 = 15; const int PC02 = 16; const int PC03 = 17; const int PC04 = 18; const int PC05 = 19; // servosconst int PD05 = 5; const int PD06 = 6; // audioconst int PD03 = 3; const int PB03 = 11; // arduinoconst int PB05 = 13; const int ledPin = 13; #define DEL 1000 #define PULLUPS digitalRead(PB01) // ( 1 << PB1) | ( 1 << PB2 ) // pullups on pb1 #define FLASHERH11 digitalWrite(PC01, HIGH); #define FLASHERH12 digitalWrite(PC02, HIGH); #define FLASHERH21 digitalWrite(PC04, HIGH); #define FLASHERH22 digitalWrite(PC05, HIGH); #define FLASHERL11 digitalWrite(PC01, LOW); #define FLASHERL12 digitalWrite(PC02, LOW); #define FLASHERL21 digitalWrite(PC04, LOW); #define FLASHERL22 digitalWrite(PC05, LOW); #define GATE1LEDH digitalWrite(PC03, HIGH); #define GATE2LEDH digitalWrite(PC00, HIGH); #define GATE1LEDL digitalWrite(PC03, LOW); #define GATE2LEDL digitalWrite(PC00, LOW); #define FLASHERH1 FLASHERH11; FLASHERH12; #define FLASHERH2 FLASHERH21; FLASHERH22; #define FLASHERL1 FLASHERL11; FLASHERL12; #define FLASHERL2 FLASHERL21; FLASHERL22; #define SERVOH01 digitalWrite(PD06, HIGH); #define SERVOH02 digitalWrite(PD05, HIGH); #define SERVOL01 digitalWrite(PD06, LOW); #define SERVOL02 digitalWrite(PD05, LOW); #define GATE1LEDOFF GATE1LEDL; #define GATE1LEDON GATE1LEDH; #define GATE2LEDOFF GATE2LEDL; #define GATE2LEDON GATE2LEDH; #define GATELEDL GATE1LEDOFF;GATE2LEDOFF; #define GATELEDH GATE1LEDON;GATE2LEDON; #define DIV1024CLOCK ( 1 << CS00) | ( 1 << CS02) #define CLOCKSTOP 0 #define DIV64CLOCK ( 1 << CS00) | ( 1 << CS01) #define SERVOMIN 1 // minimum value for servo #define SERVOMAX 255 // maximum value for servo #define ALLLEDOFF GATE2LEDL; GATE1LEDL; FLASHERL11; FLASHERL12; FLASHERL21; FLASHERL22 #define OCCUPIED !digitalRead(PB01) // pb1 pulled low by occupancy detector #define VACANT digitalRead(PB01) // volatile unsigned int tickcount; volatile unsigned char fasttick; // 8 ticks = 1 millisec volatile unsigned int millisec; // increments once per millisec volatile unsigned char servoticks; // 192 ticks for each servo period volatile unsigned char flashers; volatile unsigned char seconds; volatile unsigned char servomoving; // 0 servo stopped, 1 servo moving volatile uint16_t sample; //pointer to current audio sample byteunsigned char servo1min; unsigned char servo1max; unsigned char servo2min; unsigned char servo2max; unsigned char servodelay; // realtime clock// Timer1 compare A interrupt 16e6/2000 = interrupt 8000 times per sec ( .000125sec) ISR( TIMER1_COMPA_vect ) { // 8000 times a second update the audio pwm with the next sample // if the flashers are going or we are decaying after the last ding if ( flashers == 1 || sample != 0 ) { OCR2A = pgm_read_byte( &pcm_samples[sample++] ); if ( sample > 2200 ) { // Normal bell repeats every .36 seconds if ( flashers == 1 ) { // if the bell is ringing, repeat back to the beginning. sample = 0; } else if ( sample > pcm_length ) { // at the last ding, let the bell decay as long as we have samples sample = 0; } } } else { if ( OCR2A > 0 ) OCR2A--; // fade to zero to eliminate the click at the end. } if ( servomoving ) { // servo control sends pulses once every .024 seconds if ( ++servoticks >= 192 ) { // every 24 milliseconds start sending the servo pulse. // a servo pulse is between 1 and 2 milliseconds long. We turn it on here // in 1 millisecond, we will start counter 0 to turn off the servo bits based on OCR0A and OCR0B // this gives us 255 possible servo positions /////PORTD |= SERVO1 | SERVO2; SERVOH01; SERVOH02; //TIMSK0 |= ( 1<<TOIE0 ); //enable the overflow interrupt so we can stop the timer as soon as it runs out. servoticks = 0; // reset the servo pulse counter } // after the servo bits have been on for 1 millisecond, start timer 0 counting up. When it // reaches the count specified in OCR0A and OCR0B we get an interrupt where we turn off the servo bit enough) if ( servoticks == 8 ) { // the servo bits have been on for 1 msec. start timer0 to turn them off // start timer 0 running at 250khz // send an interrupt when the counter matches our servo postion TCNT0 = 0; TIFR0 = ( 1<<OCF0A) | ( 1<<OCF0B ); // clear any pending interrupts ( writing 1 to the bits clears them for whatever reaszon) TIMSK0 = ( 1 << OCIE0A) | ( 1 << OCIE0B ); // enable the output compare interrupts } } // 8 fast ticks is 1 millisec // The millisec counter gets cleared by the delay subroutine, all we do here is increment it. if ( fasttick++ >= 8 ) { millisec++; fasttick=0; } // 4000 ticks = half a second if ( tickcount++ >= 4000 ) { //PORTB=( PORTB ^ FLASHER2) | PULLUPS; seconds++; // actually counts half seconds tickcount = 0; if ( flashers == 1 ) { // alternating flasher1, flasher2 GATELEDH; if ( seconds & 1 ) { FLASHERH1; FLASHERL2; } else { FLASHERL1; FLASHERH2; } } else { FLASHERL1; FLASHERL2; GATELEDL; } } } // ISR// timer 0 compare A -- turn off servo bit 1 at the end of the pulse ISR( TIMER0_COMPA_vect ) { SERVOL01 // make sure servo bit is off TIMSK0 &= ~( 1<<OCIE0A ); // disable compare A interrupt } // ISR// timer 0 compare B -- turn off servo bit 2 at the end of the pulse ISR( TIMER0_COMPB_vect ) { SERVOL02; // make sure servo bit is off TIMSK0 &= ~( 1 << OCIE0B ); // disable compare B interrupt } // ISR// deelay -- stop processing for N milliseconds.void deelay( unsigned int ticks ) { // set the millisecond tick timer to zero. Interrupts will increment this for us // at .001 seconds per tick. millisec = 0; while( millisec < ticks ) {}; } // deelay// toggle the arduino LED once per period mask secconds// 1 = once per second gates moving// 2 = once per 2 seconds occupied// 4 = once every 4 seconds vacantvoid flashled( unsigned char period ) { if ( seconds & period ) { digitalWrite( ledPin, HIGH ); } else { digitalWrite( ledPin, LOW ); } // if } // flashled// moveto -- move the servos to the given targetvoid moveto( unsigned char target1, unsigned char target2 ) { while( OCR0A != target1 || OCR0B != target2 ) { flashled( 1 ); // while moving the gates flash the arduino LED once per second servomoving = 1; // turn on the servo deelay( servodelay ); //. // move first servo //_delay_ms( 20 ); if ( OCR0A > target1 ) { OCR0A--; } // if if ( OCR0A < target1 ) { OCR0A++; } // if // move 2nd servo if ( OCR0B > target2 ) { OCR0B--; } // if if ( OCR0B < target2 ) { OCR0B++; } // if } // while servomoving = 0; //turn off the servo } // moveto// doprogram -- change the program settings// move the resistor full clockwise to start the programming.// The flashers will blink showing which value is being programmed// flasher1 = set servo1 minimum// flasher2 = set servo1 maximum// both flashers = set servo 1 min// gate led = set servo 2 max// gate led + flasher1 = set servo speed// press the occupancy button to move to the next program value. // after all program values have been set, the unit returns to normal mode// with the new values.void doprogram( void ) { // sorry, we took all the EEPROM stuff out, just upload the Sketch again, after changing the values! } // doprogram// given a bit on port C, turn on that LED for half a secondvoid testit( unsigned char thePin ) { digitalWrite( thePin, HIGH ); deelay( DEL ); digitalWrite( thePin, LOW ); deelay( DEL ); digitalWrite( thePin, HIGH ); } // testit/* initialise everything */void init_pins( void ) { // set up the data direction registers pinMode( PB01, INPUT_PULLUP ); pinMode( PC00, OUTPUT ); pinMode( PC01, OUTPUT ); pinMode( PC02, OUTPUT ); pinMode( PC03, OUTPUT ); pinMode( PC04, OUTPUT ); pinMode( PC05, OUTPUT ); pinMode( PD03, OUTPUT ); // audioL pinMode( PB03, OUTPUT ); // audioH pinMode( PD05, OUTPUT ); // servo02 pinMode( PD06, OUTPUT ); // servo01 pinMode( ledPin, OUTPUT ); // turn off all leds //PORTC = FLASHER11 | FLASHER12 | FLASHER21 | FLASHER22 | GATE1LED | GATE2LED; flashers = 0; if ( servodelay == 0 ) { servodelay = 1; } // if // set up timer 0 servo pulse timer // a servo pulse is 1 millisecond for 0 degrees and 2 milliseconds for 90 degrees. // the pulses are every 24 milliseconds. The servo pulse is started every 24 milliseconds // by the realtime clock ( timer1). Timer 0 compare match A and B are used to // set the length of the pulses TCCR0A = 0; // normal mode TCCR0B = DIV64CLOCK; // 250kHz OCR0A = servo1min; // start with the gates up OCR0B = servo2min; // set up the timer1 to provide the main realtime clock of 8000 ticks per second TCCR1A = 0; // Normal operation TCCR1B = ( 1<<WGM12 ) | ( 1 << CS10 ); // OCR1A CTC mode, divide by 1 clock ( 16mhz) OCR1A = 2000; // upper limit of counter, generate 8000 overflow interrupts per second TIMSK1 = ( 1 << OCIE1A ); // interrupt on output compare A // set up the analog to digital converter ADMUX = ( 1<<ADLAR ) // left adjust to get the high 8 bits ( just read ADCH | ( 1 << REFS0 ) // 5 volt reference | 6; // ADC6 input ( actually mux1 | mux2) ADCSRA = ( 1 << ADEN ) // Enable A/D conversion | ( 1 << ADSC) // start the conversion | ( 1 << ADATE) // enable auto conversion ( free running) | ( 1<<ADPS2) | ( 1<<ADPS0 ); // /32 Prescale // set up timer 2 for AUDIO pwm generation // clear OC0A on compare match // set OC0A at BOTTOM, non-inverting mode // Fast PWM, 8bit TCCR2A = ( 1<<COM2A1 ) | ( 1<<WGM21 ) | ( 1<<WGM20 ); // Fast PWM, 8bit // Prescaler: clk/1 = 16MHz // PWM frequency = 16MHz / ( 255 + 1) = 62.5kHz TCCR2B = ( 1<<CS20 ); // set initial duty cycle to zero */ OCR2A = 0; sei( ); // Enable interrupts deelay( 1000 ); // pause for a bit at startup to allow the ADC to rise above 0 } // init_pinsvoid setup( ) { init_pins( ); servodelay = 25; // on startup flash each led once servomoving = 1; // force gates to the top while LEDs are flashing testit( PC00 ); testit( PC01 ); testit( PC02 ); testit( PC03 ); servomoving = 0; // turn off the servo now that it is at the top } // setupvoid loop( ) { flashled( 4 ); // toggle the led while we are waiting // if we have occupancy, make the gates go down if ( OCCUPIED ) { flashers = 1; delay( 2000 ); //. // delay for 2 seconds after starting flashers moveto( servo1max, servo2max ); while( OCCUPIED ) flashled( 2 ); } // if OCCUPIED // if no occupancy, the gates go up. (might need some debouncing here, GM) if ( VACANT ) { moveto( servo1min, servo2min ); // turn off flashers flashers = 0; deelay( 1000 ); //. } // if VACANT } // loop

And then there is some confusion on the what is connected to the Arduino Nano and what is not, so to have it all clear in everyone's mind, here is the schematic for connecting an Arduino Nano, the one with the USB connector to a Servo, the DCC occupancy sensor and the LED's on the boom and in the hoods. You can either use the CTRL line to drive a second servo, or you can use one servo to move the boom down on the other side of the tracks. Latter case, you need to make sure the travel and start and stop positions are all similar and in the first case, you need to know that both servos are going to travel the same distance at the same speed, so again, you need to mechanically adjust.

A word about Common Anode (the high side of the LEDs are connected to 5V, and the other side to an Arduino pin) and Common Cathode (where the low side of the LEDs are connected together to GND and the high side goes to a pin), either is fine for what we do here. It is commonly known that most digital circuits can sink more current when the pin goes low (so CA has a little bit of an advantage over CC) than sourcing the current when the pin goes high. But, when you use CA to turn an LED on, you need to make the pin LOW, so LOW is ON and HIGH is OFF. Assuming you are not going to need 20 mA, either should be fine. When you decide to use CA, you need to flip all the flasher and gate lines after a #define from LOW to HIGH and HIGH to LOW (or change the FLASHERH22 to FLASHERL22, and same with FLASHERL22 to FLASHERH22):

#define FLASHERH22 digitalWrite(PC05, LOW);

#define FLASHERL11 digitalWrite(PC01, HIGH);

Whatever configuration you choose, please make sure you use a resistor for each LED. Different LEDs turn on at different voltages using different currents, so sharing a resistor is not advised. Unless you are using LEDs in series, and now what you are doing. My first suggestion is always: try a 1kOhm resistor if the supply is less than 6V.

The servo might not sit still at the point it is sent too, that is usually a lack of sufficient current, so remove the 5V wire from the Arduino and use a separate 5V power supply to connect to the servo's 5V and GND. Make sure the GND is still (also) connected to the Arduino, since the CTRL line needs a return path. This will quickly become apparent when you are using only the USB cable to power everything. You could also use the Vin pin (6V-12V) to power the Arduino and servo, but if you hear buzzing from the servo, same problem, since the regulator on-board is not providing enough current, so now you need another supply too, since it has to be 5V and not like the 6V-12V supply you used for the Arduino.

And here is the schematic (download the pdf attached below if you need to print this):

The simpleGate.ino show below does not have sound, but is using Servo.h. I have noticed that you need an external power supply to make a servo do it its thing properly. Vin takes 6 to 12Vdc!