/*
Testado no simulador:
Potenciometro ligado em A0 simulando a chave de audio:
Chave tres posições ligando as portas em 8 e 9 ao gnd;
Com as duas portas(8 e 9) destivadas(portas em HIGH pelo comando PULL-UP) o potenciometro atua e desatua a saida P0 ( e as demais conforme o delay);
A porta 8 ativada (arduino recebendo nivel LOW na porta 8) aciona a P0 ( e as demais conforme o delay de ativação) automaticamente e ao ser desligado desativa todas as saídas também conforme o delay de desativação;
A porta 9 ativada (arduino recebendo nivel LOW na porta 9) desativa todas as saídas;
Para desarme por falha (sobre temperatura e/ou DC nos fones) Foi inserida interrupção no
pino D2 para desativar as saidas quando D2 estiver em "LOW" (só reativa apos reset).
Quando houver o desarme por falha o LED "faulLed" começará apiscar rapido (0,5Hz).
É necessario ligar os circuitos detectores exernos ao pino D2 que devem ativar em "LOW"
Ardunio based tube power amplifier controller.
Mark Driedger
v1.0 Jan 19, 2014
(c) Mark Driedger, 2014
Automatically controls power on sequencing and power off for an audio device based on sensing analog audio input signal presence
When audio first appears, P0 goes high and enables power (start filament supply on tube amp).
After 8 seconds (filament warmup on tube amps), P1 goes high and enables the driver B+.
After another 1 second (driver output stabilizes), P2 goes high and enables the output stage B+.
When audio is absent for more than 120 seconds, then the audio device is turned off.
If the manual_off input is low, then the audio device is turned off regardless of the audio input or current power on sequencing state
If the manual_on input is low, then the audio device is turned on regardless of the audio input, going through the same sequencing of P0, P1, P2
*/
//===========[ port mapping ]======================
const int PortExtAudio = 6; // external audio present input (active low) from DAC or USB chip
const int faulLed = 7; // use spare port to measure loop sample rate, high during loop processing
const int PortManualOn = 8; // manual ON override switch input, active low
const int PortManualOff = 9; // manual OFF override switch input, active low
const int relayPhone = 10; // optoisolator output port, P2, active high
const int relayVcc = 11; // optoisolator output port, P1, active high
const int relayPower = 12; // optoisolator output port, P0, active high
const int audioLed = 13; // use on board LED to mirror state of AudioPresent, active high
//===========[ global variables ]===================
int state; // current state
long CounterOn; // counts up from 0 to DelayP0 and then DelayP1
long CounterOff; // counts down from DelayOff to 0
float AudioFiltered; // filtered audio present signal
//===========[ constants ]==========================
// Possible values of variable "state"
const int StateOff = 0; // all outputs off (LOW), waiting for AudioPresent or ManualOn
const int StateP0 = 1; // P0 output on (HIGH), waiting for CounterOn to hit DelayP0
const int StateP1 = 2; // P0 and P1 outputs on (HIGH), waiting for CounterOn to hit DelayP1
const int StateP2 = 3; // P1, P1, and P2 outputs on (HIGH), waiting for AudioPresent to become inactive
const int Tp = 300; // loop() processing time in usec (excluding added delayMicrosecond() call)
const int Ts = 500; // total desired loop() time in usec, must be > Tp
const long loop_Fs = 1000000L/Ts; // sample freqency in samples/sec
const long DelayP0 = 8*loop_Fs; // 8 second delay after P0 is active before P1 is enabled
const long DelayP1 = 15*loop_Fs; // 9 second delay after P0 is active before P2 is enabled (2 seconds between P1 & P2)
const long DelayOff = 12*loop_Fs; // 120 second delay after audio disappears before power is disabled
//===========[ Blink and fault variables ]==========================
// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis = 0; // will store last time LED was updated
// constants won't change:
const long interval = 100; // interval at which to blink (milliseconds)
int ledState = LOW; // ledState used to set the LED
const byte interruptPin = 2;
int normal= LOW;
void setup()
{
state = StateOff;
AudioFiltered = 0.0;
pinMode(PortManualOn, INPUT_PULLUP);
pinMode(PortManualOff, INPUT_PULLUP);
pinMode(PortExtAudio, INPUT_PULLUP);
pinMode(relayPower, OUTPUT);
pinMode(relayVcc, OUTPUT);
pinMode(relayPhone, OUTPUT);
pinMode(audioLed, OUTPUT);
pinMode(faulLed, OUTPUT);
pinMode(interruptPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin), faultn, FALLING);
normal= LOW;
}
void faultn()
{
normal=HIGH;
}
void blink()
{
// here is where you'd put code that needs to be running all the time.
// check to see if it's time to blink the LED; that is, if the difference
// between the current time and last time you blinked the LED is bigger than
// the interval at which you want to blink the LED.
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval)
{
// save the last time you blinked the LED
previousMillis = currentMillis;
// if the LED is off turn it on and vice-versa:
if (ledState == LOW) {
ledState = HIGH;
} else {
ledState = LOW;
}
// set the LED with the ledState of the variable:
digitalWrite(faulLed , ledState);
}
}
void carry()
{
if(digitalRead(interruptPin)==LOW)
{
normal=HIGH;
}
const int ThresholdA = 7; // threshold to declare audio L or R input present in ADC LSB's (relative to 1023)
const float ThresholdB = 0.05; // threshold to declare AudioPresent after filtering (relative to 1.0)
const float alpha = 0.91; // single pole filter constant to filter audio present signal
boolean AudioPresent; // true if audio input is present
boolean ManualOn; // true if manual on switch is active (low)
boolean ManualOff; // true if manual off switch is active (low)
boolean ExtAudioPresent; // true if external audio present pin is active (low)
//read audio inputs, compare to ThresholdA and then filter
AudioFiltered = alpha * AudioFiltered + (1-alpha) * ((analogRead(A0) > ThresholdA));
AudioPresent = AudioFiltered > ThresholdB;
digitalWrite(audioLed, AudioPresent);
// read digital input ports
ManualOn = digitalRead(PortManualOn) == LOW;
ManualOff = digitalRead(PortManualOff) == LOW;
ExtAudioPresent = digitalRead(PortExtAudio) == LOW;
// if manual off switch is active, then change state to StateOff
if (ManualOff)
{
state = StateOff;
CounterOn = 0;
}
// implement state machine
switch (state)
{
case StateOff:
digitalWrite(relayPhone, LOW); digitalWrite(relayVcc, LOW); digitalWrite(relayPower, LOW);
if (ManualOn || AudioPresent || ExtAudioPresent)
{
state = StateP0;
CounterOn = 0;
}
break;
case StateP0:
digitalWrite(relayPhone, LOW); digitalWrite(relayVcc, LOW); digitalWrite(relayPower, HIGH);
if (++CounterOn > DelayP0)
state = StateP1;
break;
case StateP1:
digitalWrite(relayPhone, LOW); digitalWrite(relayVcc, HIGH); digitalWrite(relayPower, HIGH);
if (++CounterOn >= DelayP1)
{
state = StateP2;
CounterOff = DelayOff;
}
break;
case StateP2:
digitalWrite(relayPhone, HIGH); digitalWrite(relayVcc, HIGH); digitalWrite(relayPower, HIGH);
if (AudioPresent)
CounterOff = DelayOff;
else if (--CounterOff <=0)
state = StateOff;
break;
}
delayMicroseconds(Ts-Tp);
}
void loop()
{
if(normal==LOW)
{
carry();
}
else
{
pinMode(relayPhone, LOW);
digitalWrite(audioLed, LOW);
delay(100);
pinMode(relayVcc, LOW);
delay(100);
digitalWrite(relayPower, LOW);
blink();
}
}