Eric G
Created October 7, 2016

Lets MAKE Some Props.

Want to make your friends SCREAM? These are some tips and tricks the professionals use in their haunts.

315
Lets MAKE Some Props.

Things used in this project

Schematics

Button Banger Pin out and breadboard wiring.

Code

FourBanger.ino

C/C++
All code is Credited to Mike North: http://buttonbanger.com/?page_id=37
/*
*************Four Banger*********
3/26/2015 hacked together by Mike North aka mikkojay on multiple forums.
The latest info and updates for this sketch may be found at ButtonBanger.com

The mini/nano has 1k of eeprom and a 12v regulator.  At the moment of writing this (3/2015) the 
pro mini could be found on ebay for $2.33 shipped which is insanely cheap. Speaking of insanely cheap,
how about a 4 relay module, optocoupled and all, for $3.30 shipped?  This means that for under $6 you
could have a very tidy little programmable prop controller.  
I also found a nice "5 button board" on ebay that should work nicely as a manual programmer.

For audio, we are currently using the Catalex module because it is cheaper, smaller, and has a jack.

reset_delay is programmed using the following procedure:
-While in ambient mode, hold down the menu button (button 3) for 3+ seconds. 
-RECORD_LED will blink 3 times.
-enter desired delay seconds by entering the number in binary, 8 digits (up to 255 seconds)
-  This is done by using the - and + buttons on the programming board.
-  Entering - is a zero, entering + is a one
-  For example, we want a delay of 1.5 minutes, or 90 seconds.
-- In windows calculator, enter 90 (programming mode) in decimal, then switch view to binary.
-- you then get: 1011010
-- This would be entered starting with the lowest(rightmost) digit, padded with zeros for a total of 8 digits.
-- Imagine it as a dip switch panel with 8 positions.
                          0 1 0 1 1 0 1 0
-- The sequence would be: -,+,-,+,+,-,+,-
-- The RECORD_LED will blink 3 times to indicate the successful setting of this value.
 */
#include <EEPROM.h>
//#include <SoftwareSerial/SoftwareSerial.h>
#include "MiniAudio.h"

volatile bool stampok = true;
volatile byte stamp_buff[5] = {6,70,4,1,4}; //holder for stamp
volatile bool pir_ok = true;
int pirValue = 0;        // value read from the pir pin

//these are the free standing defines

#define RECORD_PIN 2      //the pin number of our recording switch
#define BUTTON_1_PIN 6 //the pin number of our "1" switch, may be marked as - on programming board
#define BUTTON_2_PIN 5 //the pin number of our "2" switch, may be marked as + on programming board
#define BUTTON3_MENU_PIN 8 //the pin number of our "3" switch, may be marked as Menu on programming board
#define BUTTON4_TRIGGER_PIN 7 //pin number of our trigger, same as 4 switch, may be marked Auto on prog board
#define RECORD_LED 3 //red led
#define PLAY_LED 4 //green led
#define PIR_INPUT_PIN A5 //PIR read from analog input pin

/*
//these are the host board pin assignments
#define RECORD_PIN 9      //the pin number of our recording switch
#define BUTTON_1_PIN 4 //the pin number of our "1" switch, may be marked as - on programming board
#define BUTTON_2_PIN 5 //the pin number of our "2" switch, may be marked as + on programming board
#define BUTTON3_MENU_PIN 2 //the pin number of our "3" switch, may be marked as Menu on programming board
#define BUTTON4_TRIGGER_PIN 3 //pin number of our trigger, same as 4 switch, may be marked Auto on prog board
#define RECORD_LED 8 //red led
#define PLAY_LED 7 //green led
#define PIR_INPUT_PIN A6 //PIR read from analog input pin
*/

#define AUDIO_PIN 10 //mp3 serial out
#define TRIGGER_TTL_PIN 11 //alt input for a TTL trigger- uses internal pull up

#define TRIGGER_OUT_PIN 12
#define ARDUINO_LED 13 //board led
#define DEBOUNCE 6  // button debouncer, how many ms to debounce, 5+ ms is usually plenty

