#include #include #define DEBUG // Sends various bits of info back to serial port #define TIMEPROCS // Enables estimation of the time taken by interrupts // Use this with the Debug tab in MIT /* RX is digital pin 10 (connect to TX of other device) TX is digital pin 11 (connect to RX of other device) Normal serial used for debugging */ // field definations of the Device Status Array. #define L1Stat 0 // Socket1 percent light #define L1Dimable 1 #define L1Autodim 2 #define L1Triggered 3 #define L1TrigRate 4 // Brightness #define L2Stat 5 #define L2Dimable 6 #define L2Autodim 7 #define L2Triggered 8 #define L2TrigRate 9 #define SoundTrigLow 10 #define SoundTrigHigh 11 #define AutoTimeout 12 // Time for auto dim in mins #define L1TimedOff 13 // Flag for delayed switching off #define L1DelayLow 14 #define L1DelayHi 15 #define L2TimedOff 16 // Flag for delayed switching off #define L2DelayLow 17 #define L2DelayHi 18 #define INFOBYTES 19 // pins were originally selected due to position on micro controller // however over development stages more came into use and logic placement // went out of the window. Sorry about that :) // Pins 0 and 1 left for serial communication // Pins 2 and 3 left for interrupts #define L1PowerPin 4 #define L1TriggedPin 5 #define L1Pin 6 #define L2Pin 7 #define L2PowerPin 8 #define L2TriggedPin 9 // Pins 10 and 11 used for serial communication to Bluetooth device #define PowerPin 12 #define SoundCheckPin 13 // Called it sound check pin as it will stay on then, but its used // to indicate when bluetooth serial activity #define CheckDelay 100 // Delay percent for the timer in micro seconds // Delay note: 50 hz is 100 1/2 cycles per second making each 1/2 cycles // 10 ms. 1% of this is 100 micro seconds. At the start of each 1/2 cycle // Percent is set at 100(%) and decremented over each 1/2 cycle String DevName; // Name for this device, gets displayed on App bool Time1Running, Time2Running; // Flags to show timers running bool TriggerActive; // Flag to show a trigger is active volatile unsigned long Time1End; // to hold counter timers volatile unsigned long Time2End; volatile int L1AutoCount; // Counters for Auto dim. Count is a countdown volatile int L1AutoNum; // counter and Num is the starting point for count volatile bool L1AutoFlag; // Flag indicating if auto dim is running volatile int L2AutoCount; // Counters for Auto dim volatile int L2AutoNum; volatile bool L2AutoFlag; volatile bool DoingSoundCheck; volatile int TrigSoundLevel = 0; volatile byte DevStatus[INFOBYTES]; // App values volatile byte CheckFlag; // Used to test if testing is needed volatile byte Percent; // Percentage of half cycle left volatile unsigned long TimerTick; // Used by my timer routines // Debug Variables left in to aid further development #ifdef TIMEPROCS volatile int CrossMic; // holds time taken approx for interrupt volatile int PerMic; // and again volatile unsigned long LongTemp; // Used in debug mode #endif SoftwareSerial mySerial(10, 11); // RX, TX void setup() { noInterrupts(); // Disable till we setup to handle them attachInterrupt( 0, CrossOver, CHANGE ); // This will setup the percent // Pins 0 and 1 left for serial communication // Pins 2 and 3 left for interrupts pinMode( L1PowerPin, OUTPUT); pinMode( L1TriggedPin, OUTPUT); pinMode( L1Pin, OUTPUT); // Setup pins pinMode( L2Pin, OUTPUT); pinMode( L2PowerPin, OUTPUT); pinMode( L2TriggedPin, OUTPUT); // Pins 10 and 11 used for serial communication to Bluetooth device pinMode( PowerPin, OUTPUT); pinMode( SoundCheckPin, OUTPUT); digitalWrite( PowerPin, HIGH ); // indicate power // The pin for analog input will be A5, however i have not put it as a 'define' statement. // the reason for this is im going to hard code the registers for auto triggering. // the 'analogRead' function can take up to 100 milli seconds, too long....... // Please dont mess unless you know what your doing. pinMode( A5, INPUT ); DIDR0 = 0b00100000; ADMUX = 0b01000101; ADCSRA = 0b10100111; ADCSRB = 0; ADCSRA |= 0b01000000; digitalWrite(L1Pin, LOW); // and make sure power is off digitalWrite(L2Pin, LOW); CheckFlag = 0; // On initial startup nothing to check Time1Running = false; // Make sure no timers running Time2Running = false; for ( int x = 0; x < INFOBYTES; x++ ) { // Zero all values DevStatus[x] = 0; } DevStatus[AutoTimeout] = 5; // need to set minimum to > 5 DoingSoundCheck = false; interrupts(); // Allow interrupts before starting timer1 DevName = String("Unset"); // Predefine device name #ifdef DEBUG Serial.begin(9600); while (!Serial) { }; Serial.println("We Ready"); #endif mySerial.begin(9600); Timer1.initialize(CheckDelay); Timer1.attachInterrupt(PerCentCheck); // } void loop() { // run over and over byte Temp, Temp1; // Temporary storage bool TempBool; // Temporary flag float AutoValue; // Used for percent calculations int last1Auto, last2Auto; // Used to check if autodim changed int last1Timed, last2Timed; // Check if timed changed int SensorValue; // Current sensor reading value // The value of codes sent are as follows; // 0..64 are offsets into the device status array // 65..90 ('A'..'Z') are functions // > 96 are debugging routines, most will be removed but usefull ones will remain if (mySerial.available()) { // indicate serial comms digitalWrite( PowerPin, LOW ); digitalWrite( SoundCheckPin, HIGH ); // get values of those that will make immediate changes when changed last1Auto = DevStatus[L1Autodim]; last2Auto = DevStatus[L2Autodim]; last1Timed = DevStatus[L1TimedOff]; last2Timed = DevStatus[L2TimedOff]; Temp = mySerial.read(); #ifdef DEBUG Serial.print("Got --> "); Serial.println( Temp ); #endif if ( Temp < 65 ) { WaitSer(); DevStatus[Temp] = mySerial.read(); // check to see if we getting a 2 byte number if (( Temp == 10 ) || ( Temp == 14 ) || ( Temp == 17 )) { #ifdef DEBUG Serial.println(DevStatus[Temp]); #endif WaitSer(); DevStatus[Temp + 1] = mySerial.read(); // high byte #ifdef DEBUG Serial.println(DevStatus[Temp + 1]); #endif if ( Temp == 10 ) { // sound trigger level TrigSoundLevel = ( DevStatus[11] * 256 ) + DevStatus[10]; } } DoingSoundCheck = ((DevStatus[L1Triggered] > 0 ) || (DevStatus[L2Triggered] > 0 )); } else { switch (Temp) { case 65 : // A SendName(); break; case 66 : // B SetName(); break; case 67 : // C SendStatus(); break; case 68 : // D DoingSoundCheck = true; do { WaitSer(); Temp1 = mySerial.read(); if ( Temp1 != 68 ) { #ifdef DEBUG SensorValue = ADCL + ( 256 * ADCH ); Serial.println(SensorValue); mySerial.write( lowByte(SensorValue)); mySerial.write( highByte(SensorValue)); #else mySerial.write( ADCL ); mySerial.write( ADCH ); #endif } } while (Temp1 != 68 ); DoingSoundCheck = false; break; #ifdef TIMEPROCS case 97 : // a - get crossover time mySerial.write( lowByte(CrossMic)); mySerial.write( highByte(CrossMic)); break; case 98 : // b - get Percent time mySerial.write( lowByte(PerMic)); mySerial.write( highByte(PerMic)); break; #endif } } // Test to see if an auto dim has changed // -------------------------------------- if ( last1Auto != DevStatus[L1Autodim]) { if ( DevStatus[L1Autodim] == 1 ) { AutoValue = DevStatus[L1Stat] / DevStatus[AutoTimeout]; AutoValue = 6000 / AutoValue; L1AutoNum = (int) AutoValue; L1AutoCount = L1AutoNum; L1AutoFlag = true; } else { DevStatus[L1Autodim] = 0; L1AutoFlag = false; } } if ( last2Auto != DevStatus[L2Autodim]) { if ( DevStatus[L2Autodim] == 1 ) { AutoValue = DevStatus[L2Stat] / DevStatus[AutoTimeout]; AutoValue = 6000 / AutoValue; L2AutoNum = (int) AutoValue; L2AutoCount = L2AutoNum; L2AutoFlag = true; } else { DevStatus[L2Autodim] = 0; L2AutoFlag = false; } } // Test to see if a timed switch off has changed // --------------------------------------------- if ( last1Timed != DevStatus[L1TimedOff]) { // Test for change if ( DevStatus[L1TimedOff] == 1 ) { // Timer switched on Time1End = (6000 * ( DevStatus[L1DelayLow] + ( 256 * DevStatus[L1DelayHi] ))) + TimerTick; Time1Running = true; } else { Time1Running = false; } } if ( last2Timed != DevStatus[L2TimedOff]) { // Test for change if ( DevStatus[L2TimedOff] == 1 ) { // Timer switched on Time2End = (6000 * ( DevStatus[L2DelayLow] + ( 256 * DevStatus[L2DelayHi] ))) + TimerTick; Time2Running = true; } else { Time2Running = false; } } digitalWrite( PowerPin, HIGH ); digitalWrite( SoundCheckPin, LOW ); } // for those bewilded here is the end of the testing of serial char available :) // Now check if any timers running if ( Time1Running ) { // Check if timer running if ( TimerTick >= Time1End ) { // if so check times over Time1Running = false; // Stop checking DevStatus[L1TimedOff] = 0; // Flag timed as off DevStatus[L1Stat] = 0; // Flag status as off } } if ( Time2Running ) { if ( TimerTick >= Time2End ) { Time2Running = false; DevStatus[L2TimedOff] = 0; DevStatus[L2Stat] = 0; } } // Eventually TimerTick will count over which could cause unexpected delays // so if we not using any timers then reset timertick to 0 if ( !( Time1Running || Time2Running )) { TimerTick = 0; } // Check for any triggers if (( DevStatus[L1Triggered] > 0 ) || ( DevStatus[L2Triggered] > 0 ) ) { SensorValue = ADCL + ( 256 * ADCH ); if ( SensorValue > ( DevStatus[SoundTrigLow] + ( 256 * DevStatus[SoundTrigHigh]))) { if ( DevStatus[L1Triggered] ) { DevStatus[L1Triggered] = 0; DevStatus[L1Stat] = DevStatus[L1TrigRate]; } if ( DevStatus[L2Triggered] ) { DevStatus[L2Triggered] = 0; DevStatus[L2Stat] = DevStatus[L2TrigRate]; } } } // Indicate any triggered output if ( DevStatus[L1Triggered] > 0 ) { digitalWrite(L1TriggedPin, HIGH ); } else { digitalWrite(L1TriggedPin, LOW ); } if ( DevStatus[L2Triggered] > 0 ) { digitalWrite(L2TriggedPin, HIGH ); } else { digitalWrite(L2TriggedPin, LOW ); } // And finally indicate power status if ( DevStatus[L1Stat] > 0 ) { digitalWrite(L1PowerPin, HIGH ); } else { digitalWrite(L1PowerPin, LOW ); } if ( DevStatus[L2Stat] > 0 ) { digitalWrite(L2PowerPin, HIGH ); } else { digitalWrite(L2PowerPin, LOW ); } } // End of 'Loop' procedure // ----------------- // CrossOver is fired when the ac input signal passes through 0 volts // ----------------- void CrossOver() { #ifdef TIMEPROCS LongTemp = micros(); // Used to help estimate time taken for routine #endif TimerTick++; // Increase tick count for timed switch off Percent = 100; // Set start of half cycle CheckFlag = 0; // Clear flag of what we need to test digitalWrite(L1Pin, LOW); // Have both off by default digitalWrite(L2Pin, LOW); if ( DevStatus[L1Stat] > 0 ) { // Setup check flag CheckFlag = 1; } if ( DevStatus[L2Stat] > 0 ) { CheckFlag = CheckFlag + 2; } // now for auto dimmers. // We have already worked out the 'count' needed to lower the // output by 1% if ( L1AutoFlag ) { // See if l1 autodimmed L1AutoCount --; // if so decrease inner timer count if ( L1AutoCount == 0 ) { // if count is zero we decrease L1Stat by 1% DevStatus[L1Stat]--; if ( DevStatus[L1Stat] == 0 ) { // see if we finished dimming L1 L1AutoFlag = false; // if so clear its flag DevStatus[L1Autodim] = 0; // and update status in status array } else { L1AutoCount = L1AutoNum; // otherwise reload inner count } } } if ( L2AutoFlag ) { // Same for L2 L2AutoCount --; if ( L2AutoCount == 0 ) { DevStatus[L2Stat]--; if ( DevStatus[L2Stat] == 0 ) { L2AutoFlag = false; DevStatus[L2Autodim] = 0; } else { L2AutoCount = L2AutoNum; } } } #ifdef TIMEPROCS CrossMic = micros() - LongTemp; #endif } void PerCentCheck() { byte ChangeAmount = 0; // Value to update CheckFlag #ifdef TIMEPROCS LongTemp = micros(); #endif if ( Percent > 0 ) { // ensure we dont go negative Percent--; } if ( CheckFlag > 0 ) { // now test the 3 combinations possible switch (CheckFlag ) { // and switch on where needed. case 1 : if ( DevStatus[L1Stat] >= Percent ) { digitalWrite(L1Pin, HIGH); ChangeAmount = 1; } break; case 2 : if ( DevStatus[L2Stat] >= Percent ) { digitalWrite(L2Pin, HIGH); ChangeAmount = 2; } break; case 3 : if ( DevStatus[L1Stat] >= Percent ) { digitalWrite(L1Pin, HIGH); ChangeAmount = 1; } if ( DevStatus[L2Stat] >= Percent ) { digitalWrite(L2Pin, HIGH); ChangeAmount = ChangeAmount + 2; } break; } CheckFlag = CheckFlag - ChangeAmount; // update check flag for next time } // incase we switched one on #ifdef TIMEPROCS PerMic = micros() - LongTemp; #endif } void WaitSer() { // waiting for data from BT int x = 0; // Dummy variable while (!(mySerial.available())) { x ++; // This does nothing, gives a chance for interrupts } } void SendName() { // sends the device name to the app mySerial.write( DevName.length()); mySerial.print( DevName ); } void SetName() { // set device name WaitSer(); byte x = mySerial.read(); // first byte is length of name DevName = String(""); while ( x > 0 ) { // now get the chars. WaitSer(); DevName += (char)mySerial.read(); x--; } } void SendStatus() { for ( int x = 0; x < INFOBYTES; x++ ) { mySerial.write( DevStatus[x] ); } }