Rave Cave Control Panel

Project Status: Under Construction

My boy has a bunk-bed, the bottom half doesn’t have a bed, its boarded out and surrounded by camo net. Its also got lights in it. I want a control panel to control the following:

  • The existing lights
  • Some audio samples
  • Some video loops to playback in a porthole
  • Some addressable LEDs.

The existing lights are:

  • Fairy lights (multi-colour)
  • Laser (red and green)
  • RGB Flood (with a IR remote control)

At the moment he controls these by powering them on with a switched extension block attached to the headboard.

Existing Lights

Option 1

Make a single box that attaches to the headboard that contains all of the plugs and sockets, has one power inlet and presents only the switches in a dashboard.

Option 2

Make two boxes, one with the plugs and sockets and then a but of flexible conduit running to a much smaller box with just the switches.

Components

Sockets
https://www.toolstation.com/axiom-low-profile-unswitched-socket/p10119

Dry Boxes
https://www.toolstation.com/axiom-dry-lining-box/p78122

Update

So I have a better idea that is more flexible. The control panel will have the power and switches but will then have outlet sockets that I can connect extension leads to.

https://www.aliexpress.com/item/4000381014457.html

These outlets will be controlled by toggle switches on the control panel.

https://www.aliexpress.com/item/32800984154.html

Each outlet will be connected to a single domestic 13 Amp socket.

https://www.aliexpress.com/item/10000046026506.html

I know I need 3 already so I’l probably wire up 4, no harm in having a spare and they will be easy to wire up.

Porthole Videos

So far my ideas for the views are:

Aquarium
https://www.youtube.com/watch?v=zJ7hUvU-d2Q

Space Travel
https://www.youtube.com/watch?v=XeUP83wRhjQ
https://www.youtube.com/watch?v=nGnX6GkrOgk
https://www.youtube.com/watch?v=bZNFRIwlQxQ
https://www.youtube.com/watch?v=NtOwzU5Rpp8 (Short Loop)

Earth Orbit / ISS
https://www.youtube.com/watch?v=Xjs6fnpPWy4

Polar / Winter Landscape
https://www.youtube.com/watch?v=9NmeAQruCgs
https://www.youtube.com/watch?v=fIrfF08azwc

Desert Landscape
https://www.youtube.com/watch?v=fvRkWO5g2HE
https://www.youtube.com/watch?v=1mexsOOHBSA (Short Loop)

Nature FlyBy
https://www.youtube.com/watch?v=BHACKCNDMW8

SciFi City
https://www.youtube.com/watch?v=Uj8DYegtrHg

Autumn
https://www.youtube.com/watch?v=oOhMGdFbYvU (Short Loop)
https://www.youtube.com/watch?v=eGD-5jYR9jk (Short Loop)

Sunset
https://www.youtube.com/watch?v=A9cHouJRGsU (Short Loop)

Clouds
https://www.youtube.com/watch?v=0_jNjpVxUt0 (Short Loop)
https://www.youtube.com/watch?v=rwfqnsK2M_s (Short Loop)

Porthole Components

I have some old spare computer screens, I have a Pi that I’m not currently using (so-long PiBot) and I’ve tracked down some instructions that could be good….

https://www.instructables.com/id/Play-Video-With-Python-and-GPIO/
https://www.raspberrypi.org/forums/viewtopic.php?t=101359

I like the first set of instructions but need to rejig them slightly so that its a momentary button press that triggers them or a rotary switch(?).

The thing that I am definitely missing is a porthole

Porthole Build Process

So it looks as thought the loop videos are going to work best, I’ve noted which are which above but I’ll have more of a play and find out whats happening. Now I just need to find some momentary switches…. to AliExpress!

Porthole Code so Far

This gives me 5 video buttons and an exit button. Currently you have to manually launch the code from a terminal window and the exit button only returns you to the Pi desktop.

Expand the link below to see the current code or better yet go to the public GitHub repository for the latest version.

https://github.com/IceniDesign/RaveCaveControls

Video Code So Far
#import the needed libraries
import os
import sys
import time
import subprocess
import RPi.GPIO as GPIO

#Set GPIO pin format
GPIO.setmode(GPIO.BCM)

#Setup the GPIO buttons
GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_UP) #YELLOW
GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP) #RED
GPIO.setup(24, GPIO.IN, pull_up_down=GPIO.PUD_UP) #WHITE
GPIO.setup(27, GPIO.IN, pull_up_down=GPIO.PUD_UP) #BLUE
GPIO.setup(22, GPIO.IN, pull_up_down=GPIO.PUD_UP) #GREEN
GPIO.setup(23, GPIO.IN, pull_up_down=GPIO.PUD_UP) #BLACK

#VARIABLES
movie1 = ("/home/pi/Videos/Loop_Videos/Space_u9TuAZHeziw_240p.mp4")
movie2 = ("/home/pi/Videos/Loop_Videos/Clouds_0_jNjpVxUt0_240p.mp4")
movie3 = ("/home/pi/Videos/Loop_Videos/Fish_A2WqvBVCUJQ_360p.mp4")
movie4 = ("/home/pi/Videos/Loop_Videos/Desert_1mexsOOHBSA_240p.mp4")
movie5 = ("/home/pi/Videos/Loop_Videos/Sunset_A9cHouJRGsU_240p.mp4")
Layer = 1 #this needs to be presented as a string not a variable
OMX1 = '' #Define them early so we can try and kill them even if they aren't running
OMX1_active = False
OMX2 = '' #Define them early so we can try and kill them even if they aren't running
OMX2_active = False
OMX3 = '' #Define them early so we can try and kill them even if they aren't running
OMX3_active = False
OMX4 = '' #Define them early so we can try and kill them even if they aren't running
OMX4_active = False
OMX5 = '' #Define them early so we can try and kill them even if they aren't running
OMX5_active = False

#and off we go...
os.system('killall omxplayer.bin') #kill OMX in case it is already running
#Space = subprocess.Popen(['omxplayer', '--loop', '--win', '600,100,900,400', '--layer', str(Layer), movie2],stdin=subprocess.PIPE) #Load the 1st video
OMX1_active = True
OMX1 = subprocess.Popen(['omxplayer', '--loop', '--win', '600,100,900,400', '--layer', str(Layer), movie1],stdin=subprocess.PIPE) #Load the 1st video
        
