/* Arduino code to test and exercise Whole House Telephone Intercom circuit */ /* Version 1.0, June 6, 2009 */ /* Copyright (c) 2009, Joseph E. Doll */ /* You may use this code and derivative works for personal, non-profit applications. */ /* You may distribute copies without charge to others, so long as this copyright notice remains intact and modifications are clearly identified. */ /* You may not distribute this code, or any part or embodiment of it, in association with any product or service for which you charge money */ /* Original distribution point: http://joes.com/intercom/ATI_Test10.txt (ATI = "Arduino Telephone Intercom") */ /* Code verified on Arduino Duemilanove with ATmega328 */ /* This test code was derived from an early (pre-release) version of intercom operating code. */ /* It should help you verify operation of your newly built intercom circuit, and quickly track down any errors. */ /* Special tests and exercises are included in the Setup() section. You will have to read the code to see what they do. */ /* You may use measurements you get from this code to re-calibrate the map statements that convert ADC readings to milliamps and volts. */ #include const int kill90V = 2; // Shut off 90V supply pin const int intercom = 3; // Activate Intercom relay, connects local line to internal supply const int hold = 4; // Activate Hold relay, connects telco line to 100 ohm load const int add27mASense = 5; // Extra current to bring loop sense positive. Can be always on, or hook to +5V instead. const int add100VTelco = 6; // Extra current to bring telcoVSense positive. Can be always on, or hook to +5V instead. const int add100VLocal = 7; // Extra current to bring localVSense positive. Can be always on, or hook to +5V instead. const int intercomSense = 8; // Contact closure to ground when intercom relay active const int holdSense = 9; // Contact closure to ground when hold relay active const int led = 13; // Standard output pin for Arduino LED const int loopSense = 0; // Analog in pin, loop current through 4.7 ohms const int localVSense = 1; // Analog in pin, scaled value of local line voltage const int telcoVSense = 2; // Analog in pin, scaled value of telco line voltage const int P90VSense = 4; // Analog in pin, scaled value of 90V after limiting resistors const int ringFrequency = 18; // Intercom internal ringing frequency, Hz const int ringPattern[] = { 1000, 1000, 1000, 1000, 1000, 3000 }; // On/Off pattern for intercom ringing. 4194 ms max is limit of 16 bit timer 1 const long ringMaxTime = 120000; // Maximum time to ring before exiting intercom mode, in milliseconds. Set to 0 to ring forever. const int RINGING = 1; // Values for ringingStatus const int PULSING = 2; const int SILENT = 3; const int ONHOOK = 0; const int OFFHOOK = 1; const int minOffFlash = 200; // Minimum off hook time to count as off-flash (milliseconds) const int maxOffFlash = 2000; // Maximum off hook time to count as off-flash const int minOnFlash = 200; // Minimum on hook time to count as on-flash const int maxOnFlash = 2000; // Maximum on hook time to count as on-flash int ringPatternLength = sizeof(ringPattern) / sizeof(int); //Number of elements in ringPattern int errorCode = 0; volatile byte ringingStatus = false; volatile int ringPatternPosition = 0; // Pointer to current ringPattern[] element volatile unsigned int timerLoadValue; // For use in ISR // Prepare Timer1 for use as ring pattern generator void setupTimer1() { //Timer1 Settings: Timer Prescaler /1024, mode 0 (normal mode) //Timer clock = 16MHz/1024 = 15,625 Hz or 64us //Maximum overflow time = 65535/15625 = 4.19424 seconds TCCR1A = 0; TCCR1B = 1< */ TCNT1 = initTo10ms; /* Restore global interrupt flag */ SREG = sreg; interrupts(); } //Timer1 overflow interrupt vector handler ISR(TIMER1_OVF_vect) { char sreg; if ( !ringingStatus ) { // Monitor status every 10 ms /* Save global interrupt flag */ sreg = SREG; /* Disable interrupts */ noInterrupts(); /* Set TCNT1 to */ TCNT1 = 65379; // 10 ms to go: 65535 - 156 /* Restore global interrupt flag */ SREG = sreg; interrupts(); } else { int pulseLevel; int pulseNumber; int ringLength; int numPulses; int pulseWidth; // Manipulate kill90V per ringPattern[] and ringFrequency if ( ringingStatus == SILENT ) // Period between rings is complete { ringingStatus = RINGING; ringPatternPosition++; if ( ringPatternPosition >= ringPatternLength ) { ringPatternPosition = 0; } } if ( ringingStatus == RINGING ) // Entering a new ring period { ringLength = ringPattern[ringPatternPosition]; numPulses = ringLength * ringFrequency / 1000; pulseWidth = ( ringLength / 2 ) / numPulses; // 50% duty cycle timerLoadValue = 65535 - ( pulseWidth * 15.625 ); // value to load for duration of 1/2 ringing cycle pulseLevel = HIGH; pulseNumber = 0; ringingStatus = PULSING; } if ( ringingStatus == PULSING ) { if ( pulseLevel == HIGH ) { pulseLevel = LOW; } else { pulseLevel = HIGH; pulseNumber++; } digitalWrite(kill90V, pulseLevel); if ( pulseNumber > numPulses ) // Ringing on-time is complete, begin silent period { ringingStatus = SILENT; ringPatternPosition++; timerLoadValue = 65535 - ( ringPattern[ringPatternPosition] * 15.625 ); } } /* Save global interrupt flag */ sreg = SREG; /* Disable interrupts */ noInterrupts(); /* Set TCNT1 to */ TCNT1 = timerLoadValue; /* Restore global interrupt flag */ SREG = sreg; interrupts(); } } // End TIMER1_OVF_vect void setup() { pinMode(kill90V, OUTPUT); pinMode(hold, OUTPUT); pinMode(intercom, OUTPUT); pinMode(add27mASense, OUTPUT); pinMode(add100VLocal, OUTPUT); pinMode(add100VTelco, OUTPUT); pinMode(led, OUTPUT); long lastOffHook = 0; // Used to detect hook flashes long lastOnHook = 0; int errorCode = 0; digitalWrite(intercomSense, HIGH); // Activate pullup on intercom sense input digitalWrite(holdSense, HIGH); // Activate pullup on hold sense input if ( digitalRead(intercomSense) != HIGH ) { errorCode = 1; } else if ( digitalRead(holdSense) != HIGH ) { errorCode = 2; } //Announce startup by blinking LED 3 times for (int i=0; i < 3; i++) { digitalWrite(led, HIGH); delay(500); digitalWrite(led, LOW); delay(500); } // Begin Test Code Section setupTimer1(); Serial.begin(9600); for (int i=2; i<10; i++) { int value = digitalRead(i); Serial.print(i); Serial.print("="); Serial.println(value); } // Check Intercom Relay, position sense // Verify local out disconnects from telco in for (int i=0; i<10; i++) { digitalWrite(intercom, HIGH); delay(4); // wait for actuation, at least 2ms int value = digitalRead(intercomSense); Serial.print("ON - intercomSense = "); Serial.println(value); delay(500); digitalWrite(intercom, LOW); delay(4); // wait for actuation, at least 2ms value = digitalRead(intercomSense); Serial.print("OFF = intercomSense = "); Serial.println(value); delay(500); } // Check Hold Relay, position sense // Verify telco in connects to 100 ohms for (int i=0; i<10; i++) { digitalWrite(hold, HIGH); delay(4); // wait for actuation, at least 2ms int value = digitalRead(holdSense); Serial.print("ON - holdSense = "); Serial.println(value); delay(500); digitalWrite(hold, LOW); delay(4); // wait for actuation, at least 2ms value = digitalRead(holdSense); Serial.print("OFF = holdSense = "); Serial.println(value); delay(500); } // Check Loop Sense input for (int i=0; i<10; i++) { digitalWrite(add27mASense, HIGH); delay(1); // adds 10% to reading int value = analogRead(loopSense); Serial.print("ON - loopSense = "); Serial.println(value); delay(500); digitalWrite(add27mASense, LOW); delay(1); value = analogRead(loopSense); Serial.print("OFF = loopSense = "); Serial.println(value); delay(500); } // Check Telco V Sense input for (int i=0; i<10; i++) { digitalWrite(add100VTelco, HIGH); delay(1); // stabilize reading int value = analogRead(telcoVSense); Serial.print("ON - telcoVSense, no hold = "); Serial.println(value); delay(500); digitalWrite(add100VTelco, LOW); delay(1); value = analogRead(telcoVSense); Serial.print("OFF = telcoVSense, no hold = "); Serial.println(value); delay(500); } digitalWrite(hold, HIGH); delay(4); for (int i=0; i<10; i++) { digitalWrite(add100VTelco, HIGH); delay(1); // stabilize reading int value = analogRead(telcoVSense); Serial.print("ON - telcoVSense, hold = "); Serial.println(value); delay(500); digitalWrite(add100VTelco, LOW); delay(1); value = analogRead(telcoVSense); Serial.print("OFF = telcoVSense, hold = "); Serial.println(value); delay(500); } digitalWrite(hold, LOW); // Check Local V Sense input for (int i=0; i<10; i++) { digitalWrite(add100VLocal, HIGH); delay(1); // stabilize reading int value = analogRead(localVSense); Serial.print("ON - localVSense, no intercom = "); Serial.println(value); delay(500); digitalWrite(add100VLocal, LOW); delay(1); value = analogRead(localVSense); Serial.print("OFF = localVSense, no intercom = "); Serial.println(value); delay(500); } digitalWrite(intercom, HIGH); delay(4); for (int i=0; i<10; i++) { digitalWrite(add100VLocal, HIGH); delay(1); // stabilize reading int value = analogRead(localVSense); Serial.print("ON - localVSense, intercom = "); Serial.println(value); delay(500); digitalWrite(add100VLocal, LOW); delay(1); value = analogRead(localVSense); Serial.print("OFF = localVSense, intercom = "); Serial.println(value); delay(500); } digitalWrite(intercom, LOW); /* **************** Remove comment delimiters to activate this section ***** // Check 90V Sense *** with no 90V present *** // remove this test when 90V circuit in place // Calc 2 counts, actual 1 count, with intercom relay only digitalWrite(add100VLocal, HIGH); digitalWrite(add100VTelco, HIGH); for (int i=0; i<10; i++) { digitalWrite(intercom, HIGH); delay(4); int value = analogRead(90VSense); Serial.print("intercom, 90VSense = "); Serial.println(value); digitalWrite(hold, HIGH); delay(4); value = analogRead(P90VSense); Serial.print("intercom + hold, 90VSense = "); Serial.println(value); digitalWrite(intercom, LOW); digitalWrite(hold, LOW); delay(4); value=analogRead(P90VSense); Serial.print("neither relay, 90VSense = "); Serial.println(value); } */ // Check kill 90V Control for (int i=0; i<10; i++) { digitalWrite(kill90V, HIGH); delay(1); int value = analogRead(P90VSense); Serial.print("kill90 HIGH, 90VSense = "); Serial.println(value); delay(500); digitalWrite(kill90V, LOW); delay(1); value = analogRead(P90VSense); Serial.print("kill90 LOW, 90VSense = "); Serial.println(value); delay(500); } } void loop() { // If error detected, flash errorCode value indefinitely while ( errorCode ) { for ( int i=0; i -15 ) { hookStatus = ONHOOK; } else { hookStatus = OFFHOOK; } if ( hookStatus != prevHookStatus ) { if ( hookStatus == ONHOOK ) { lastOnHook = millis(); if ( lastOnHook < lastOffHook ) // timer rollover has occurred { /* This is illegal. lastOffHook must be unsigned long, cannot take negative value lastOffHook -= 4294967295; So do the best we can: */ lastOffHook = 0; } offHookTime = lastOnHook - lastOffHook; if ( offHookTime > minOffFlash && offHookTime < maxOffFlash ) { offFlashDetected = true; } } else // hookStatus == OFFHOOK { lastOffHook = millis(); if ( lastOffHook < lastOnHook ) // timer rollover has occurred { /* This is illegal. lastOnHook must be unsigned long, cannot take negative value lastOnHook -= 4294967295; So do the best we can: */ lastOnHook = 0; } onHookTime = lastOffHook - lastOnHook; if ( onHookTime > minOnFlash && onHookTime < maxOnFlash ) { onFlashDetected = true; } } if ( offFlashDetected ) { // ** Turn on Intercom if ( !digitalRead(intercom) ) { digitalWrite(intercom, HIGH); ringingStatus = RINGING; digitalWrite(kill90V, LOW); lastRingStart = millis(); if ( digitalRead(intercomSense) != LOW ) { errorCode = 3; } } } else // No offFlashDedtecte, on hook { // ** Turn off Intercom if ( hookStatus == ONHOOK && digitalRead(intercom) ) { digitalWrite(intercom, LOW); ringingStatus = false; ringPatternPosition = 0; if ( digitalRead(intercomSense) != HIGH ) { errorCode = 4; } } } if ( hookStatus == OFFHOOK && digitalRead(intercom) ) { ringingStatus = false; ringPatternPosition = 0; digitalWrite(kill90V, LOW); } } if ( ringingStatus && ringMaxTime > 0 ) { unsigned long now = millis(); if ( now < lastRingStart ) // rollover has occurred { lastRingStart = 0; // fix lastRingStart value as best we can } else { if ( now - lastRingStart > ringMaxTime ) // Cease ringing and abandon intercom mode { digitalWrite(intercom, LOW); ringingStatus = false; ringPatternPosition = 0; digitalWrite(kill90V, LOW); if ( digitalRead(intercomSense) != HIGH ) { errorCode = 5; } } } } // Potential future exercise: implement Hold mode prevHookStatus = hookStatus; }