//eeprom offsets
//sequence data 0 - 979
const int SAMPLE_COUNT = 980; //saving last 20 bytes for config use (total of 1k eeprom)
const int MARK_OFFSET = 980; //start byte of stamp
//980-984 stamp
const int MS_PER_EVENT_SLOT = 985; 
const int TRIGGER_LOW_SLOT = 986;
const int TTL_TYPES_SLOT = 987;
const int RESET_DELAY_SLOT = 988; 
const int HI_SAMPLE_LB = 989;
const int HI_SAMPLE_UB = 990;
const int REC_LOCK_SLOT = 991;
const int AMBIENT_ON_DELAY_SLOT = 992;
const int BOOT_DELAY_SLOT = 993;
const int VOLUME_SLOT = 994;

bool AMBIENT_ON_DELAY = true;
const int TTL_COUNT = 4; //number of TTL outputs
const int INPUT_COUNT = 6; //reading from 6 io pins
bool TTL_TYPES[TTL_COUNT] = {1,1,1,1}; //translated TTL Types
int8_t TTL_PINS[TTL_COUNT];

int8_t SWITCH_PINS[INPUT_COUNT];
volatile byte pressed[INPUT_COUNT];

volatile uint8_t TTL_TYPE_BYTE = 15;
int val = 0;     // variable to store the read value
unsigned int hi_sample = 0;
uint8_t MS_PER_EVENT_OPTS[4] = {200,100,50,40}; //5,10,20,25 fps
volatile uint8_t MS_PER_EVENT = 50; //default is 20 frames per sec (50 ms each)
volatile uint8_t RESET_DELAY_SECS = 30; //default will be 30 secs I suppose
volatile byte samples[SAMPLE_COUNT]; //working buffer so we don't read from eeprom constantly
volatile uint8_t PIR_ANALOG_NORMALLY = LOW;
volatile uint8_t record_lock = 0X0; //if 1, we are locked
const int SERIAL_WAIT = 5; //delay for serial reads
volatile uint8_t BOOT_DELAY_SECS = 10; //delay before the prop "goes hot"
volatile uint8_t _volume = 30;
volatile bool IS_HOT = true;
MiniAudio _MA;

/*********************************************************************/

void setup() 
{
    Serial.begin(115200); //we talk to the PC at 115200
	delay(100);
	//map our pins
	TTL_PINS[0] = A0;
	TTL_PINS[1] = A1;
	TTL_PINS[2] = A2;
	TTL_PINS[3] = A3;
	SWITCH_PINS[0] = BUTTON_1_PIN;
    SWITCH_PINS[1] = BUTTON_2_PIN;
    SWITCH_PINS[2] = BUTTON3_MENU_PIN;
    SWITCH_PINS[3] = BUTTON4_TRIGGER_PIN;
	SWITCH_PINS[4] = RECORD_PIN;
	SWITCH_PINS[5] = TRIGGER_TTL_PIN;

  //init output pins
  for (int i = 0; i < TTL_COUNT; i++)
  {
    pinMode(TTL_PINS[i], OUTPUT);
    digitalWrite(TTL_PINS[i], TTL_TYPES[i]); //HIGH = off
  }
  //init input pins
  for (int i = 0; i < INPUT_COUNT; i++)
  {
    pinMode(SWITCH_PINS[i], INPUT); 
    digitalWrite(SWITCH_PINS[i], HIGH); // connect internal pull-up 
  }  
  // initialize the LED pin as an output:
  pinMode(ARDUINO_LED, OUTPUT); 
  digitalWrite(ARDUINO_LED, LOW);
  //if they are holding down the play/Auto button on power up, do a factory reset
  pinMode(RECORD_LED, OUTPUT); 
  pinMode(PLAY_LED, OUTPUT); 
  digitalWrite(RECORD_LED, LOW); 
  digitalWrite(PLAY_LED, HIGH); 
  digitalWrite(PLAY_LED, LOW); 
  pinMode(TRIGGER_OUT_PIN, OUTPUT);
  digitalWrite(TRIGGER_OUT_PIN, HIGH); // connect internal pull-up 

  //check for previous fault and explain why with an error code
  //CheckMCU();

  if(digitalRead(SWITCH_PINS[3]) == LOW)
  {
	  delay(1000); //make them hold for a full second then check again
	  if(digitalRead(SWITCH_PINS[3]) == LOW)
	  {
		  burn_config(); //will burn defaults
		  blink_alert();
		  while(digitalRead(SWITCH_PINS[3]) == HIGH){}
	  }
  }
  //read config from eeprom
  read_config();
  _MA.Init(AUDIO_PIN,_volume);
  set_ambient_out();
  _MA.PlayAmbient();
  buffer_samples();
  report_config();
  check_pir();
  delay(100);
  if(BOOT_DELAY_SECS > 0)
  {
	  Serial.println(F("Waiting for Boot Delay..."));
	  blink_times(BOOT_DELAY_SECS); //this uses 1 second per blink
  }
  Serial.println(F("Ready"));
  clear_rx_buffer(); //just in case someone has been asking HEY HEY Do this do that while we were busy
}