while True:
    #Read states of the inputs
    button1 = GPIO.input(17) #Yellow
    button2 = GPIO.input(18) #Red
    button3 = GPIO.input(27) #Blue
    button4 = GPIO.input(22) #Green
    button5 = GPIO.input(23) #Black
    quit_video = GPIO.input(24) #White

    #If GPIO(17) is shorted to ground
    if button1 == False: #If button1 is pressed...
        if OMX1_active == False: #And movie1 is not already running
            #Play movie1
            Layer = Layer + 1
            OMX1_active = True
            OMX1 = subprocess.Popen(['omxplayer', '--loop', '--win', '600,100,900,400', '--layer', str(Layer), movie1],stdin=subprocess.PIPE) #Load the 1st video
            time.sleep(5)
            if OMX2_active == True: #shutdown movie2 if it is playing
                OMX2.stdin.write('q')
                OMX2.stdin.flush()
                OMX2_active = False
            if OMX3_active == True: #shutdown movie3 if it is playing
                OMX3.stdin.write('q')
                OMX3.stdin.flush()
                OMX3_active = False
            if OMX4_active == True: #shutdown movie4 if it is playing
                OMX4.stdin.write('q')
                OMX4.stdin.flush()
                OMX4_active = False
            if OMX5_active == True: #shutdown movie4 if it is playing
                OMX5.stdin.write('q')
                OMX5.stdin.flush()
                OMX5_active = False

    elif button2 == False: #ElseIf button2 is pressed...
        if OMX2_active == False: #And movie1 is not already running
            #Play movie2
            Layer = Layer + 1
            OMX2_active = True
            OMX2 = subprocess.Popen(['omxplayer', '--loop', '--win', '600,100,900,400', '--layer', str(Layer), movie2],stdin=subprocess.PIPE) #Load the 1st video
            time.sleep(5)
            if OMX1_active == True: #shutdown movie1 if it is playing
                OMX1.stdin.write('q')
                OMX1.stdin.flush()
                OMX1_active = False
            if OMX3_active == True: #shutdown movie3 if it is playing
                OMX3.stdin.write('q')
                OMX3.stdin.flush()
                OMX3_active = False
            if OMX4_active == True: #shutdown movie4 if it is playing
                OMX4.stdin.write('q')
                OMX4.stdin.flush()
                OMX4_active = False
            if OMX5_active == True: #shutdown movie4 if it is playing
                OMX5.stdin.write('q')
                OMX5.stdin.flush()
                OMX5_active = False
                
    elif button3 == False: #ElseIf button2 is pressed...
        if OMX3_active == False: #And movie1 is not already running
            #Play movie2
            Layer = Layer + 1
            OMX3_active = True
            OMX3 = subprocess.Popen(['omxplayer', '--loop', '--win', '600,100,900,400', '--layer', str(Layer), movie3],stdin=subprocess.PIPE) #Load the 1st video
            time.sleep(5)
            if OMX1_active == True: #shutdown movie1 if it is playing
                OMX1.stdin.write('q')
                OMX1.stdin.flush()
                OMX1_active = False
            if OMX2_active == True: #shutdown movie3 if it is playing
                OMX2.stdin.write('q')
                OMX2.stdin.flush()
                OMX2_active = False
            if OMX4_active == True: #shutdown movie4 if it is playing
                OMX4.stdin.write('q')
                OMX4.stdin.flush()
                OMX4_active = False
            if OMX5_active == True: #shutdown movie4 if it is playing
                OMX5.stdin.write('q')
                OMX5.stdin.flush()
                OMX5_active = False
                
    elif button4 == False: #ElseIf button2 is pressed...
        if OMX4_active == False: #And movie1 is not already running
            #Play movie2
            Layer = Layer + 1
            OMX4_active = True
            OMX4 = subprocess.Popen(['omxplayer', '--loop', '--win', '600,100,900,400', '--layer', str(Layer), movie4],stdin=subprocess.PIPE) #Load the 1st video
            time.sleep(5)
            if OMX1_active == True: #shutdown movie1 if it is playing
                OMX1.stdin.write('q')
                OMX1.stdin.flush()
                OMX1_active = False
            if OMX2_active == True: #shutdown movie3 if it is playing
                OMX2.stdin.write('q')
                OMX2.stdin.flush()
                OMX2_active = False
            if OMX3_active == True: #shutdown movie4 if it is playing
                OMX3.stdin.write('q')
                OMX3.stdin.flush()
                OMX3_active = False
            if OMX5_active == True: #shutdown movie4 if it is playing
                OMX5.stdin.write('q')
                OMX5.stdin.flush()
                OMX5_active = False
                
    elif button5 == False: #ElseIf button2 is pressed...
        if OMX5_active == False: #And movie1 is not already running
            #Play movie2
            Layer = Layer + 1
            OMX5_active = True
            OMX5 = subprocess.Popen(['omxplayer', '--loop', '--win', '600,100,900,400', '--layer', str(Layer), movie5],stdin=subprocess.PIPE) #Load the 1st video
            time.sleep(5)
            if OMX1_active == True: #shutdown movie1 if it is playing
                OMX1.stdin.write('q')
                OMX1.stdin.flush()
                OMX1_active = False
            if OMX2_active == True: #shutdown movie4 if it is playing
                OMX2.stdin.write('q')
                OMX2.stdin.flush()
                OMX2_active = False
            if OMX3_active == True: #shutdown movie3 if it is playing
                OMX3.stdin.write('q')
                OMX3.stdin.flush()
                OMX3_active = False
            if OMX4_active == True: #shutdown movie4 if it is playing
                OMX4.stdin.write('q')
                OMX4.stdin.flush()
                OMX4_active = False
                
    #If GPIO(24) is shorted to ground
    if quit_video == False:
        os.system('killall omxplayer.bin')
        exit()

Porthole Code Improvements…

  • The script should fire up as the Pi loads up
  • The exit button should be a hidden button for troubleshooting
  • There should be a flag to set if the videos are full screen or window for debugging
  • There is a lot of repetitive code that could be written out especially if this can go up to 20 videos!
  • There should be a shutdown button that closes the videos and shuts down the Pi
  • The last one is a big one……. should this be a flight between destinations? As in should pressing a button play a take-off video / space video / landing video? At the moment it just loads whichever loop you select with no transition.

Porthole Draft Hardware

First I’ve soldered jumper cables and heat-shrink onto the switches. The cables are colour coded to make it easier to work out which is which when I’m looking at the other side.

Then I quickly knocked up a draft box to hold the switches while I’m doing my prototyping. I’ve made it over large so I can add additional switches.

Here’s the first draft. I should have thought this through more as it doesn’t quite fit the Pi in there as well! Oh well its only a draft and there will be a shiney box coming soon anyway.

Porthole Hardware Improvement Ideas

  • Larger slimmer case, probably based on the skeleton of a rack mount network switch (I work in IT, they are free for me!)
  • More switches
  • More lights
  • Add the Pi into the case
  • Add the unused ports as external ports to the box (power, 2x USB, Network, Audio, HDMI)

1st Round of Improvements

