/* Arduino code for Whole House Telephone Intercom */ /* Version 2.0, June 14, 2009 - Add Intercom/Hold mode */ /* 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/ATI10.txt (ATI = "Arduino Telephone Intercom") */ /* Code verified on Arduino Duemilanove with ATmega328 */ /* Revision History */ /* Version 1.1, June 13, 2009 - Improve hook detection immunity to disturbances on Telco line */ /* Version 1.0, June 5, 2009 */ #include const int serialReport = true; // Set to false if serial reporting/debugging messages not desired 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 hvTo90 = 10; // When low, raises high voltage to ~160 V. Leave low (voltage high) when safe (intercom off) to minimize power dissipation. 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 NORMAL = 0; // Values for opMode const int INTERCOM = 1; const int INTHOLD = 2; // Values for onFlashProtect const int BLOCK1 = 1; // Blocking first (potential) on-flash const int AWAIT2 = 2; // Single flash blocked, awaiting 2nd const int BLOCK2 = 3; // Blocking second (potential) on-flash const int TELCOFLASH = 4; // Generating off-flash to Telco const int CHECKHOLDEND = 5; // Delay Intercom/Hold ringing while terminating on-flash may be in progress const int NOTHOLDEND = 6; // OK to ring in Intercom/Hold mode const int ringFrequency = 20; // 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 TERMINATED = 4; 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 const int hookVThreshold = 250; // Corresponds to ~1.22V at localVSense. Expect ~1V off hook, 2V on hook with 90V applied const int telcoOnFlash = 750; // Period to flash Telco when double on-flash is detected int ringPatternLength = sizeof(ringPattern) / sizeof(int); //Number of elements in ringPattern int errorCode = 0; volatile int ringingStatus = false; volatile int ringPatternPosition = 0; // Pointer to current ringPattern[] element volatile unsigned int timerLoadValue; // For use in ISR // Variables used in ISR int pulseLevel; int pulseNumber; int ringLength; int numPulses; int pulseWidth; int localVoltage; int termConfirm = false; // Confirm off-hook ringing termination // Variables used in loop(): int loopReading; // For loopSense readings int loopCurrent; // Map loopReading to mA int hookStatus; // ONHOOK or OFFHOOK int prevHookStatus; // For detecting hook status change unsigned long lastOnHook; // For detecting flash periods unsigned long lastOffHook; // For detecting flash periods long offHookTime = 0; // For detecting flash periods long onHookTime = 0; // For detecting flash periods unsigned long lastRingStart = 0; int localV; // Local line voltage int telcoV; // Telephone line voltage int telcoReading; // ADC reading on telcoVSense int localReading; // ADC reading on localVSense int prevRingingStatus; // Debounce ringingStatus int hookDetector; // Debugging variable, determine where hook status detected int telcoJumper = false; // Set true in setup() if power supply is jumpered to Telco line (for debugging) int opMode = 0; // Operating Mode (normal, intercom, intercom/hold) int onFlashProtect = false; // Protect Telco line from intercom on hook flashes long onFlashProtectStart = 0; // 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 || ( ringingStatus == TERMINATED ) ) { // 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 { // Manipulate kill90V per ringPattern[] and ringFrequency // Manipulate hvTo90 for maximum ring voltage during pulsing if ( ringingStatus == SILENT ) // Period between rings is complete, was set to SILENT on previous interrupt { 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 = LOW; pulseNumber = 0; ringingStatus = PULSING; digitalWrite(hvTo90, LOW); // Allow high voltage to rise to maximum during ring pulsing } if ( ringingStatus == PULSING ) { if ( pulseLevel == HIGH ) { pulseLevel = LOW; pulseNumber++; } else { pulseLevel = HIGH; // Check for low voltage on localVSense, indicating off-hook condition if ( pulseNumber > 0 ) // Avoid 1st pulse - experience shows low readings somtimes { localVoltage = analogRead(localVSense); if ( localVoltage < hookVThreshold ) // Intercom phone is off hook, stop ringing { // Check current first loopReading = analogRead(loopSense); loopCurrent = map( loopReading, 23, 46, 0, 27); //Measures 23 counts with add27mASense applied if ( loopCurrent >= 15 ) // Yep, really is off hook { if ( termConfirm ) // Require off hook detection on two successive pulses as glitch protection { ringingStatus = TERMINATED; digitalWrite(hvTo90, HIGH); digitalWrite(kill90V, LOW); timerLoadValue = 65379; // Check ringing status again in 10 mS termConfirm = false; } else { termConfirm = true; } } } else { termConfirm = false; } } } if ( ringingStatus && ( ringingStatus != TERMINATED ) ) // Not off hook, still ringing { digitalWrite(kill90V, pulseLevel); if ( pulseNumber >= numPulses ) // Ringing on-time is complete, begin silent period { ringingStatus = SILENT; digitalWrite(hvTo90, HIGH); // Minimize high voltage period 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); digitalWrite(hvTo90, HIGH); // Assume safe level in case jumper is present pinMode(hvTo90, OUTPUT); pinMode(hold, OUTPUT); pinMode(intercom, OUTPUT); pinMode(add27mASense, OUTPUT); pinMode(add100VLocal, OUTPUT); pinMode(add100VTelco, OUTPUT); pinMode(led, OUTPUT); digitalWrite(intercomSense, HIGH); // Activate pullup on intercom sense input digitalWrite(holdSense, HIGH); // Activate pullup on hold sense input digitalWrite(add27mASense, HIGH); // Use this line at all times digitalWrite(add100VLocal, HIGH); // And this one digitalWrite(add100VTelco, HIGH); // And this one if ( digitalRead(intercomSense) != HIGH ) { errorCode = 1; } else if ( digitalRead(holdSense) != HIGH ) { errorCode = 2; } telcoReading = analogRead(telcoVSense); telcoV = map( telcoReading, 203, 370, 0, 89 ); // Measures 203 counts with add100VTelco applied, 370 counts with 89V applied if ( telcoV > 50 ) // Telco line could be jumpered to internal power supply, test if it drops when internal supply killed { digitalWrite(kill90V, HIGH); delay(2); telcoReading = analogRead(telcoVSense); digitalWrite(kill90V, LOW); // Don't leave this on longer than necessary telcoV = map( telcoReading, 203, 370, 0, 89 ); // Measures 203 counts with add100VTelco applied, 370 counts with 89V applied if ( telcoV < 10 ) // Must be from internal supply { telcoJumper = true; } } if ( !telcoJumper ) { digitalWrite(hvTo90, LOW); // Minimize power dissipation for normal, unjumpered operation } //Announce startup by blinking LED 3 times for (int i=0; i < 3; i++) { digitalWrite(led, HIGH); delay(500); digitalWrite(led, LOW); delay(500); } setupTimer1(); if ( serialReport ) { Serial.begin(9600); Serial.print("Setup complete. Telco Jumper = "); Serial.print(telcoJumper); if ( digitalRead(hvTo90) ) Serial.println(". Power Supply 90V."); else Serial.println(". Power Supply High."); } } void loop() { // If error detected, flash errorCode value indefinitely if ( serialReport && errorCode ) { Serial.print("Error Code "); Serial.println(errorCode); } while ( errorCode ) { // Assure intercom is off, voltages to nominal ringingStatus = false; digitalWrite(intercom, LOW); digitalWrite(hold, LOW); delay(4); if ( ( digitalRead(intercomSense) == HIGH ) && ( telcoJumper == false ) ) // Intercom relay is released, no jumper { digitalWrite(hvTo90, LOW); // Minimize power dissipation } else { digitalWrite(hvTo90, HIGH); // Do not apply high voltage to attached phone } digitalWrite(kill90V, LOW); // Display error code for ( int i=0; i 15 ) { hookStatus = ONHOOK; hookDetector = 3; } else { hookStatus = prevHookStatus; hookDetector = 0; } } else // high loop current { if ( localV < 15 ) { hookStatus = OFFHOOK; hookDetector = 3; } else { hookStatus = prevHookStatus; hookDetector = 0; } } } else { delay(50); // Wait for ringing transients to dissipate } if ( ringingStatus != SILENT ) // Ringing status has changed, so discard (potenitally corrupted) hook status { hookStatus = prevHookStatus; hookDetector = 0; } if ( serialReport ) { localReading = analogRead(localVSense); localV = map( localReading, 197, 365, 0, 89 ); // Measures 197 with telcoVSense applied, 365 with 89V (and telcoVSense) applied telcoReading = analogRead(telcoVSense); telcoV = map( telcoReading, 203, 370, 0, 89 ); // Measures 203 counts with add100VTelco applied, 370 counts with 89V applied } } else // Line in steady state { loopReading = analogRead(loopSense); loopCurrent = map( loopReading, 23, 46, 0, 27); //Measures 23 counts with add27mASense applied if ( digitalRead(intercom) ) { localReading = analogRead(localVSense); localV = map( localReading, 197, 365, 0, 89 ); // Measures 197 with telcoVSense applied, 365 with 89V (and telcoVSense) applied if ( loopCurrent < 15 ) { if ( localV > 15 ) { hookStatus = ONHOOK; hookDetector = 4; } else { hookStatus = prevHookStatus; hookDetector = 0; } } else // high loop current { if ( localV < 15 ) { hookStatus = OFFHOOK; hookDetector = 4; } else { hookStatus = prevHookStatus; hookDetector = 0; } } if ( serialReport ) { telcoReading = analogRead(telcoVSense); telcoV = map( telcoReading, 203, 370, 0, 89 ); // Measures 203 counts with add100VTelco applied, 370 counts with 89V applied } } else // intercom relay off { if ( ( loopCurrent < 15 ) && ( loopCurrent > -15 ) ) { telcoReading = analogRead(telcoVSense); telcoV = map( telcoReading, 203, 370, 0, 89 ); // Measures 203 counts with add100VTelco applied, 370 counts with 89V applied if ( telcoV > 15 || telcoV < -15 ) // Voltage is high { hookStatus = ONHOOK; hookDetector = 6; } else { hookStatus = prevHookStatus; } if ( serialReport ) { localReading = analogRead(localVSense); localV = map( localReading, 197, 365, 0, 89 ); // Measures 197 with telcoVSense applied, 365 with 89V (and telcoVSense) applied telcoReading = analogRead(telcoVSense); telcoV = map( telcoReading, 203, 370, 0, 89 ); // Measures 203 counts with add100VTelco applied, 370 counts with 89V applied } } else // Could be off hook, but assure voltage is low so current is not due to telco ringing { delay(2); // Reconfirm loop current with averaged value - could be from Telco line noise or audio loopReading = analogRead(loopSense); int loopReadings[31]; for ( int i = 0; i < 31; i++ ) { delay(1); loopReadings[i] = analogRead(loopSense); loopReading = loopReading + loopReadings[i]; } loopReading = loopReading / 32; loopCurrent = map( loopReading, 23, 46, 0, 27); //Measures 23 counts with add27mASense applied telcoReading = analogRead(telcoVSense); telcoV = map( telcoReading, 203, 370, 0, 89 ); // Measures 203 counts with add100VTelco applied, 370 counts with 89V applied if ( ( telcoV > 15 ) || ( telcoV < -15 ) ) // Voltage is high { hookStatus = ONHOOK; hookDetector = 7; } else if ( ( ( telcoV > 0 ) && ( loopCurrent > 15 ) ) || ( ( telcoV < 0 ) && ( loopCurrent < -15 ) ) ) // Current, voltage polarities match { hookStatus = OFFHOOK; hookDetector = 7; } else { hookStatus = prevHookStatus; hookDetector = 0; } if ( serialReport ) { localReading = analogRead(localVSense); localV = map( localReading, 197, 365, 0, 89 ); // Measures 197 with telcoVSense applied, 365 with 89V (and telcoVSense) applied } } } } if ( onFlashProtect == CHECKHOLDEND ) // Check for expiry { unsigned long now = millis(); if ( now < onFlashProtectStart ) // rollover has occurred { onFlashProtectStart = 0; // fix onFlashProtectStart value as best we can } if ( ( now - onFlashProtectStart ) > maxOnFlash ) // Hold end negated. Begin ringing if still on hook { onFlashProtect = false; if ( ( opMode == INTHOLD ) && ( hookStatus == ONHOOK ) ) { onFlashProtect = false; onFlashProtectStart = 0; digitalWrite(hvTo90, HIGH); digitalWrite(kill90V, LOW); ringingStatus = RINGING; lastRingStart = millis(); } } } else if ( onFlashProtect == BLOCK2 ) // Check for expiry { unsigned long now = millis(); if ( now < onFlashProtectStart ) // rollover has occurred { onFlashProtectStart = 0; // fix onFlashProtectStart value as best we can } if ( ( now - onFlashProtectStart ) > maxOnFlash ) // Double on-flash negated. Keep relays active, recognize INTHOLD and start ringing { opMode = INTHOLD; onFlashProtect = false; onFlashProtectStart = 0; digitalWrite(hvTo90, HIGH); // Assure power supply not at high level when intercom on delay(2); // Wait for hi V to reduce digitalWrite(intercom, HIGH); digitalWrite(kill90V, LOW); delay(4); // Assure intercom relay has time to pull in if ( digitalRead(intercomSense) != LOW ) { errorCode = 13; } if ( hookStatus == ONHOOK ) { ringingStatus = RINGING; // Should be on hook, but don't start ringing if not! lastRingStart = millis(); } if ( serialReport ) { Serial.print("Intercom Hold mode, BLOCK2 expired. "); Serial.println("Power Supply 90V./Ringing"); } } } else if ( onFlashProtect == AWAIT2 ) // Check for expiry { unsigned long now = millis(); if ( now < onFlashProtectStart ) // rollover has occurred { onFlashProtectStart = 0; // fix onFlashProtectStart value as best we can } if ( ( now - onFlashProtectStart ) > maxOffFlash ) // Double off-flash negated, recognize INTHOLD mode, activate relays. { opMode = INTHOLD; onFlashProtectStart = 0; onFlashProtect = false; digitalWrite(hvTo90, HIGH); digitalWrite(kill90V, LOW); digitalWrite(intercom, HIGH); digitalWrite(hold, HIGH); if ( serialReport ) Serial.println("INTHOLD mode active, Flash protect expired AWAIT2, onFlashProtect = false"); } } else if ( onFlashProtect == BLOCK1 ) // check for expiry { unsigned long now = millis(); if ( now < onFlashProtectStart ) // rollover has occurred { onFlashProtectStart = 0; // fix onFlashProtectStart value as best we can } if ( ( now - onFlashProtectStart ) > maxOnFlash ) // Off-flash negated. Cancel blocking, remain in NORMAL mode { onFlashProtect = false; onFlashProtectStart = 0; if ( opMode != NORMAL ) errorCode = 14; digitalWrite(intercom, LOW); digitalWrite(hold, LOW); if ( telcoJumper ) digitalWrite(hvTo90, HIGH); else digitalWrite(hvTo90, LOW); digitalWrite(kill90V, LOW); if ( serialReport ) Serial.println("BLOCK1 Expired, Normal mode."); } } if ( hookStatus != prevHookStatus ) { if ( hookStatus == ONHOOK ) { lastOnHook = millis(); if ( serialReport ) { Serial.print("On Hook @ "); Serial.print(hookDetector); } digitalWrite(led, HIGH); delay(10); digitalWrite(led, LOW); 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 ( serialReport ) { Serial.print(", offHookTime = "); Serial.print(offHookTime); Serial.print(", "); Serial.print(loopCurrent); Serial.print("mA, localV = "); Serial.print(localV); Serial.print(", telcoV = "); Serial.print(telcoV); Serial.print(", ringingStatus = "); Serial.print(ringingStatus); Serial.print(", prevRingingStatus = "); Serial.println(prevRingingStatus); } if ( ( offHookTime > minOffFlash ) && ( offHookTime < maxOffFlash ) ) { offFlashDetected = true; if ( serialReport ) Serial.println("Off Flash Detected."); } } else // hookStatus == OFFHOOK { lastOffHook = millis(); // Flash LED twice for off hook event digitalWrite(led, HIGH); delay(10); digitalWrite(led, LOW); delay(50); digitalWrite(led, HIGH); delay(10); digitalWrite(led, LOW); 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 ( serialReport ) { Serial.print("Off Hook @ "); Serial.print(hookDetector); Serial.print(", onHookTime = "); Serial.print(onHookTime); Serial.print(", "); Serial.print(loopCurrent); Serial.print("mA, localV = "); Serial.print(localV); Serial.print(", telcoV = "); Serial.print(telcoV); Serial.print(", ringingStatus = "); Serial.print(ringingStatus); Serial.print(", prevRingingStatus = "); Serial.println(prevRingingStatus); } if ( ( onHookTime > minOnFlash ) && ( onHookTime < maxOnFlash ) ) { onFlashDetected = true; if ( serialReport ) Serial.println("On Flash Detected."); } } if ( offFlashDetected && ( opMode == NORMAL ) && !onFlashProtect ) { // ** Turn on Intercom, set INTERCOM mode opMode = INTERCOM; digitalWrite(hvTo90, HIGH); // Assure power supply not at high level when intercom on delay(2); // Wait for hi V to reduce digitalWrite(intercom, HIGH); ringingStatus = RINGING; digitalWrite(kill90V, LOW); lastRingStart = millis(); delay(4); // Assure intercom relay has time to pull in if ( digitalRead(intercomSense) != LOW ) { errorCode = 3; } if ( serialReport ) { Serial.print("Intercom mode. "); Serial.println("Power Supply 90V./Ringing"); } } else if ( ( hookStatus == ONHOOK ) && ( opMode == INTERCOM ) ) { // ** Turn off Intercom, set NORMAL mode opMode = NORMAL; digitalWrite(intercom, LOW); ringingStatus = false; ringPatternPosition = 0; digitalWrite(hvTo90, HIGH); // Assure boosted high voltage is off digitalWrite(kill90V, LOW); // And 90V not turned off delay(4); // Assure intercom relay has time to release if ( digitalRead(intercomSense) != HIGH ) { errorCode = 4; } else if ( telcoJumper == false ) { digitalWrite(hvTo90, LOW); // Minimize power dissipation while intercom is inactive } if ( serialReport ) { Serial.print("Normal mode. "); if ( digitalRead(hvTo90) ) Serial.println("Power Supply 90V."); else Serial.println("Power Supply High."); } } else if ( ( hookStatus == ONHOOK ) && ( opMode == NORMAL ) && ( onFlashProtect == false ) ) // Block potential on flash from reaching Telco line { onFlashProtect = BLOCK1; digitalWrite(hold, HIGH); onFlashProtectStart = millis(); // With hold relay in, Telco power must be replaced with intercom power for hook status detection digitalWrite(hvTo90, HIGH); digitalWrite(kill90V, LOW); digitalWrite(intercom, HIGH); delay(4); if ( digitalRead(holdSense) != LOW ) errorCode = 6; if ( serialReport ) Serial.println("onFlashProtect = BLOCK1"); } else if ( ( hookStatus == ONHOOK ) && ( opMode == INTHOLD ) && ( onFlashProtect == false ) ) // Ringing starts when CHECKHOLDEND expires { onFlashProtect = CHECKHOLDEND; onFlashProtectStart = millis(); if ( digitalRead(intercomSense) != LOW ) errorCode = 9; // Intercom relay should already be active } else if ( ( hookStatus == OFFHOOK ) && ( onFlashProtect == BLOCK1 ) && !onFlashDetected ) { unsigned long now = millis(); if ( now < onFlashProtectStart ) // rollover has occurred { onFlashProtectStart = 0; // fix onFlashProtectStart value as best we can } if ( ( now - onFlashProtectStart ) < minOnFlash ) // On-flash negated, stop blocking { digitalWrite(hold, LOW); onFlashProtectStart = 0; onFlashProtect = false; delay(4); if ( digitalRead(holdSense) != HIGH ) errorCode = 7; if ( serialReport ) Serial.println("onFlashProtect = false"); } } else if ( ( hookStatus == ONHOOK ) && ( onFlashProtect == AWAIT2 ) ) // Activate relays, transition to BLOCK2 { onFlashProtect = BLOCK2; onFlashProtectStart == millis(); // Wait until BLOCK2 expiry to ring digitalWrite(hvTo90, HIGH); digitalWrite(kill90V, LOW); digitalWrite(hold, HIGH); digitalWrite(intercom, HIGH); if ( serialReport ) Serial.println("onFlashProtect = BLOCK2"); } else if ( ( onFlashProtect == BLOCK2 ) && onFlashDetected ) // Double on-flash. On-flash Telco, continue NORMAL mode { onFlashProtect = TELCOFLASH; onFlashProtectStart = millis(); // This value not needed digitalWrite(hvTo90, HIGH); // Assure power supply not at high level when intercom temporarily on digitalWrite(kill90V, LOW); delay(2); // Wait for hi V to reduce digitalWrite(intercom, HIGH); // Activate intercom relay so Telco will be guaranteed off hook digitalWrite(hold, LOW); // Begin Telco on-flash delay(telcoOnFlash); // Wait for on-flash to complete if ( digitalRead(intercomSense) != LOW ) { errorCode = 10; } if ( digitalRead(holdSense) != HIGH ) { errorCode = 11; } opMode = NORMAL; onFlashProtect = false; digitalWrite(intercom, LOW); delay(4); if ( digitalRead(intercomSense) != HIGH ) { errorCode = 12; } else if ( telcoJumper == false ) { digitalWrite(hvTo90, LOW); // Minimize power dissipation while intercom is inactive } if ( serialReport ) { Serial.print("Normal mode, Telco on-flash sent. "); if ( digitalRead(hvTo90) ) Serial.println("Power Supply 90V."); else Serial.println("Power Supply High."); } } else if ( onFlashDetected && ( opMode == INTHOLD ) ) // Exit INTHOLD, go to NORMAL mode { opMode = NORMAL; ringingStatus = false; ringPatternPosition = 0; digitalWrite(intercom, LOW); digitalWrite(hold, LOW); if ( telcoJumper ) digitalWrite(hvTo90, HIGH); else digitalWrite(hvTo90, LOW); digitalWrite(kill90V, LOW); if ( serialReport ) Serial.print("Normal <- Intercom/Hold. "); if ( digitalRead(hvTo90) ) Serial.println("Power Supply 90V."); else Serial.println("Power Supply High."); } else if ( onFlashDetected && ( opMode == NORMAL ) && ( onFlashProtect == BLOCK1 ) ) // Remain in NORMAL mode, await 2nd flash { onFlashProtect = AWAIT2; onFlashProtectStart = millis(); digitalWrite(hold, LOW); digitalWrite(intercom, LOW); if ( telcoJumper ) digitalWrite(hvTo90, HIGH); else digitalWrite(hvTo90, LOW); if ( serialReport ) { Serial.print("Still NORMAL mode pendng 2nd flash, onFlashProtect = AWAIT2, supplying talk current. "); if ( digitalRead(hvTo90) ) Serial.println("Power Supply 90V."); else Serial.println("Power Supply High."); } } else if ( ( hookStatus == OFFHOOK ) && ( opMode == INTERCOM || opMode == INTHOLD ) ) { // Intercom to talk mode ringingStatus = false; digitalWrite(hvTo90, HIGH); // Assure boosted high voltage is off digitalWrite(kill90V, LOW); // And 90V not turned off ringPatternPosition = 0; if ( serialReport ) { Serial.print("Intercom supplying talk current. "); if ( digitalRead(hvTo90) ) Serial.println("Power Supply 90V."); else Serial.println("Power Supply High."); } } } if ( ringingStatus && ( ringMaxTime > 0 ) ) { unsigned long now = millis(); if ( now < lastRingStart ) // rollover has occurred { lastRingStart = 0; // fix lastRingStart value as best we can } if ( ( now - lastRingStart ) > ringMaxTime ) // Cease ringing and abandon intercom mode, revert to NORMAL { opMode = NORMAL; digitalWrite(intercom, LOW); ringingStatus = false; digitalWrite(hvTo90, HIGH); // Assure boosted high voltage is off digitalWrite(kill90V, LOW); // And 90V not turned off ringPatternPosition = 0; delay(4); // Assure intercom relay has time to release if ( digitalRead(intercomSense) != HIGH ) { errorCode = 5; } else if ( telcoJumper == false ) { digitalWrite(hvTo90, LOW); // Minimize power dissipation while intercom is inactive } if ( serialReport ) { Serial.print("Normal mode - ringing timeout. "); if ( digitalRead(hvTo90) ) Serial.println("Power Supply 90V."); else Serial.println("Power Supply High."); } } loopReading = analogRead(loopSense); loopCurrent = map( loopReading, 23, 46, 0, 27); //Measures 23 counts with add27mASense applied } // Potential future exercise: implement Hold mode if ( serialReport ) { delay(5); // Missing ringingStatus prints without this if ( ringingStatus != prevRingingStatus ) { if ( ringingStatus == TERMINATED ) { Serial.print("TERMINATED<-"); Serial.println(prevRingingStatus); } else if ( ringingStatus == false ) { Serial.print("NOT RINGING<-"); Serial.println(prevRingingStatus); } else if ( ringingStatus == PULSING ) { Serial.print("PULSING<-"); Serial.println(prevRingingStatus); } else if ( ringingStatus == SILENT ) { Serial.print("SILENT<-"); Serial.println(prevRingingStatus); } else { Serial.print("ringingStatus = "); Serial.print(ringingStatus); Serial.print("<-"); Serial.println(prevRingingStatus); } } } prevHookStatus = hookStatus; prevRingingStatus = ringingStatus; }