void check_switches()
{
  //this is dumbed-down debounce check.
  //it looks at all input in one sweep.
	//button is ussumed off at first
	//2 samples are taken for each input, separated by n milliseconds.
	//if the samples are both on, we consider it a true on condition
  static byte state1[INPUT_COUNT];
  static byte state2[INPUT_COUNT];
  static long lasttime;
  byte index;

  for (index = 0; index < INPUT_COUNT; index++)
  {
    pressed[index] = 1; //not pressed
    state1[index] = digitalRead(SWITCH_PINS[index]);   // read the button
  }
  delay(DEBOUNCE);
  for (index = 0; index < INPUT_COUNT; index++)
  {
    state2[index] = digitalRead(SWITCH_PINS[index]);   // read the button
    if (state1[index] + state2[index] == 0)
	{
      pressed[index] = 0;  
    }
  }
}
void CheckMCU()
{
	return;
	int times = 0;
	if(MCUSR & (1<<PORF )) times = 1; //Power-on Reset

	if(MCUSR & (1<<EXTRF)) times = 2; //External reset

	if(MCUSR & (1<<BORF )) times = 3; //Brownout reset

	if(MCUSR & (1<<WDRF )) times = 4; //Watchdog reset

	if(times > 0)
	{
		blink_alert();
		delay(1000);
		blink_times(times);
		delay(1000);
		blink_alert();
		delay(500);
	}
	MCUSR = 0;
}
void check_pir()
{
	pir_ok = false;
	int waitmax = 45;
	Serial.println(F("Checking for PIR..."));
	for(int i = 0; i < waitmax; i++)
	{
		blink_times(1);
		pirValue = analogRead(PIR_INPUT_PIN); 
		if(PIR_ANALOG_NORMALLY == LOW) 
		{
		   if (pirValue < 20)
		   {
			   pir_ok = true;
			   break;
		   }
		}
		else
		{
		   if (pirValue > 900)
		   {
			   pir_ok = true;
			   break;
		   }		
		}
	}
	if(pir_ok)
	{
		Serial.println(F("PIR found, 10 sec wait..."));
		//courtesy delay of 10 secs
		for(int i = 0; i < 10; i++)
		{
			blink_once();
		}
		Serial.println(F("PIR ready"));
	}
	else
		Serial.println(F("PIR not found, ignoring PIR"));
}
void buffer_samples()
{
  //fetch whatever eeprom values we have into buffer
  if(hi_sample > 0)
  {
	  for (int i = 0; i < SAMPLE_COUNT; i++)
	  {
		 samples[i] = EEPROM.read(i); 
	  }
  }
}
void read_config()
{
  byte dtemp = 0;
  stampok = true;
  for(int i = 0; i < 5; i++)
  {
	  dtemp = EEPROM.read(MARK_OFFSET + i);
	  if(dtemp != stamp_buff[i])
	  {
		  stampok = false;
		  break;
	  }
  }
  
  if(stampok) //control number so we don't read in junk
  {
	  PIR_ANALOG_NORMALLY = EEPROM.read(TRIGGER_LOW_SLOT);
	  hi_sample = EEPROM.read(HI_SAMPLE_UB);   //read the high/upper byte into hi_sample
	  hi_sample = hi_sample << 8;  
	  //bitshift the high byte left 8 bits to make room for the low byte
	  hi_sample = hi_sample | EEPROM.read(HI_SAMPLE_LB); //read the low byte
	  //get reset_delay out of EEPROM, represents seconds
	  MS_PER_EVENT = EEPROM.read(MS_PER_EVENT_SLOT);  
	  if(MS_PER_EVENT < 40)
		  MS_PER_EVENT = 50;
	  RESET_DELAY_SECS = EEPROM.read(RESET_DELAY_SLOT); 
	  BOOT_DELAY_SECS = EEPROM.read(BOOT_DELAY_SLOT);
	  record_lock = EEPROM.read(REC_LOCK_SLOT); 
	  AMBIENT_ON_DELAY = EEPROM.read(AMBIENT_ON_DELAY_SLOT); 
	  TTL_TYPE_BYTE = EEPROM.read(TTL_TYPES_SLOT); 
	  _volume = EEPROM.read(VOLUME_SLOT); 
	  if(_volume < 1 || _volume > 30)
		  _volume = 30;
	  _MA.Init(AUDIO_PIN,_volume);
	  for (int i = 0; i < TTL_COUNT; i++)
	  {
		TTL_TYPES[i] = bitRead(TTL_TYPE_BYTE,i); //default to TTL low Output default, active high
	  }
  }
  else
  {
	  init_defaults();
  }
}
void report_config()
{
	Serial.print(F("FourBanger v"));
	for (int i = 3; i < 5; i++)
	{
        Serial.print(stamp_buff[i]);
		delay(1);
		if(i < 4) 
			Serial.print(".");
		else
			Serial.println();
	}
	
	if(stampok) //control number so we don't read in junk
	{
		Serial.println(F("Config OK"));
	}
	else
	{
		Serial.println(F("Config NOT FOUND, using defaults"));
	}
	Serial.print(F("PIR Normally Hi/Low: "));
	Serial.println(PIR_ANALOG_NORMALLY);
	Serial.print(F("PIR Input Pin: "));
	Serial.println(PIR_INPUT_PIN);

	Serial.print(F("MS Per Event: "));
	Serial.println(MS_PER_EVENT);
	Serial.print(F("Event Count: "));
	Serial.println(hi_sample);
	Serial.print(F("Reset Delay Secs: "));
	Serial.println(RESET_DELAY_SECS);
	Serial.print(F("Boot Delay Secs: "));
	Serial.println(BOOT_DELAY_SECS);

	if(record_lock > 0)
		Serial.println(F("Recording Locked"));
	Serial.print(F("TTL TYPES: "));
	for (int i = 0; i < TTL_COUNT; i++)
	{
	    Serial.print(TTL_TYPES[i]); 
		if(i<TTL_COUNT - 1)
			Serial.print(","); 
		else
			Serial.println();
	}
	Serial.print(F("TTL PINS: "));
	for (int i = 0; i < TTL_COUNT; i++)
	{
	    Serial.print(TTL_PINS[i]); 
		if(i<TTL_COUNT - 1)
			Serial.print(","); 
		else
			Serial.println();
	}
}
void init_defaults()
{
	_volume = 30;
  for (int i = 0; i < TTL_COUNT; i++)
  {
    TTL_TYPES[i] = true; //default to TTL low Output default, active high
  }
}
byte softRead(byte pin)
{
  for (int i = 0; i < INPUT_COUNT; i++)
  {
    if (SWITCH_PINS[i] == pin)
		return pressed[i];
  }  
  return 0; //unknown pin
}
void loop()
{
	check_serial();
	if(IS_HOT)
	{
		check_switches(); 
		if (softRead(BUTTON4_TRIGGER_PIN) == LOW || softRead(TRIGGER_TTL_PIN) == LOW || pir_triggered())
		{
			play_sequence(); 
		}
		else
		{
			if (softRead(RECORD_PIN) == LOW)
			{
				record_sequence(); 
			}
			else if (softRead(BUTTON3_MENU_PIN) == LOW)
			{
				configure_reset_delay(); 
			} 
			else if (softRead(BUTTON_1_PIN) == LOW)
			{
				set_volume(false); 
			}
			else if (softRead(BUTTON_2_PIN) == LOW)
			{
				set_volume(true); 
			} 
		}
	}

}
bool set_volume(bool up)
{
	uint8_t newvol = _volume;
	if(up && _volume < 30)
		newvol++;
	if(!up && _volume > 1)
		newvol--;
	if(newvol != _volume)
	{
		_volume = newvol;
		EEPROM.write(VOLUME_SLOT,_volume);
		_MA.SetVolume(_volume);
	}
}
bool pir_triggered()
{
	if(pir_ok == false) return false;
	pirValue = analogRead(PIR_INPUT_PIN); 
	if(PIR_ANALOG_NORMALLY == LOW) 
	{
	    if (pirValue > 600) return true;
	}
	else
	{
		if (pirValue < 20) return true;
	}
	return false;
}
void record_sequence()
{
	int held_count = 0;
	//do not begin until they let up on the button
    while (digitalRead(RECORD_PIN) == LOW)
    {
        delay(1);
		held_count++;
		if(held_count > 3000)
		{
			if(record_lock > 0)
			{
				record_lock = 0;
				Serial.println(F("Recording enabled"));
			}
			else
			{
				record_lock = 1;
				Serial.println(F("Recording disabled"));
			}
			EEPROM.write(REC_LOCK_SLOT,record_lock);
			blink_alert();
			while (digitalRead(RECORD_PIN) == LOW){} //so recording does not get triggered again
			return;
		}
    }
	if(record_lock > 0)
	{
		Serial.println(F("Recording disabled"));
		Serial.println(F("Hold Rec button for 3+ seconds to enable"));
        blink_alert();
		return;
	}
	//clear buffer
	for(int i = 0; i < SAMPLE_COUNT;i++)
    {
	   samples[i] = 0;
	}
	digitalWrite(RECORD_LED, HIGH); //let them know we are recording
    _MA.PlayScare();
	Serial.println(F("Recording manually..."));
    hi_sample = 0;
    for(int i = 0; i < SAMPLE_COUNT;i++)
    {
        //each byte sample has 8 bits.  since we have 4 relays, that means one byte
        // can actually store 2 sets of 4 on/off states.
        hi_sample++;
		check_switches();
        for(int x = 0;x < TTL_COUNT; x++)
        {
            if (softRead(SWITCH_PINS[x]) == LOW) bitSet(samples[i],x);
        }
		OutputTTL(samples[i],0); //output lower half
        if (softRead(RECORD_PIN) == LOW) break;
        delay(MS_PER_EVENT - DEBOUNCE);
        hi_sample++;
		check_switches();
        for(int x = 0;x < TTL_COUNT; x++)
        {
            if (softRead(SWITCH_PINS[x]) == LOW) bitSet(samples[i],x + TTL_COUNT);
        }
		OutputTTL(samples[i],4); //output the upper half
        delay(MS_PER_EVENT - DEBOUNCE);
	    if (softRead(RECORD_PIN) == LOW) break;
	}
    //set all outputs off
    set_ambient_out();
	digitalWrite(RECORD_LED, LOW); 
    //save samples to EEPROM
    for (int i = 0; i < SAMPLE_COUNT; i++)
    {
        EEPROM.write(i,samples[i]); 
    }
	burn_config();
    //blink to let them know we saved
	Serial.println(F("Recording complete"));
	Serial.print(F("Samples recorded: "));
	Serial.println(hi_sample);
    blink_alert();
	delay(1000);
    _MA.PlayAmbient();
	check_switches();
	clear_rx_buffer();
}