Now all my shiny switches are mounted in an old piece of networking hardware that was broken. I work in IT so stuff like this is easy to obtain for free but check out my scrounging post if you want some ideas on getting free stuff!
https://icenidesign.com/scrounging-and-on-the-cheap/

The twelve switches down the left-hand side don’t currently do anything but my idea is to have them control audio clips (robots and dinosaurs is the boys request!)

The holes for the switches were all cut using a stepped drill bit which are awesome!

They are fairly cheap and definitely worth the investment.

I bought a stand-off kit from ebay to mount various electronics to the inside of the box! This project is starting to look expensive but I’ve pretty much got enough parts to build 10 of them! The kit that I bought is no longer available but if you google “standoff kit” there are loads out there.

One problem I have is that I’m mounting the hardware to the bottom of the case and the switches to the top of the case. So I’ve decided to link the top of the case to the bottom of the case with a few 9-pin serial cables. i decided on 9-pin as that is a standard more likely to be of use in the future, its cheap and I can run multiple cables when needed. I could have gone for 25 pin but they are really bulky.

I’ll probably end up mounting 3 or 4 of these on the top of the case and 3 or 4 on the bottom of the case. They are really cheap!
https://www.aliexpress.com/item/32838512514.html?spm=a2g0s.9042311.0.0.4d734c4dx3t83z

My idea is to have one connector for each group of buttons (6 live + 1 GND for each set, leaves 2 spare pins, possibly for led lights).

Addressable LEDs

So the next bits I wanted to do was to also have some addressable LED lights connected with another batch of buttons on there for more controls.

I’ve picked up a few bits to play with…

https://www.aliexpress.com/item/32241249128.html?spm=a2g0s.9042311.0.0.27424c4d2OeBw9


https://www.aliexpress.com/item/32682015405.html?spm=a2g0s.9042311.0.0.27424c4d2OeBw9

Some Reading to Do

https://www.arduino.cc/en/tutorial/button

Turns out that the Arduino page has some really good tutorials.

Addressable LED Code Attempt 1

Here’s my first attempt at Arduino coding with FastLED, 6 buttons controlling a strip of RGB addressable LEDs

Expand the link below to see the current code or better yet go to the public GitHub repository for the latest version.

https://github.com/IceniDesign/RaveCaveControls

Addressable LEDs Code So Far
#include <FastLED.h>

#define buttonPin_BLACK   2
#define buttonPin_WHITE   3
#define buttonPin_RED     4
#define buttonPin_GREEN   5
#define buttonPin_BLUE    6
#define buttonPin_YELLOW  7
#define LED_PIN     12
#define NUM_LEDS    30
#define BRIGHTNESS  20
#define LED_TYPE    WS2812B
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];

#define UPDATES_PER_SECOND 100

// variables will change:
int buttonState_BLACK = 0;        // variable for reading the pushbutton status
int buttonState_WHITE = 0;        // variable for reading the pushbutton status
int buttonState_RED = 0;        // variable for reading the pushbutton status
int buttonState_GREEN = 0;        // variable for reading the pushbutton status
int buttonState_BLUE = 0;        // variable for reading the pushbutton status
int buttonState_YELLOW = 0;        // variable for reading the pushbutton status

// This example shows several ways to set up and use 'palettes' of colors
// with FastLED.
//
// These compact palettes provide an easy way to re-colorize your
// animation on the fly, quickly, easily, and with low overhead.
//
// USING palettes is MUCH simpler in practice than in theory, so first just
// run this sketch, and watch the pretty lights as you then read through
// the code.  Although this sketch has eight (or more) different color schemes,
// the entire sketch compiles down to about 6.5K on AVR.
//
// FastLED provides a few pre-configured color palettes, and makes it
// extremely easy to make up your own color schemes with palettes.
//
// Some notes on the more abstract 'theory and practice' of
// FastLED compact palettes are at the bottom of this file.



CRGBPalette16 currentPalette;
TBlendType    currentBlending;

extern CRGBPalette16 myRedWhiteBluePalette;
extern const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM;


void setup() {
    delay( 3000 );                // power-up safety delay
    pinMode(buttonPin_BLACK, INPUT);    // initialize the pushbutton pin as an input:
    pinMode(buttonPin_WHITE, INPUT);    // initialize the pushbutton pin as an input:
    pinMode(buttonPin_RED, INPUT);    // initialize the pushbutton pin as an input:
    pinMode(buttonPin_GREEN, INPUT);    // initialize the pushbutton pin as an input:
    pinMode(buttonPin_BLUE, INPUT);    // initialize the pushbutton pin as an input:
    pinMode(buttonPin_YELLOW, INPUT);    // initialize the pushbutton pin as an input:
    FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
    FastLED.setBrightness(  BRIGHTNESS );
    
    currentPalette = RainbowColors_p;
    currentBlending = LINEARBLEND;
}


void loop()
{
    // read the state of the pushbutton values:
    buttonState_BLACK = digitalRead(buttonPin_BLACK);
    buttonState_WHITE = digitalRead(buttonPin_WHITE);
    buttonState_RED = digitalRead(buttonPin_RED);
    buttonState_GREEN = digitalRead(buttonPin_GREEN);
    buttonState_BLUE = digitalRead(buttonPin_BLUE);
    buttonState_YELLOW = digitalRead(buttonPin_YELLOW);

    // check if the pushbutton is pressed. If it is, the buttonState is HIGH:
    if (buttonState_BLACK == HIGH) {
      currentPalette = PartyColors_p;           
      currentBlending = LINEARBLEND;
    
      static uint8_t startIndex = 0;
      startIndex = startIndex + 1; /* motion speed */
    
      FillLEDsFromPaletteColors( startIndex);
    
      FastLED.show();
      FastLED.delay(1000 / UPDATES_PER_SECOND);
    }
    else if (buttonState_WHITE == HIGH) {
      currentPalette = RainbowStripeColors_p;   
      currentBlending = NOBLEND;
    
      static uint8_t startIndex = 0;
      startIndex = startIndex + 1; /* motion speed */
    
      FillLEDsFromPaletteColors( startIndex);
    
      FastLED.show();
      FastLED.delay(1000 / UPDATES_PER_SECOND);
    }
    else if (buttonState_RED == HIGH) {
      SetupBlackAndRedStripedPalette();  
      currentBlending = LINEARBLEND;
    
      static uint8_t startIndex = 0;
      startIndex = startIndex + 1; /* motion speed */
    
      FillLEDsFromPaletteColors( startIndex);
    
      FastLED.show();
      FastLED.delay(1000 / UPDATES_PER_SECOND);
    }
    else if (buttonState_GREEN == HIGH) {
      SetupPurpleAndGreenPalette();           
      currentBlending = LINEARBLEND;
    
      static uint8_t startIndex = 0;
      startIndex = startIndex + 1; /* motion speed */
    
      FillLEDsFromPaletteColors( startIndex);
    
      FastLED.show();
      FastLED.delay(1000 / UPDATES_PER_SECOND);
    }
    else if (buttonState_BLUE == HIGH) {
      currentPalette = CloudColors_p;           
      currentBlending = LINEARBLEND;
    
      static uint8_t startIndex = 0;
      startIndex = startIndex + 1; /* motion speed */
    
      FillLEDsFromPaletteColors( startIndex);
    
      FastLED.show();
      FastLED.delay(1000 / UPDATES_PER_SECOND);
    }
    else if (buttonState_YELLOW == HIGH) {
      currentPalette = myRedWhiteBluePalette_p; 
      currentBlending = LINEARBLEND; 
    
      static uint8_t startIndex = 0;
      startIndex = startIndex + 1; /* motion speed */
    
      FillLEDsFromPaletteColors( startIndex);
    
      FastLED.show();
      FastLED.delay(1000 / UPDATES_PER_SECOND);
    }
}

