/* 11/10/2021 IOM_UNO_Shedv2 incorporates the effects from IOM_UNO_Shedv1 Sound on Card 01 grinder 37 seconds 02 big arc welder 7 seconds 03 small arc weld 8 seconds 04 drill/machine 11 secnods 05 file 5 seconds 06 grinder 4 seconds 07 light hammer 4 seconds 08 hacksaw 4 secs 09 big machine 28 secs...could repeat 10 another big machine 11 secs to end 11 tool box 3 secs 12 seagulls boats 8m 48 secs 13 waterfall 3m 19 could repeat 14 single whistle 15 short whistle.horn 16 guard whistle 17 birds/country side 1m 50 18 short whistle 19 double short whistle 20 hydraulics 2m 33 21 ship/boat big horn 22 coaling 21 sec repeatable Commands to send to DCC++ EX base station for testing <1> = power on < a 25 0 > = accessory number 25 direction 0 < a 25 1 > = accessory number 25 direction 1 Pins used 2 = interrupt pin for decoder messages/circuit 3 software serial...MP3 player 4 software serial...MP3 player 5 Neopixels 6 arc welder */ //Set up NMRA DCC library #include "NmraDcc.h" NmraDcc Dcc ; DCC_MSG Packet ; //Serial UART WAV/MP3 player #include "SoftwareSerial.h" #define ARDUINO_RX 4//should connect to TX of the Serial MP3 Player module #define ARDUINO_TX 3//connect to RX of the module SoftwareSerial myMP3(ARDUINO_RX, ARDUINO_TX); byte sendBuffer[6]; //buffer that will be used to store commands before sending //Neopixels #include "Adafruit_NeoPixel.h" // Which pin on the Arduino is connected to the NeoPixels? #define neoPin 5 // How many NeoPixels are attached to the Arduino? #define NUMPIXELS 16 // 16 on my test strip Adafruit_NeoPixel pixels(NUMPIXELS, neoPin, NEO_GRB + NEO_KHZ800); //Arc Welder variables const int arcPin = 6;//arc welder Digital Pin int arcWeldState; unsigned long arcWeldSoundTimer; unsigned long arkEndFlashTimer; unsigned long arkFlashTimer; int arcSoundCounter; //Ash pit variables unsigned long ashSoundTimer; int ashSoundCounter; //4 neopixels for ash effects...each to operate independently unsigned long ashNeoPixelTimer[4]; int ashNeoPixelCounter[4] = {251, 251, 251, 251}; //values required for them to start //Other sound variables unsigned long soundChange; unsigned long soundEnd; int soundVolume; int soundStart; int playThisSound; int SoundOn = 1;//defaults to on // This function is called whenever a normal DCC Turnout Packet is received and we're in Output Addressing Mode void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint8_t OutputPower ) { Serial.print("notifyDccAccTurnoutOutput: ") ; Serial.print(Addr, DEC) ; Serial.print(','); Serial.println(Direction, DEC) ; // Lines below of no interest to me so commented out but left in to show original position // Serial.print(','); // Serial.println(OutputPower, HEX) ; //Add thr accessory decoders you want to use into the sketch switch (Addr) { //background workshop sounds case 25: if (Direction < 1) {//< a 25 0 > background sounds off SoundOn = 0; soundEnd = millis(); } else { //< a 25 1 > background sounds on SoundOn = 1; } break; //coal pit sounds case 26: if (Direction < 1) {//< a 26 0 > coal off ashSoundCounter = 20; soundEnd = millis(); } else { //< a 26 1 > coal on playThisSound = 17; soundStart = 0; soundEnd = 0; } break; //Any address not listed above...do nothing default: break; } } void setup() { Serial.begin(115200); Serial.println("IOM_UNO_Shedv2..."); // Setup which External Interrupt, the Pin it's associated with that we're using and enable the Pull-Up Dcc.pin(0, 2, 1);//Pin 2 is the interrupt pin on an UNO // Call the main DCC Init function to enable the DCC Receiver Dcc.init( MAN_ID_DIY, 10, CV29_ACCESSORY_DECODER | CV29_OUTPUT_ADDRESS_MODE, 0 ); Serial.println("DCC Init Done"); //serial mp3 player myMP3.begin(9600); delay(500);//allow everything to settle down //first we need to select the TF Card selectTFCard(); delay(100); //Play a test sound number 19 on card playSound(19);//double whistle test on start up delay(3000); //neopixels pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED) pixels.clear(); // Set all pixel colors to 'off' pixels.show(); //Test the neopixels for (int q = 0; q < 16; q++) { //pixel test pixels.setPixelColor(q, pixels.Color(0, 0 , 150)); //green pixels.show(); delay(100); } pixels.clear(); // Set all pixel colors to 'off' pixels.show(); //arc welder pinMode(arcPin, OUTPUT); //quick test of arc welder led digitalWrite(arcPin, HIGH); delay(500); digitalWrite(arcPin, LOW); } void loop() { // You MUST call the NmraDcc.process() method frequently from the Arduino loop() function for correct library operation Dcc.process(); backgroundSound(); } /////////////////////////////////////SerialWav commands////////////////////////////////////////// //Serial UART commands //Manual says the code for this is 7E 03 35 01 EF so load into buffer array void selectTFCard(){ sendBuffer[0] = 0x7E; sendBuffer[1] = 0x03; sendBuffer[2] = 0x35; sendBuffer[3] = 0x01; sendBuffer[4] = 0xEF; sendUARTCommand(); } void playSound(byte songNumber){ sendBuffer[0] = 0x7E; sendBuffer[1] = 0x04; sendBuffer[2] = 0x41; sendBuffer[3] = 0x00; sendBuffer[4] = songNumber; sendBuffer[5] = 0xEF; sendUARTCommand(); } //play a sound at a set volume, only seems to apply to root directory files void playSoundAtVolume(byte volume,byte songNumber){ sendBuffer[0] = 0x7E; sendBuffer[1] = 0x04; sendBuffer[2] = 0x31; sendBuffer[3] = volume; sendBuffer[4] = songNumber; sendBuffer[5] = 0xEF; sendUARTCommand(); } //loop sound 7E 04 33 00 01 EF void repeatSound(byte songNumber){ sendBuffer[0] = 0x7E; sendBuffer[1] = 0x04; sendBuffer[2] = 0x33; sendBuffer[3] = 0x00; sendBuffer[4] = songNumber; sendBuffer[5] = 0xEF; sendUARTCommand(); } //set the playback volume 7E 03 31 0F EF = set volume to 0x0F = 15 void setplayVolume(byte volume){ sendBuffer[0] = 0x7E; sendBuffer[1] = 0x03; sendBuffer[2] = 0x31; sendBuffer[3] = volume; sendBuffer[4] = 0xEF; sendUARTCommand(); } //stop the current track playing void stopSound(){ sendBuffer[0] = 0x7E; sendBuffer[1] = 0x02; sendBuffer[2] = 0x0E; sendBuffer[3] = 0xEF; sendUARTCommand(); } void sendUARTCommand(){ int q; for(q=0;q < sendBuffer[1] + 2;q++){ myMP3.write(sendBuffer[q]); } Serial.println("Commands Sent"); for(q=0;q < sendBuffer[1] + 2;q++){ Serial.println(sendBuffer[q],HEX); } delay(25);//stops odd commands being missed } ///////////////////////////////sound and light functions///////////////// //sound functions void backgroundSound(){ if(soundStart < 1 || millis() < soundEnd){ switch(playThisSound){ case 1 ... 2: arcWeld(); break; case 3: grinderSound(); break; case 4 ... 5: drillSound(); break; case 6 ... 9: fileSound(); break; case 10 ... 12: hammerSound(); break; case 13: bigMachineSound(); break; case 14 ... 16: toolboxSound(); break; case 17://triggered sound ashpit(); break; default: break; } } if(millis() > soundEnd){ playThisSound = random(30);//increase this value to get more time without sounds //playThisSound = random(14,18);//increase this value to get more time without sounds Serial.print("playThisSound: "); Serial.println(playThisSound); soundStart = 0; digitalWrite(arcPin, LOW); if(playThisSound > 16){ soundEnd = millis() + random(2000,20000);//take a 10 second break from playing sounds } //if sound is turned off just keep looping nothing if(SoundOn < 1){ playThisSound = 100;// soundEnd = millis() + random(2000,20000);//take a 10 second break from playing sounds } } } void toolboxSound(){ if(soundStart < 1){ soundStart = 1; soundVolume = 15; playSoundAtVolume(soundVolume, 11); //drill 4 seconds soundChange = millis() + 4000; soundEnd = millis() + 4000; } } void bigMachineSound(){ if(soundStart < 1){ soundStart = 1; soundVolume = 15; playSoundAtVolume(soundVolume, 9); // 28 seconds soundChange = millis() + 24000; soundEnd = millis() + 28000; } if(millis() > soundChange && millis() < soundEnd){ soundChange = millis() + 500; soundVolume -=1; if(soundVolume < 0){ soundVolume = 0; } setplayVolume(soundVolume); } } void hammerSound(){ if(soundStart < 1){ soundStart = 1; soundVolume = 15; playSoundAtVolume(soundVolume, 7); //drill 4 seconds soundChange = millis() + 5000; soundEnd = millis() + 5000; } } void fileSound(){ if(soundStart < 1){ soundStart = 1; soundVolume = 15; playSoundAtVolume(soundVolume, 5); //drill 5 seconds soundChange = millis() + 5000; soundEnd = millis() + 5000; } } void drillSound(){ if(soundStart < 1){ soundStart = 1; soundVolume = 15; playSoundAtVolume(soundVolume, 4); //drill 11 seconds soundChange = millis() + 11000; soundEnd = millis() + 11000; } } void grinderSound(){ if(soundStart < 1){ soundStart = 1; soundVolume = 15; playSoundAtVolume(soundVolume, 1); //grinder 37 seconds soundChange = millis() + 32000; soundEnd = millis() + 37000; } if(millis() > soundChange && millis() < soundEnd){ soundChange = millis() + 500; soundVolume -=1; if(soundVolume < 0){ soundVolume = 0; } setplayVolume(soundVolume); } } void arcWeld() { if(soundStart < 1){ soundStart = 1; soundEnd = millis() + 40000; arcSoundCounter = 0; } if (millis() > arcWeldSoundTimer && arcSoundCounter < 5) { playSoundAtVolume(15, 2); arkEndFlashTimer = millis() + 7000;//resets the flash timer arcSoundCounter++; arcWeldSoundTimer = millis() + random(7000, 10000); } if (millis() < arkEndFlashTimer) { if (millis() > arkFlashTimer) { arkFlashTimer = millis() + random(10, 40); //next change state if (arcWeldState > 0) { arcWeldState = 0; digitalWrite(arcPin, LOW); } else { arcWeldState = 1; digitalWrite(arcPin, HIGH); } } } else { digitalWrite(arcPin, LOW); } } //handles 4 neopixels for falling ash plus coal filling sound void ashpit() { int q; if (millis() > ashSoundTimer && ashSoundCounter < 20) { soundStart = 1; playSoundAtVolume(15, 22); ashSoundCounter++; ashSoundTimer = millis() + random(21000, 30000); soundEnd = ashSoundTimer + 5000; } if(ashSoundCounter == 20 && soundStart > 0){ soundStart = 0; soundEnd = millis() + 5000; pixels.clear(); pixels.show(); } //cycle through the neopixels if (ashNeoPixelCounter[0] == 0 && ashSoundCounter < 10) { for (q = 0; q < 4; q++) { ashNeoPixelCounter[q] = 251; ashNeoPixelTimer[q] = millis() + random(1000, 1200); } } if (ashSoundCounter > 15) { //All ash should be cold by now for (q = 0; q < 4; q++) { pixels.setPixelColor(q, pixels.Color(0, 0, 0)); pixels.show(); } } for (q = 0; q < 4; q++) { if (millis() > ashNeoPixelTimer[q] && ashNeoPixelCounter[q] > -1) { switch (ashNeoPixelCounter[q]) { case 0 ... 24: pixels.setPixelColor(q, pixels.Color(ashNeoPixelCounter[q], 0, 0)); //red break; case 25 ... 49: pixels.setPixelColor(q, pixels.Color(ashNeoPixelCounter[q], 1, 0)); //red break; case 50 ... 74: pixels.setPixelColor(q, pixels.Color(ashNeoPixelCounter[q], 2, 0)); //red break; case 75 ... 99: pixels.setPixelColor(q, pixels.Color(ashNeoPixelCounter[q], 3, 0)); //red break; case 100 ... 124: pixels.setPixelColor(q, pixels.Color(ashNeoPixelCounter[q], 4, 0)); //red break; case 125 ... 149: pixels.setPixelColor(q, pixels.Color(ashNeoPixelCounter[q], 5, 0)); //red break; case 150 ... 174: pixels.setPixelColor(q, pixels.Color(ashNeoPixelCounter[q], 6, 0)); //red break; case 175 ... 199: pixels.setPixelColor(q, pixels.Color(ashNeoPixelCounter[q], 7, 0)); //red break; case 200 ... 224: pixels.setPixelColor(q, pixels.Color(ashNeoPixelCounter[q], 8, 0)); //red break; case 225 ... 250: pixels.setPixelColor(q, pixels.Color(ashNeoPixelCounter[q], 9, 0)); //red break; default: pixels.setPixelColor(q, pixels.Color(0, 0, 0)); //red break; } if (random(0, 10) > 7) { pixels.setPixelColor(q, pixels.Color(0, 0, 0)); //red } pixels.show(); ashNeoPixelCounter[q]--; ashNeoPixelTimer[q] = millis() + random(5, 50); } } }