void burn_config()
{
	//called after recording, write variables to eeprom so that we restore on next restart
	//write the stamp
	for(int i = 0; i < 5; i++)
		EEPROM.write(MARK_OFFSET + i,stamp_buff[i]);
	stampok = true; //now that we have written it
	//write the hi_sample value to eeprom
    EEPROM.write(HI_SAMPLE_UB, highByte(hi_sample));//writes the first byte of hi_sample
    EEPROM.write(HI_SAMPLE_LB, lowByte(hi_sample));//writes the second byte of hi_sample
	EEPROM.write(MS_PER_EVENT_SLOT,MS_PER_EVENT);   
	EEPROM.write(RESET_DELAY_SLOT,RESET_DELAY_SECS);
	EEPROM.write(BOOT_DELAY_SLOT,BOOT_DELAY_SECS);
	EEPROM.write(TTL_TYPES_SLOT,TTL_TYPE_BYTE); 
	EEPROM.write(AMBIENT_ON_DELAY_SLOT,AMBIENT_ON_DELAY);
	EEPROM.write(TRIGGER_LOW_SLOT,PIR_ANALOG_NORMALLY);
	EEPROM.write(VOLUME_SLOT,_volume);
}
void play_sequence()
{
	if (hi_sample == 0) return; //nothing recorded
	bool was_hot = IS_HOT;
	IS_HOT = false;
    digitalWrite(PLAY_LED, HIGH);
	digitalWrite(TRIGGER_OUT_PIN, LOW); //trigger output pin for daisy chain
	delay(100); // .1 sec should be sufficient
	digitalWrite(TRIGGER_OUT_PIN, HIGH); //put back
    _MA.PlayScare(); 
	Serial.println(F("Playing sequence..."));
    int play_sample = 0;
    for(int i = 0; i < SAMPLE_COUNT;i++)
    {
        play_sample++;
        if(play_sample > hi_sample) break;
        OutputTTL(samples[i],0);
        delay(MS_PER_EVENT);
        play_sample++;
        if(play_sample > hi_sample) break;
        OutputTTL(samples[i],4);
        delay(MS_PER_EVENT);
	}
	if(RESET_DELAY_SECS > 0)
	{
		Serial.print(F("Waiting delay secs: "));
		Serial.println(RESET_DELAY_SECS);
	}
	set_ambient_out();
	if(AMBIENT_ON_DELAY)  //true means go back to ambient audio before the delay
		_MA.PlayAmbient();
	blink_times(RESET_DELAY_SECS); //this uses 1 second per blink
    digitalWrite(PLAY_LED, LOW);
    digitalWrite(RECORD_LED, LOW);
	if(!AMBIENT_ON_DELAY) //true means go back to ambient audio before the delay
		_MA.PlayAmbient();
	Serial.println(F("Sequence complete"));
	check_switches();
	clear_rx_buffer(); //this will wipe any crap that may have been transmitted in the time we were playing
	IS_HOT = was_hot;
}
void set_ambient_out()
{
    //set all outputs to default
	byte n = 0;
    OutputTTL(n,0);
}
//writes the upper or 4 or lower 4 of one byte to our 4 TTL pins.
//It will use the usage booleans to know whether to invert
void OutputTTL(byte val, int offset)
{
	for(int x = 0;x < TTL_COUNT; x++)
    {
		if(TTL_TYPES[x])
			digitalWrite(TTL_PINS[x], !(bitRead(val,x + offset)));
		else
			digitalWrite(TTL_PINS[x], bitRead(val,x + offset));
    }
}