void FillLEDsFromPaletteColors( uint8_t colorIndex)
{
    uint8_t brightness = 255;
    
    for( int i = 0; i < NUM_LEDS; i++) {
        leds[i] = ColorFromPalette( currentPalette, colorIndex, brightness, currentBlending);
        colorIndex += 3;
    }
}


// There are several different palettes of colors demonstrated here.
//
// FastLED provides several 'preset' palettes: RainbowColors_p, RainbowStripeColors_p,
// OceanColors_p, CloudColors_p, LavaColors_p, ForestColors_p, and PartyColors_p.
//
// Additionally, you can manually define your own color palettes, or you can write
// code that creates color palettes on the fly.  All are shown here.

void ChangePalettePeriodically()
{
    uint8_t secondHand = (millis() / 1000) % 60;
    static uint8_t lastSecond = 99;
    
    if( lastSecond != secondHand) {
        lastSecond = secondHand;
        if( secondHand ==  0)  { currentPalette = RainbowColors_p;         currentBlending = LINEARBLEND; }
        if( secondHand == 10)  { currentPalette = RainbowStripeColors_p;   currentBlending = NOBLEND;  }
        if( secondHand == 15)  { currentPalette = RainbowStripeColors_p;   currentBlending = LINEARBLEND; }
        if( secondHand == 20)  { SetupPurpleAndGreenPalette();             currentBlending = LINEARBLEND; }
        if( secondHand == 25)  { SetupTotallyRandomPalette();              currentBlending = LINEARBLEND; }
        if( secondHand == 30)  { SetupBlackAndWhiteStripedPalette();       currentBlending = NOBLEND; }
        if( secondHand == 35)  { SetupBlackAndWhiteStripedPalette();       currentBlending = LINEARBLEND; }
        if( secondHand == 40)  { currentPalette = CloudColors_p;           currentBlending = LINEARBLEND; }
        if( secondHand == 45)  { currentPalette = PartyColors_p;           currentBlending = LINEARBLEND; }
        if( secondHand == 50)  { currentPalette = myRedWhiteBluePalette_p; currentBlending = NOBLEND;  }
        if( secondHand == 55)  { currentPalette = myRedWhiteBluePalette_p; currentBlending = LINEARBLEND; }
    }
}

// This function fills the palette with totally random colors.
void SetupTotallyRandomPalette()
{
    for( int i = 0; i < 16; i++) {
        currentPalette[i] = CHSV( random8(), 255, random8());
    }
}

// This function sets up a palette of black and white stripes,
// using code.  Since the palette is effectively an array of
// sixteen CRGB colors, the various fill_* functions can be used
// to set them up.
void SetupBlackAndWhiteStripedPalette()
{
    // 'black out' all 16 palette entries...
    fill_solid( currentPalette, 16, CRGB::Black);
    // and set every fourth one to white.
    currentPalette[0] = CRGB::White;
    currentPalette[4] = CRGB::White;
    currentPalette[8] = CRGB::White;
    currentPalette[12] = CRGB::White;
    
}

// This function sets up a palette of black and red stripes,
// using code.  Since the palette is effectively an array of
// sixteen CRGB colors, the various fill_* functions can be used
// to set them up.
void SetupBlackAndRedStripedPalette()
{
    // 'black out' all 16 palette entries...
    fill_solid( currentPalette, 16, CRGB::Black);
    // and set every fourth one to white.
    currentPalette[0] = CRGB::Red;
    currentPalette[4] = CRGB::Red;
    currentPalette[8] = CRGB::Red;
    currentPalette[12] = CRGB::Red;
    
}

// This function sets up a palette of purple and green stripes.
void SetupPurpleAndGreenPalette()
{
    CRGB purple = CHSV( HUE_PURPLE, 255, 255);
    CRGB green  = CHSV( HUE_GREEN, 255, 255);
    CRGB black  = CRGB::Black;
    
    currentPalette = CRGBPalette16(
                                   green,  green,  black,  black,
                                   purple, purple, black,  black,
                                   green,  green,  black,  black,
                                   purple, purple, black,  black );
}


// This example shows how to set up a static color palette
// which is stored in PROGMEM (flash), which is almost always more
// plentiful than RAM.  A static PROGMEM palette like this
// takes up 64 bytes of flash.
const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM =
{
    CRGB::Red,
    CRGB::Gray, // 'white' is too bright compared to red and blue
    CRGB::Blue,
    CRGB::Black,
    
    CRGB::Red,
    CRGB::Gray,
    CRGB::Blue,
    CRGB::Black,
    
    CRGB::Red,
    CRGB::Red,
    CRGB::Gray,
    CRGB::Gray,
    CRGB::Blue,
    CRGB::Blue,
    CRGB::Black,
    CRGB::Black
};



// Additionl notes on FastLED compact palettes:
//
// Normally, in computer graphics, the palette (or "color lookup table")
// has 256 entries, each containing a specific 24-bit RGB color.  You can then
// index into the color palette using a simple 8-bit (one byte) value.
// A 256-entry color palette takes up 768 bytes of RAM, which on Arduino
// is quite possibly "too many" bytes.
//
// FastLED does offer traditional 256-element palettes, for setups that
// can afford the 768-byte cost in RAM.
//
// However, FastLED also offers a compact alternative.  FastLED offers
// palettes that store 16 distinct entries, but can be accessed AS IF
// they actually have 256 entries; this is accomplished by interpolating
// between the 16 explicit entries to create fifteen intermediate palette
// entries between each pair.
//
// So for example, if you set the first two explicit entries of a compact 
// palette to Green (0,255,0) and Blue (0,0,255), and then retrieved 
// the first sixteen entries from the virtual palette (of 256), you'd get
// Green, followed by a smooth gradient from green-to-blue, and then Blue

Hardware Tweaks

OK so my build was getting more and more complex… having the butttons on the ‘lid’ of the case and the circuits on the ‘bottom’ meant i was routing more and more connectors between the two so I could easily open the case to repair/upgrade.

I’d moved on from using DB9 connectors to using VGA connectors as you got 15 pins instead of 9 in pretty much the same form factor. Also VGA cables are cheaper than DB9 and its much easier to get shorter (0.5m) VGA cables.

Then I thought of moving most of the circuits to the lid so that I needed fewer VGA cables. I didn’t want to add everything to the lid as I would turn the top side of the lid into swiss cheese and ruin the aesthetics.

Then came another small revelation. why don’t I hang a shelf / bracket off the inside to the lid that I can turn into swiss cheese! This could be attached by 4 screws through the corners of the bracket to the lid and then mount all of the circuit boards on the bracket. This means that the only cables running from the bottom to the lid should be the power cables.

Now I need to grab some thin metal to bend into the shape of the bracket. I reckon that I can grab the side panel from the side of a scrap PC and bend it into the appropriate shape.

As always I’m jumping around all over the place so I’ve added a power light and an on-switch for the switches that will control the audio samples on the left.

At some point I missed out adding the fact that I’ve mounted the buttons for RGD LEDs, as well as now adding an off/on switch and power indicator LED.

Here’s where I am with the hardware so far.

Some of the power connectors have now arrived as well so I can start adding in the external power controls for the lighting that is already in the Rave Cave.

Fitted the power outlets to the back. I jigsawed them out… note to self buy some ear defenders!

Started wiring up the AC circuits inside. The ground is also linked to the chassis. You can also see the 10A AC to DC converter to give me 10A @ 5V. I bought this straight of Aliexpress. I wasn’t going to try and build one myself.

I linked this up to test. Before I do much more I will add an inline fuse on the live line above the Wago connectors.