//the whole reset delay thing is a total pain to set manually.
//I am leaving it in, but it will most likely never be called.
//setting this property in the GUI is 100% easier
void configure_reset_delay()
{
  Serial.println(F("configuring reset delay..."));
  unsigned long time1 = millis();
  while (digitalRead(BUTTON3_MENU_PIN) == LOW)
  {
     delay(100);
     if(millis() - time1 > 3000) break;
  }
  if(millis() - time1 < 3000) return; //only do this if they hold the button down for 3 secs
  blink_alert();
  RESET_DELAY_SECS = 0;
  //do nothing until the switch is released
  while (digitalRead(BUTTON3_MENU_PIN) == LOW)
  {
     delay(10);
  }
  //the switch states for the 1 and 0 pin 
  byte switch1 = digitalRead(BUTTON_2_PIN);
  byte switch0 = digitalRead(BUTTON_1_PIN);   

  byte bitnumber = 0; //the number of bits recorded so far
  //runs until all 8 bits have been recorded into the address.
  while (bitnumber < 8) 
  {
    //first, make sure both switches are released before accepting new bit.
    if (switch0 == LOW || switch1 == LOW)
    {
          blink_once(); //flash off pin 13 to indicate bit accepted
          //remain in the while loop while either switch is pressed, then delay half a second to avoid bounce and signal to the user that the bit has been accepted. 
          while (switch0 == LOW || switch1 == LOW)
          {
            switch0 = digitalRead(BUTTON_1_PIN);
            switch1 = digitalRead(BUTTON_2_PIN);
          }
          delay(500);
    }//end if
    
    //The switches have been released, now wait for the new bit.
    digitalWrite(RECORD_LED, HIGH);
    digitalWrite(PLAY_LED, HIGH); //turn on to indicate ready for new bit.
    switch0 = digitalRead(BUTTON_1_PIN);
    switch1 = digitalRead(BUTTON_2_PIN);    
    if (switch1 == LOW)
    {//execute if 1 pin is pressed
        bitSet(RESET_DELAY_SECS, bitnumber);  //write a 1 to the appropriate bit of reset_delay
        bitnumber++;
    }
    else if (switch0 == LOW)
    {//execute if 0 pin is pressed
        bitClear(RESET_DELAY_SECS, bitnumber);
        bitnumber++;
    }
  } //end while loop 
  //write the new address to EEPROM
  blink_once();
  EEPROM.write(RESET_DELAY_SLOT, RESET_DELAY_SECS);//writes the first byte of reset_delay
  blink_alert();
  digitalWrite(PLAY_LED, LOW);
  digitalWrite(RECORD_LED, LOW);
  Serial.print(F("new reset_delay: ")); 
  Serial.println(RESET_DELAY_SECS); 
}
  