From above you can see the last few things I’m going to squeeze in (apart from 5 fast blow fuses one on the main live line and one prior to each mains power switch.

The four switches for the external power are now mounted. They were going to go across the top but room inside was just too tight.

Mains power is now complete and tested.

Sliced a diffuser for the LEDs from a milk bottle!

A reflector to mount the LEDs on cut out of ply and wrapped in aluminium tape.

The two arduinos wired in for audio samples and LEDs. I’ve abandoned the bracket mounting idea as there just isn’t enough room in the case so I’ve so far got 8 ugly screw heads showing on the case with another 4 to add for the Pi (to do the video loops). I’m really enjoying making this.

LED Controls and wiring reassembled, tested and kind of bodged together. Now to make that neat and tidy and add a couple more brackets to hold the diffuser in place. I’ve cut the brackets out of some tough clear plastic (actually an old LTO5 tape case) so they show up less from the front. I’ve doubled them up and I’ve got two rows of 12 as they are fairly low density. I’ve left the rows on separate connectors so in can come back and program them independently if I want. The are linked with some Wago connectors but I’ll solder a Y connector tomorrow.

The brackets for the diffuser were first sawn and then snipped off. They could then be snipped to an appropriate size.

Here’s one of the brackets in place so you can see what I’m rambling about!

Here’s the soldered Y connector for the LED strips, tested and working. Chuffed at how neatly this came out. Each of the separate cables were soldered and heat shrunk and then additional heat shrink over the top.

Pi Video module mounted, the two arduinos are now powered through the power switches and the power indicator LEDs added (with a 470 Ohm resistor for each as I couldn’t find a 330 but they work fine and they don’t have to be bright).

AAARrrrrrrrrrrrrrrggggghhhhhh……. I’m fine.

  • The LED strips were peeling off the aluminium tape reflector.
  • The Pi power isn’t lined up and there is a switch in the way of the USB cable
  • The Pi HDMI is pointing out over the little breadboard that I’m using (don’t judge me!)

I’ve pulled out the reflector, removed the flappy LED strips with their rubbish glue and stuck them back down with some gorgeous 3M double-sided pressure tape that I had lying around!

The power supply issue I resolved by mounting the Pi on slightly taller stand-offs, the benefit of buying a kit of them is that I have a selection. I may still need to use longer but lets run with this for now and see if it works. If I make them much longer then the Pi is going to bump into the base!

The HDMI cable issue I resolved by moving the breadboard to a different location and rewiring all the connections (doh).

Next project I need to plan out my wiring better but on the plus side because I’m anal about using reusable components I could swap out different length jumper leads in seconds as oppose to dismounting and re-soldering leads on switches. Tomorrow will see some testing of all the systems and re-gluing the LED reflector (with attached LED strips) back in place.

Audio Samples from an Arduio

So here’s where I have chosen a bunch of my samples from….

Here’s a basic tutorial I’m going to follow.

Amplification will be done by an external portable sound system (I’m going to use a mini rig) this build is complex enough already!

https://www.arduino.cc/en/Reference/SDCardNotes

https://www.arduino.cc/en/Reference/SD

At this point I’ve realised that I only have digital outs. There are options to build my own 8-bit DAC but they all look a but shoddy and I haven’t expanded to printing my own circuit boards as of yet. There are some nice shields that that use IS2 comms to output nicer audio quality but then I stumbled across some YX5300 boards that will do both the card reader and the audio DAC as well as giving me way more features than I could need.

There seem to be some good tutorials for this board, I will give them a try and update here. I’m hoping that this will allow me to use an Arduino Nano with the board instead of the Mega 2560 that I’ve got installed and then maybe reuse my now spare Mega instead of the Pi to alleviate the issue of the Pi being sensitive to losing power and corrupting its SD card.

Random Aside

I’ve been planning on having 1 action assigned to each button for both the leds and the audio samples. Why? It’s easy enough for them to have a different setting for each time the button is pressed.

LEDs (6 buttons)

  • Solid colours
  • Chase colours
  • Patterns
  • Pulsing
  • White
  • Blackout

Each button has an integer associated with it to cycle through the options, resetting at the highest to cycle back to the lowest. Swapping to pressing a different button resets the integer on the other buttons.

Audio samples (12 buttons)

This plan has grown somewhat!

ButtonPinFolderTrack
Black A3001001-Nasa-apollo11_countdown.mp3
Black A3001002-Nasa-Mercury-4_Clock-Started.mp3
Black A3001003-Nasa-houston_problem.mp3
Black B3102001-Nasa-Mercury-6_Zero-G.mp3
Black B3102002-Nasa-eagle_has_landed.mp3
Black B3102003-Nasa-small_step.mp3
Red A3203001-StarTrek-Klaxon.wav
Red B3304001-StarWars-Han_Blaster.wav
Yellow A3405001-AmenBreak.wav
Yellow B3506001-Bridge_to_captain.mp3
Yellow B3506002-Losing_power.mp3
Yellow B3506003-Blow_up_any_minute.mp3
Yellow B3506004-Can_be_repaired.mp3
Blue A3607001-Tardis.mp3
Blue B3708001-StarWars-ee_trench_run6.wav
Green A3809001-StarWars-R2D2.wav
Green B3910001-Broadsword.mp3
White A4011001-Trex.wav
White B4112001-WilhelmScream.wav

Its alive….. (the MP3 player plays MP3s)

Expand the link below to see the current code or better yet go to the public GitHub repository for the latest version.

https://github.com/IceniDesign/RaveCaveControls

MP3 Player Code So Far
//code rearranged by Javier Muñoz 10/11/2016 ask me at javimusama@hotmail.com
#include <SoftwareSerial.h>

#define ARDUINO_TX 5//should connect to TX of the Serial MP3 Player module
#define ARDUINO_RX 6//connect to RX of the module
SoftwareSerial mySerial(ARDUINO_TX, ARDUINO_RX);//init the serial protocol, tell to myserial wich pins are TX and RX

////////////////////////////////////////////////////////////////////////////////////
//all the commands needed in the datasheet(http://geekmatic.in.ua/pdf/Catalex_MP3_board.pdf)
static int8_t Send_buf[8] = {0} ;//The MP3 player undestands orders in a 8 int string
//0X7E FF 06 command 00 00 00 EF;(if command =01 next song order)
#define NEXT_SONG 0X01
#define PREV_SONG 0X02

#define CMD_PLAY_W_INDEX 0X03 //DATA IS REQUIRED (number of song)

#define VOLUME_UP_ONE 0X04
#define VOLUME_DOWN_ONE 0X05
#define CMD_SET_VOLUME 0X06//DATA IS REQUIRED (number of volume from 0 up to 30(0x1E))
#define SET_DAC 0X17
#define CMD_PLAY_WITHVOLUME 0X22 //data is needed  0x7E 06 22 00 xx yy EF;(xx volume)(yy number of song)

#define CMD_SEL_DEV 0X09 //SELECT STORAGE DEVICE, DATA IS REQUIRED
#define DEV_TF 0X02 //HELLO,IM THE DATA REQUIRED

#define SLEEP_MODE_START 0X0A
#define SLEEP_MODE_WAKEUP 0X0B

#define CMD_RESET 0X0C//CHIP RESET
#define CMD_PLAY 0X0D //RESUME PLAYBACK
#define CMD_PAUSE 0X0E //PLAYBACK IS PAUSED

#define CMD_PLAY_WITHFOLDER 0X0F//DATA IS NEEDED, 0x7E 06 0F 00 01 02 EF;(play the song with the directory \01\002xxxxxx.mp3

#define STOP_PLAY 0X16

#define PLAY_FOLDER 0X17// data is needed 0x7E 06 17 00 01 XX EF;(play the 01 folder)(value xx we dont care)

#define SET_CYCLEPLAY 0X19//data is needed 00 start; 01 close

#define SET_DAC 0X17//data is needed 00 start DAC OUTPUT;01 DAC no output
////////////////////////////////////////////////////////////////////////////////////

#define buttonPin_WHITE   2
int buttonState_WHITE = 0;        // variable for reading the pushbutton status
#define buttonPin_BLUE   3
int buttonState_BLUE = 0;        // variable for reading the pushbutton status
#define buttonPin_BLACK   4
int buttonState_BLACK = 0;        // variable for reading the pushbutton status



void setup()
{
  pinMode(buttonPin_WHITE, INPUT);    // initialize the pushbutton pin as an input:
  pinMode(buttonPin_BLUE, INPUT);    // initialize the pushbutton pin as an input:
  pinMode(buttonPin_BLACK, INPUT);    // initialize the pushbutton pin as an input:

  Serial.begin(9600);//Start our Serial coms for serial monitor in our pc
  mySerial.begin(9600);//Start our Serial coms for THE MP3
  delay(500);//Wait chip initialization is complete
  sendCommand(CMD_SEL_DEV, DEV_TF);//select the TF card
  delay(200);//wait for 200ms

}
void loop()
{
  
  buttonState_WHITE = digitalRead(buttonPin_WHITE);
  buttonState_BLUE = digitalRead(buttonPin_BLUE);
  buttonState_BLACK = digitalRead(buttonPin_BLACK);

  if (buttonState_WHITE == HIGH) {
    sendCommand(CMD_PLAY_WITHVOLUME, 0X0F01);//play the first song with volume 15 class
    delay(1000);
  }
  else if (buttonState_BLUE == HIGH) {
    sendCommand(CMD_PLAY_WITHVOLUME, 0X0F02);//play the first song with volume 15 class
    delay(1000);
  }
  else if (buttonState_BLACK == HIGH) {
    sendCommand(CMD_PLAY_WITHVOLUME, 0X0F03);//play the first song with volume 15 class
    delay(1000);
  }
}

void sendCommand(int8_t command, int16_t dat)
{
  delay(20);
  Send_buf[0] = 0x7e; //starting byte
  Send_buf[1] = 0xff; //version
  Send_buf[2] = 0x06; //the number of bytes of the command without starting byte and ending byte
  Send_buf[3] = command; //
  Send_buf[4] = 0x01;//0x00 = no feedback, 0x01 = feedback
  Send_buf[5] = (int8_t)(dat >> 8);//datah
  Send_buf[6] = (int8_t)(dat); //datal
  Send_buf[7] = 0xef; //ending byte
  for (uint8_t i = 0; i < 8; i++) //
  {
    mySerial.write(Send_buf[i]) ;//send bit to serial mp3
    Serial.print(Send_buf[i], HEX); //send bit to serial monitor in pc
  }
  Serial.println();
}

….and already I want to change some of the samples so they cycle through when the same button is pressed. If you check the table of samples above I’ve changed that to reflect this.

I’ve also changed the pin layout slightly as this is now way better and tweaked the timing from the code above. I’ve left the one above as its easier to read if you don’t want all these bells and whistles.

MP3 Player Code So Far - now with multiple samples on some buttons
//code rearranged by Javier Muñoz 10/11/2016 ask me at javimusama@hotmail.com
//additional tweaks (lots of them) by Iceni Design.

#include <SoftwareSerial.h>

#define ARDUINO_TX 14//should connect to TX of the Serial MP3 Player module
#define ARDUINO_RX 15//connect to RX of the module
SoftwareSerial mySerial(ARDUINO_TX, ARDUINO_RX);//init the serial protocol, tell to myserial wich pins are TX and RX

////////////////////////////////////////////////////////////////////////////////////
//all the commands needed in the datasheet(http://geekmatic.in.ua/pdf/Catalex_MP3_board.pdf)
static int8_t Send_buf[8] = {0} ;//The MP3 player undestands orders in a 8 int string
//0X7E FF 06 command 00 00 00 EF;(if command =01 next song order)
#define NEXT_SONG 0X01
#define PREV_SONG 0X02

#define CMD_PLAY_W_INDEX 0X03 //DATA IS REQUIRED (number of song)

#define VOLUME_UP_ONE 0X04
#define VOLUME_DOWN_ONE 0X05
#define CMD_SET_VOLUME 0X06//DATA IS REQUIRED (number of volume from 0 up to 30(0x1E))
#define SET_DAC 0X17
#define CMD_PLAY_WITHVOLUME 0X22 //data is needed  0x7E 06 22 00 xx yy EF;(xx volume)(yy number of song)

#define CMD_SEL_DEV 0X09 //SELECT STORAGE DEVICE, DATA IS REQUIRED
#define DEV_TF 0X02 //HELLO,IM THE DATA REQUIRED

#define SLEEP_MODE_START 0X0A
#define SLEEP_MODE_WAKEUP 0X0B

#define CMD_RESET 0X0C//CHIP RESET
#define CMD_PLAY 0X0D //RESUME PLAYBACK
#define CMD_PAUSE 0X0E //PLAYBACK IS PAUSED

#define CMD_PLAY_WITHFOLDER 0X0F//DATA IS NEEDED, 0x7E 06 0F 00 01 02 EF;(play the song with the directory \01\002xxxxxx.mp3

#define STOP_PLAY 0X16

#define PLAY_FOLDER 0X17// data is needed 0x7E 06 17 00 01 XX EF;(play the 01 folder)(value xx we dont care)

#define SET_CYCLEPLAY 0X19//data is needed 00 start; 01 close

#define SET_DAC 0X17//data is needed 00 start DAC OUTPUT;01 DAC no output
////////////////////////////////////////////////////////////////////////////////////

//I have two columns (A, B) of 6 rows (Black, Red, Yellow, Blue, Green, White) of buttons
//Cycle variables are needed for buttons that play multiple samples sequentially

//~~~~ COLUMN A ~~~~
#define buttonPin_BLACK_A   30
int buttonState_BLACK_A = 0;      // variable for reading the pushbutton status
int Cycle_BLACK_A = 0;            // variable current Track Number
int Cycle_BLACK_A_NEXT = 1;       // variable next Track Number
#define buttonPin_RED_A   32
int buttonState_RED_A = 0;        // variable for reading the pushbutton status
#define buttonPin_YELLOW_A   34
int buttonState_YELLOW_A = 0;     // variable for reading the pushbutton status
#define buttonPin_BLUE_A   36
int buttonState_BLUE_A = 0;       // variable for reading the pushbutton status
#define buttonPin_GREEN_A   38
int buttonState_GREEN_A = 0;      // variable for reading the pushbutton status
#define buttonPin_WHITE_A   40
int buttonState_WHITE_A = 0;      // variable for reading the pushbutton status

//~~~~ COLUMN B ~~~~
#define buttonPin_BLACK_B   31
int buttonState_BLACK_B = 0;      // variable for reading the pushbutton status
int Cycle_BLACK_B = 1;            // variable current Track Number
int Cycle_BLACK_B_NEXT = 2;       // variable next Track Number
#define buttonPin_RED_B   33
int buttonState_RED_B = 0;        // variable for reading the pushbutton status
#define buttonPin_YELLOW_B   35
int buttonState_YELLOW_B = 0;     // variable for reading the pushbutton status
int Cycle_YELLOW_B = 1;           // variable current Track Number
int Cycle_YELLOW_B_NEXT = 2;       // variable next Track Number
#define buttonPin_BLUE_B   37
int buttonState_BLUE_B = 0;       // variable for reading the pushbutton status
#define buttonPin_GREEN_B   39
int buttonState_GREEN_B = 0;      // variable for reading the pushbutton status
#define buttonPin_WHITE_B   41
int buttonState_WHITE_B = 0;      // variable for reading the pushbutton status

void setup()
{
  // initialize the pushbutton pins as an input:
  pinMode(buttonPin_BLACK_A, INPUT);
  pinMode(buttonPin_RED_A, INPUT);
  pinMode(buttonPin_YELLOW_A, INPUT);
  pinMode(buttonPin_BLUE_A, INPUT);
  pinMode(buttonPin_GREEN_A, INPUT);
  pinMode(buttonPin_WHITE_A, INPUT);

  pinMode(buttonPin_BLACK_B, INPUT);
  pinMode(buttonPin_RED_B, INPUT);
  pinMode(buttonPin_YELLOW_B, INPUT);
  pinMode(buttonPin_BLUE_B, INPUT);
  pinMode(buttonPin_GREEN_B, INPUT);
  pinMode(buttonPin_WHITE_B, INPUT);

  Serial.begin(9600);//Start our Serial coms for serial monitor in our pc
  mySerial.begin(9600);//Start our Serial coms for THE MP3
  delay(500);//Wait chip initialization is complete
  sendCommand(CMD_SEL_DEV, DEV_TF);//select the TF card
  delay(200);//wait for 200ms
  sendCommand(CMD_SET_VOLUME, 0X08); //Set volume in hex 0-30

}
void loop()
{

  buttonState_BLACK_A = digitalRead(buttonPin_BLACK_A);
  buttonState_RED_A = digitalRead(buttonPin_RED_A);
  buttonState_YELLOW_A = digitalRead(buttonPin_YELLOW_A);
  buttonState_BLUE_A = digitalRead(buttonPin_BLUE_A);
  buttonState_GREEN_A = digitalRead(buttonPin_GREEN_A);
  buttonState_WHITE_A = digitalRead(buttonPin_WHITE_A);

  buttonState_BLACK_B = digitalRead(buttonPin_BLACK_B);
  buttonState_RED_B = digitalRead(buttonPin_RED_B);
  buttonState_YELLOW_B = digitalRead(buttonPin_YELLOW_B);
  buttonState_BLUE_B = digitalRead(buttonPin_BLUE_B);
  buttonState_GREEN_B = digitalRead(buttonPin_GREEN_B);
  buttonState_WHITE_B = digitalRead(buttonPin_WHITE_B);


  if (buttonState_BLACK_A == HIGH) {
    
    Cycle_BLACK_A = Cycle_BLACK_A_NEXT;
  
    if (Cycle_BLACK_A == 1) { //if its 1 then play the first track and make the next one track 2
      sendCommand(CMD_PLAY_WITHFOLDER, 0X0101);
      Cycle_BLACK_A_NEXT = 2;
      delay(1000);
    }
    else if (Cycle_BLACK_A == 2) {
      sendCommand(CMD_PLAY_WITHFOLDER, 0X0102);
      Cycle_BLACK_A_NEXT = 3;
      delay(1000);
    }
    else if (Cycle_BLACK_A == 3) {
      sendCommand(CMD_PLAY_WITHFOLDER, 0X0103);
      Cycle_BLACK_A_NEXT = 4;
      delay(1000);
    }
    else {
      sendCommand(CMD_PLAY_WITHFOLDER, 0X0101); //something has gone wrong so play the first track and make the next one track 2
      Cycle_BLACK_A_NEXT = 1;
      delay(1000);
    }

  }
  else if (buttonState_BLACK_B == HIGH) {
    
    Cycle_BLACK_B = Cycle_BLACK_B_NEXT;

    if (Cycle_BLACK_B == 1) { //if its 1 then play the first track and make the next one track 2
      sendCommand(CMD_PLAY_WITHFOLDER, 0X0201);
      Cycle_BLACK_B_NEXT = 2;
      delay(1000);
    }
    else if (Cycle_BLACK_B == 2) {
      sendCommand(CMD_PLAY_WITHFOLDER, 0X0202);
      Cycle_BLACK_B_NEXT = 3;
      delay(1000);
    }
    else if (Cycle_BLACK_B == 3) {
      sendCommand(CMD_PLAY_WITHFOLDER, 0X0203);
      Cycle_BLACK_B_NEXT = 4;
      delay(1000);
    }
    else {
      sendCommand(CMD_PLAY_WITHFOLDER, 0X0201); //something has gone wrong so play the first track and make the next one track 2
      Cycle_BLACK_B_NEXT = 2;
      delay(1000);
    }
  }
  else if (buttonState_RED_A == HIGH) {
    sendCommand(CMD_PLAY_WITHFOLDER, 0X0301);//play the song in folder 01, named 001xxxxxx.xxx
    delay(1000);
  }
  else if (buttonState_RED_B == HIGH) {
    sendCommand(CMD_PLAY_WITHFOLDER, 0X0401);//play the song in folder 01, named 001xxxxxx.xxx
    delay(1000);
  }
  else if (buttonState_YELLOW_A == HIGH) {
    sendCommand(CMD_PLAY_WITHFOLDER, 0X0501);//play the song in folder 01, named 001xxxxxx.xxx
    delay(1000);
  }
  else if (buttonState_YELLOW_B == HIGH) {
    
    Cycle_YELLOW_B = Cycle_YELLOW_B_NEXT;
    delay(1000);

    if (Cycle_YELLOW_B == 1) { //if its 1 then play the first track and make the next one track 2
      sendCommand(CMD_PLAY_WITHFOLDER, 0X0601);
      Cycle_YELLOW_B_NEXT = 2;
      delay(1000);
    }
    else if (Cycle_YELLOW_B == 2) {
      sendCommand(CMD_PLAY_WITHFOLDER, 0X0602);
      Cycle_YELLOW_B_NEXT = 3;
      delay(1000);
    }
    else if (Cycle_YELLOW_B == 3) {
      sendCommand(CMD_PLAY_WITHFOLDER, 0X0603);
      Cycle_YELLOW_B_NEXT = 4;
      delay(1000);
    }
    else if (Cycle_YELLOW_B == 4) {
      sendCommand(CMD_PLAY_WITHFOLDER, 0X0604);
      Cycle_YELLOW_B_NEXT = 1;
      delay(1000);
    }
    else {
      sendCommand(CMD_PLAY_WITHFOLDER, 0X0601); //something has gone wrong so play the first track and make the next one track 2
      Cycle_YELLOW_B_NEXT = 2;
      delay(1000);
    }

  }
  else if (buttonState_BLUE_A == HIGH) {
    sendCommand(CMD_PLAY_WITHFOLDER, 0X0701);
    delay(1000);
  }
  else if (buttonState_BLUE_B == HIGH) {
    sendCommand(CMD_PLAY_WITHFOLDER, 0X0801);
    delay(1000);
  }
  else if (buttonState_GREEN_A == HIGH) {
    sendCommand(CMD_PLAY_WITHFOLDER, 0X0901);
    delay(1000);
  }
  else if (buttonState_GREEN_B == HIGH) {
    sendCommand(CMD_PLAY_WITHFOLDER, 0X0A01);
    delay(1000);
  }
  else if (buttonState_WHITE_A == HIGH) {
    sendCommand(CMD_PLAY_WITHFOLDER, 0X0B01);
    delay(10000);
  }
  else if (buttonState_WHITE_B == HIGH) {
    sendCommand(CMD_PLAY_WITHFOLDER, 0X0C01);
    delay(1000);
  }
}

void sendCommand(int8_t command, int16_t dat)
{
  delay(20);
  Send_buf[0] = 0x7e; //starting byte
  Send_buf[1] = 0xff; //version
  Send_buf[2] = 0x06; //the number of bytes of the command without starting byte and ending byte
  Send_buf[3] = command; //
  Send_buf[4] = 0x01;//0x00 = no feedback, 0x01 = feedback
  Send_buf[5] = (int8_t)(dat >> 8);//datah
  Send_buf[6] = (int8_t)(dat); //datal
  Send_buf[7] = 0xef; //ending byte
  for (uint8_t i = 0; i < 8; i++) //
  {
    mySerial.write(Send_buf[i]) ;//send bit to serial mp3
    Serial.print(Send_buf[i], HEX); //send bit to serial monitor in pc
  }
  Serial.println();
}

LED Controls Version 2

So I’ve now got some Nano’s and didn’t need to be using all the real estate of the Mega 2560. So now I’m going to redo the code on a Nano but have it so different presses on different buttons do different things!

Still not sure ho to mount Nanos as they are timy and the mounting holes don’t fit my standoffs! So I ended up hot-glueing a yogurt pot lid to the metal surface to avoid any shorts and basically tying the nano to that…. Really I want some nice 3D printed holders that mount the Nano’s at 90 degrees with the pins sticking out sidways but this will do for now.

Check-out git-hub for all of the latest versions of my code for the various modules.

https://github.com/IceniDesign/RaveCaveControls

Things to Fix (in order of importance)

  • 3.5mm audio out socket to be mounted on the front of the box
  • The HDMI to VGA adapter is now a bit chunky to fit in the case. I might need to swap to running HDMI to the output at the back and then convert to VGA from there.
  • Sort out setting the SD card as read only to protect the Pi from powering down.
  • Try and streamline the wiring as its really tight inside.
  • Sort a decent way to mount the Nano’s
  • Look at swapping the Mega on the MP3 player for a Nano