void blink_alert()
{
        for (byte i = 0; i < 15; i++)
        { //blink LED 4 times when new address is received.
          digitalWrite(RECORD_LED, (i & 1));
		  digitalWrite(ARDUINO_LED, (i & 1));
          //i & 1 will bitwise-and to 1 if i is odd, 0 if even.
          delay(100);
        }
}
void blink_green()
{
        for (byte i = 0; i < 3; i++)
        { //blink LED 4 times when new address is received.
          digitalWrite(PLAY_LED, (i & 1));
          //i & 1 will bitwise-and to 1 if i is odd, 0 if even.
          delay(1000);
        }
}
void blink_once()
{
    digitalWrite(RECORD_LED, HIGH);
	digitalWrite(ARDUINO_LED, HIGH);
    delay(500);
    digitalWrite(RECORD_LED, LOW);
	digitalWrite(ARDUINO_LED, LOW);
    delay(500);
}
void blink_times(int times)
{
	for(int i = 0; i < times; i++)
	{
		Serial.print(F("."));
		digitalWrite(RECORD_LED, HIGH);
		digitalWrite(ARDUINO_LED, HIGH);
		delay(500);
		digitalWrite(RECORD_LED, LOW);
		digitalWrite(ARDUINO_LED, LOW);
		delay(500);
	}
}
void check_serial()
{
  if (Serial.available() > 0) 
  {
     byte b = Serial.read();
     if (b == '@')  // command header.
     {
        delay(SERIAL_WAIT);
        if (Serial.available() > 0)
        {
          b = Serial.read();
		  delay(SERIAL_WAIT);
          switch (b)
          {
			case 'V':
			    //return version
				delay(SERIAL_WAIT);
				for (int i = 0; i < 5; i++)
				{
                  Serial.print(stamp_buff[i]);
				  delay(1);
				  if(i < 4) Serial.print(".");
				}
				break;
			case 'H':
			    //go hot
				IS_HOT = true;
				Serial.println(F("Ready"));
				break;
			case 'C':
			    //go cold
				IS_HOT = false;
				Serial.println(F("Standby..."));
				break;
			case 'D':
			    //download eeprom contents back to Serial
				tx_memory();
				break;
		    case 'U':
				rx_memory(); //here comes an Upload
				break;
			case 'P': //ping back
				report_config();
				break;
            case 'T': //trigger test
				play_sequence();
				break;
            case 'M': //manual TTL state command
				b = Serial.read(); //get the state byte
				OutputTTL(b,0);
				break;
            case 'I': //PIR test
				pir_tester();
				break;
			default:
				Serial.print(F("unk char:"));
				Serial.print(b);
				clear_rx_buffer();
				break;
          }
        }
     }
  }
}
void pir_tester()
{
   byte b_now = 0;
   byte b_last = 0;
   if(pir_triggered()) b_last += 1;
   if(!digitalRead(TRIGGER_TTL_PIN)) b_last += 2;

   if(pir_ok == false)
   {
	   Serial.println(F("PIR not available"));
	   Serial.println(F("PIR test aborted"));
	   return;
   }
   Serial.print(F("PIR Normally Hi/Low: "));
   Serial.println(PIR_ANALOG_NORMALLY);
   Serial.println(F("Starting PIR tester"));

   delay(500);
   //pretty much any serial message sent will break out of this loop
   while (Serial.available() == 0)
   {
	   b_now = 0;
	   if(pir_triggered()) b_now += 1;
	   if(!digitalRead(TRIGGER_TTL_PIN)) b_now += 2;
	   if(b_now != b_last)
	   {
		   b_last = b_now;
		   Serial.print(b_now);
	   }
	   delay(50);
   }
   clear_rx_buffer();
}
//read 4 bytes from the Serial buffer and return as a 32bit integer
unsigned long SerialInt()
{
	byte b[4];
	for(int i = 0; i < 4; i++)
	{
		while(Serial.available() == 0){}
		b[i] = Serial.read();
	}
	return (unsigned long)(((unsigned long)b[3] << 24) | ((unsigned long)b[2] << 16) | ((unsigned long)b[1] << 8) | (b[0]));
}
void rx_memory()
{
	while(Serial.available() == 0){}
	int howmany = SerialInt();

	for(int i = 0; i < howmany; i++)
	{
		int waited = 0;
		while(Serial.available() == 0)
		{
			waited++;
			if(waited > 1000)
			{
				Serial.print(F("Stuck on: "));
				Serial.println(i);
				Serial.println(F("Upload terminated, please retry"));
				return;
			}
			delay(1);
		}
		byte b = Serial.read();
		if(i < 1000) EEPROM.write(i,b);
	}

	Serial.print(F("Uploaded eeprom with "));
	Serial.print(howmany);
	Serial.println(F(" bytes"));
	read_config();
	set_ambient_out();
	report_config();
	check_pir();
	buffer_samples(); //transfer into working buffer
}

void tx_memory()
{
	//tell them it is coming
	Serial.print(F("@EDUMP1000"));
	delay(1000);
	for(int i = 0; i < 1000; i++)
	{
		byte b = EEPROM.read(i);
		Serial.write(b);
		delay(1);
	}
}
void clear_rx_buffer()
{
	while(Serial.available() != 0)
	{
		 byte b = Serial.read();
		 delay(1);
	}
}

Credits

Eric G

Eric G

2 projects • 1 follower
Eric Garthwait, Creative thinker project obsessed electronics hobbyist. Special effects and animatronics connoisseur.

Comments