ARDUINO control of 1.2 kW Solid State LDMOS PA

Solid State VHF PA's are  currently commercially offered on the market by many manufacturers (BEKO, ...) but are quite expensive (2.500 - 4.000 EUR), so it is certainly worth to consider a 'homebrew' alternative.

This project describes an ARDUINO based control of a Solid State PA ( concept F1JRD ). Like most of recent SSPA's, this  PA is using one LDMOS transistor (Freescale MRFE6VP61K25H  able to produce 1.250W RF  for only 2.5W input. - see excellent application note of Freescale here) which is quite rugged (advertised to withstand a SWR of 1:65)  but nevertheless needs to be carefully monitored as it is running under 50v,  drawing up to 35A and this device costs about 250 EUR !  And yes, I have already sent one to "semiconductor's heaven" so had to design something to protect it in the most efficient manner...

In this project, ARDUINO will take care of following vital functions :

The SSPA has a 'test' mode for playing around with no +50v supplied - so nothing can go wrong ...  To enter this TEST mode,  power up while the RESET button is depressed - it will then run in 'test' mode and ignore any 50v related errors.

ARDUINO will as well take care of controlling the cooling fans. The SSP pallet is mounted on a huge  cooling 'brick',  ventilated by 2 compact & powerful - thus  very noisy - fans.

The RF pallet on the cooling brick, complete with output filter.

Running at full speed, it is like a helicopter taking off  in the shack !  So not very comfortable to implement an 'all or nothing'  control of fans... Instead, ARDUINO will monitor the temperature and progressively (PID - Proportional - Integral - Differential) regulate their RPM by PWM control.  The result is that the pallet temperature will never exceed 50C and fan speed is adapted smoothly as required.. There is as well a detection of 'EME' modes built-in : if the power is constant (like for JT65A) above a certain level, ARDUINO will have the fans running at 100% in a pro-active manner.

I used the 'Ultimate PA control board' of Jim W6PQL ( see  http://www.w6pql.com/amplifier_control_board.htm ) for it's excellent sequencer (and ALC control), it as well monitors the SWR and temperature as redundant protection. In turn, if any fault condition is detected, ARDUINO will signal this to the PA control board (which as a 'force RX' input) which will immediately revert the SSPA into RX mode in a graceful manner.

Compact PA in 19" chassis 3U high - including the low-pass filter & DB6NT HEMT preamp - the power supply is external - Click to zoom.

See the PA sketch in action here (YouTube) :   

https://www.youtube.com/watch?v=0fD5piBHD0o

https://www.youtube.com/watch?v=7sXOMGHFyPU

https://www.youtube.com/watch?v=47pa17hgofY

Stress-testing the new PA in the UBA Spring contest - 2m - March 2015 : 1st place !

Amplifier used  for operating MS from Andorra (C3) - Perseids 2016

Running 700 - 800 W in MS

 

NOTES :

DOWNLOADS:

 It was compiled with IDE version 0022.- IMPORTANT : please use the same or you might get errors when compiling !  You still can download previous versions from ARDUINO website

(see other ARDUINO stuff under 'PROJECTS' page)

Branko S52V realized a nice HF PA 1.5 kW with 2 pallets bridged running each a BFL-188 XR, using my sketch as base - click on pictures to zoom in !

Parts of the sketch are used as well in the ANAN-8000LE 200w SDR HF transceiver - see excerpt of the instruction manual here


/* 

////////////////////////////////////////////////////////////////////////
///                                                                  ///
///     SOLID STATE POWER AMPLIFIER  READOUT & PROTECTION  CIRCUIT   /// 
///                                                                  ///
////////////////////////////////////////////////////////////////////////

By ON7EQ 02/2015

Compiled with IDE 0.22 for ARDUINO NANO board

Connected with W6PQL SSPA controller

ARDUINO NANO CONNECTIONS
________________________

PIN		DESCRIPTION	                         TO PIN
			
ANALOG			
A0		Forward voltage directional coupler	Dir coupler FWD
A1		Reflected voltage directional coupler	Dir coupler REFL
A2		NTC 10k	                                NTC (10k to +5v)
A3		50v supply - voltage	                ATTOPILOT Voltage
A4		50v supply - current	                ATTOPILOT Current
A5		26v supply - Voltage	
A6		13.8v supply - voltage	
A7		PTT - read status	
			
DIGITAL	I/O		
0	I	Serial RX  &   input recover switch  (0 = recover)	
1	I	Serial TX  &   Input PREAMP status (0 = preamp ON)	
2	I	"Operate" status read	Logic HI = operate 
3	O	+50v Supply control	Output, logic HI = ON
4	O	Buzzer	Output, Logic HI = sounding
5	O	FAN Control (PWM port)	Output, Logic HI = 'ON' pulse to Mosfet Gate
6	O	LCD : DB7	LCD 01
7	O	LCD : DB6	LCD 02
8	O	LCD : DB5	LCD 03
9	O	LCD : DB4	LCD 04
10	O	LCD : En2	LCD 15
11	O	LCD : En1	LCD 09
12	O	LCD : RS	LCD 11
13	O	FAULT 	        (PTT BLOCK W6PQL) Logic HI = Kill PTT via open collector output

=================================================================== 
                         NTC Thermistor Schematic
=================================================================== 

 (+5v ) ---- (10k-Resistor) -------|------- (Thermistor) ---- (GND)
                                   |
                                Analog Pin 'IN_NTC'

*/


// include 4x40 LED driver
#include <LiquidCrystal440.h>

// include math functions
#include "math.h"  

uint8_t nRows = 4;      //number of rows on LCD
uint8_t nColumns =40;   //number of columns

// Modify the pin number below to meet your board

//Analog pins
#define IN_FWD    A0  // analog input for left channel
#define IN_REFL   A1  // analog input for right channel
#define IN_NTC    A2  // analog input for right channel
#define IN_50vv   A3  // analog input for 50v voltage
#define IN_50vI   A4  // analog input for 50v current
#define IN_26v    A5  // analog input for 26v voltage
#define IN_12v    A6  // analog input for 12v voltage
#define IN_PTT    A7  // analog input for PTT detect  (0 = keyed)

//  Digital pins
#define IN_reset   0 //  input for recover/SELFTEST switch detect - momentary pulling to GND (0 = recover/SELFTEST)
#define IN_PREAMP  1 //  input for Preamp detect  (1 = ON)
#define IN_OPERATE 2 //  input for operate detect  (1 = OPERATE)

#define supply_50v 3 //  output for 50v supply control  (1 = 50v ON)
#define Buzzer     4 //  output for Buzzer control  (1 = ON)
#define FANcontrol 5 //  output for FAN control  (PWM !)
#define PTT_block  13//  output for PTT block control  (no alarm = 0, FAULT = 1...n)


// Other minor configurable value

#define T_REFRESH1    50          // msec bargraph refresh rate
#define T_REFRESH2    500         // msec refresh rate other variables
#define T_PEAKHOLD   600          // msec peak hold time before return
#define T_pepHOLD    600          // msec pep hold time before return


#define R1               (10)     // from GND to IN_26v, express in 100R  (12 = 1200 Ohm)
#define R2               (82)     // from + power supply to IN_26v, express in 100R  (47 = 4700 Ohm)
#define R3               (12)     // from GND to IN_12v, express in 100R  (12 = 1200 Ohm)
#define R4               (47)     // from + power supply to IN_12v, express in 100R  (47 = 4700 Ohm)

// FAN speeds (0 ... 255) PWM driven

#define fan_vy_slow      (20)     // Very slow, idle speed
#define fan_slow         (30)     // Slow
#define fan_med_lo       (40)     // Medium speeds, low
#define fan_med_hi       (50)     // Medium Hi, start of PID regulation till 255 = max !

// Calibration factors

int calibrP = 540;                // Assume 3.3v = 1.000w in 50R. CalibrP = (3.3 / 5.0 x 1024 + Vdiode)x(3.3 / 5.0 x 1024 + Vdiode) / 1000w
int Vdiode = 60;                  // if we assume FWD voltage diode = 0,3v : Vdiode = 0,3 v / 5.0 v  x 1024

// local variable

byte  fill[6]={ 0x20,0x00,0x01,0x02,0x03,0xFF };      // character used to fill (0=empty  5=full)
byte  peak[7]={ 0x20,0x00,0x04,0x05,0x06,0x07,0x20 }; // character used to peak indicator
int   lmax[5];                                        // level max memory
int   dly[5];                                         // delay & speed for peak return
long  lastT1=0;                                        // update display timer1
long  lastT2=0;                                        // update display timer2
long  lastTpep = 0;                                   // update PEP display
long  lastTempTime = 0;                               // update last temp readout for fan control
long  LastTXtime = 0;                                  // timestamp when last transmission ended (for FAN control)
long  HiPowerTime = 0;                                 // timestamp Hig Power - for EME conditions
long  HiAmp50Time = 0;                                 // timestamp high current value on 50v power supply
long  LoVolt50Time = 0;                                // timestamp low voltage value on 50v power supply



int   anF = 0;                            // analog read forward power
int   anR = 0;                            // analog read reflected power

int  volt_50 = 0;                         // 50v volt supply - voltage (in 0,1 volt)
int  amp_50  = 0;                         // 50v volt supply - current (in 0,1 Amp)
int  amp_50_max = 0;                      // 50v volt supply - max current (in 0,1 Amp)
int  volt_26 = 0;                         // 26v volt supply - voltage (in 0,1 volt)
int  volt_12 = 0;                         // 13,8 v volt supply - voltage (in 0,1 volt)

int  temp = 0;                            // temperature (in 1 °C)
int  lasttemp = 0;                        // last temperature for fan control    

byte  PTT = 0;                             // 0 = RX, 1 = TX
byte  wasPTT = 0;                          // former RX/TX status byte : if 1 = was in TX mode

byte  TestMode = 0;                        // 0 = normal mode, 1 = TestMode (PA will 'operate' with no +50v). Testmode is
                                           // initiated by depressing 'RESET4 button upon startup of SSPA

int  Operate = 0;                         // When operate = 0 , PA in stby mode

int  FAN = (0);                           // Fan control (PWM) - 255 = full speed
byte HiTemp = (0);                       // Temperature passing above PID start temp

byte FAULT = 0;                           // when FAULT = 0 : NO FAULT 
                                          // when FAULT = 1 : Overcurrent
                                          // when FAULT = 2 : voltage error 50v
                                          // when FAULT = 3 : voltage error 26v
                                          // when FAULT = 4 : voltage error 12v
                                          // when FAULT = 5 : power out error 
                                          // when FAULT = 6 : SWR error 
                                          // when FAULT = 7 : NTC sensor read error 
                                          // when FAULT = 8 : Temperature error
                                          // when FAULT = 9 : 50v idle current error 
                                          // when FAULT = 10: W6PQL control PCB SWR exceed fault                                           
                                          
byte FirstLoop = (1);                     // First loop detect 
byte SelfTest  = (1);                     // 1 to force selftest

byte HiAmp50 = (0);                       // Flag to detect long lasting current peaks on 50v supply

byte LoVolt50 = (0);                      // Flag to detect long lasting voltage dip on 50v supply

byte EMEmode = (0);                       // 0 = normal, 1 = EME conditions (no SSB but CW/JT65)

unsigned long   pow_fwd = 0;              // power forward (watts)
unsigned long   pow_ref = 0;              // power reflected (watts)

unsigned int   pow_fwd_max = 0;           // power forward max (for peak hold)
unsigned int   pow_ref_max = 0;           // power reflected max (for peak hold)

float SWR = 0;                            // SWR 
float Pratio = 0;                         // Power ratio P forward / P refl
int SWRDis = 0;                           // power calculation for showing in display


LiquidCrystal lcd(12,255,11,10,9,8,7,6);  // (RS,RW,En1,En2,D4,D5,D6,D7) 255 if RW  is connected to GND and not controlled by the interface.

//=====for a 4x40 LCD with 2 HD44780 type chips and 18 pin interface in 2 rows of 9; 

//   LCD     Nano     Signal
//    18              Gnd        Backlight white on blue 4x LED draws 40 mAmps
//    17              +5V        Backligt + through external resistor 15 Ohm.   
//    16      NC      not used
//    15      10      En2 -- enable the 2nd HD44780 chip which controls the bottom 2 rows of the display
//    14              +5V supply logic
//    13              Gnd logic
//    12              Wiper of contrast resistor (22k between +5v and Gnd)
//    11      12      RS
//    10              Connect to Gnd 
//     9      11      En1 -- enable the 1st HD44780 which controls the top 2 rows
//    5-8             Data 0-3: not used in 4 bit modes
//    1-4    09-06    Data 4-7: LCD DB7 to ARDUINO 06, DB6 to 07, DB5 to 08, DB4 to 09

/////////////////// DRAW BAR //////////////////////////

void  bar  ( int row,int lev )
{
  lcd.setCursor( 3,row );
  lcd.write( row ? ' ' : ' ' );
  for( int i=1 ; i<30 ; i++ )
  {
    int f=constrain( lev      -i*5,0,5 );  // Level instant
    int p=constrain( lmax[row]-i*5,0,6 );  // Level maximum (=peak)
    if( f )
      lcd.write( fill[ f ] );
    else
      lcd.write( peak[ p ] );
  }
  if( lev>lmax[row] )
  {
    lmax[row] = lev;
    dly[row]  = -(T_PEAKHOLD)/T_REFRESH1;                // Starting delay value. Negative=peak don't move
  }
  else
  {
    if( dly[row]>0 )
      lmax[row] -= dly[row]; 

    if( lmax[row]<0 )
      lmax[row]=0;
    else
      dly[row]++;
  }
}

byte block[8][8]=
{
  { 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10 },  // define character for fill the bar
  { 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18 },
  { 0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C },
  { 0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E },

  { 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08 },  // define character for peak level
  { 0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04 },
  { 0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02 },
  { 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01 },
};

///////////////// PRINT TEMPLATE  ///////////////////

void printtemplate () {

  lcd.setCursor( 0,0 );
  lcd.print( "FWD                                   0w");
  lcd.setCursor( 0,1 );
  lcd.print( "REF                                   0w");

  lcd.setCursor( 6,2 );
  lcd.print( "Vd=--.-"); 
  lcd.setCursor( 6,3 );
  lcd.print( "Id=--.-"); 

  lcd.setCursor( 16,2 );
  lcd.print( "Vr=--.-"); 
  lcd.setCursor( 16,3 );
  lcd.print( "Vs=--.-"); 

  lcd.setCursor( 26,2 );
  lcd.print( "t--"); 
  lcd.print((char)223);  //degree symbol
  //lcd.print((char)165); 

  lcd.setCursor( 0,2 );
  lcd.print( "swr"); 
 
  lcd.setCursor( 0,3 );
  lcd.print( "-.-"); 
 
 /* PREAMP 
  lcd.setCursor( 31,2 );
  lcd.print( "PRE"); 
  lcd.setCursor( 31,3 );
  lcd.print( "AMP"); 
 */
 
  lcd.setCursor( 36,2 );
  lcd.print( "stby"); 
  
  lcd.setCursor( 4,2 ); 
  lcd.write( peak[3] );
  lcd.setCursor( 4,3 ); 
  lcd.write( peak[3] );
  
  lcd.setCursor( 14,2 ); 
  lcd.write( peak[3] );
  lcd.setCursor( 14,3 ); 
  lcd.write( peak[3] );
   
  lcd.setCursor( 24,2 ); 
  lcd.write( peak[3] );
  lcd.setCursor( 24,3 ); 
  lcd.write( peak[3] );
   
  lcd.setCursor( 30,2 ); 
  lcd.write( peak[3] );
  lcd.setCursor( 30,3 ); 
  lcd.write( peak[3] );  
  
  lcd.setCursor( 34,2 ); 
  lcd.write( peak[3] );
  lcd.setCursor( 34,3 ); 
  lcd.write( peak[3] );
  
}

/////////// This function will calculate temperature from 10k NTC readout /////////////

double Thermister(int RawADC) {
 double Temp;
 Temp = log(((10240000/RawADC) - 10000));
 Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp ))* Temp );
 Temp = Temp - 273.15;            // Convert Kelvin to Celcius
// Temp = (Temp * 9.0)/ 5.0 + 32.0; // Convert Celcius to Fahrenheit
 return Temp;
}


////////////////////////////////////////////////////////////////
///////////////////////////    SETUP      //////////////////////
////////////////////////////////////////////////////////////////

void setup() {
  
      /// Prepare I/O pins
            
      pinMode(IN_PREAMP, INPUT);
      pinMode(IN_OPERATE, INPUT);
      pinMode(IN_reset, INPUT);
      
      digitalWrite(supply_50v, LOW);     // We do not allow +50v yet
      pinMode(supply_50v, OUTPUT);
      
      digitalWrite(Buzzer,0);
      pinMode(Buzzer, OUTPUT);
      
      analogWrite(FANcontrol,FAN);
      
      digitalWrite(PTT_block,HIGH);      // we force RX mode till all is OK !
      pinMode(PTT_block, OUTPUT);
      
      
      lcd.begin(nColumns,nRows);     
                                   

      for( int i=0 ; i<8 ; i++ )
      lcd.createChar( i,block[i] );
      
      lcd.clear();
      
      lcd.setCursor(5,0 );   
      lcd.print ("1 . 2 5  k W   V H F  S S P A ");   
      lcd.setCursor(5,2 );   
      lcd.print ("       by  O N 7 E Q          ");   
      lcd.setCursor(5,3 );   
      lcd.print ("     05/2015  -  V1.08       ");      
      delay (2000);      
      lcd.clear();      
      delay (500); 
      printtemplate();
      
  
      lastT2 = millis();                // set T2 display refresh

  }

///////////////////////////////////////////////////////////
/////////////////////////// LOOP //////////////////////////
///////////////////////////////////////////////////////////


void loop() {  

 
  // Check if TESTMODE required. Testmode is initiated by depressing the RESET button upon powering up.
  // In testmode, no 50v supply is applied to PA pallet, and no 50v is required to go into 'OPERATE' and 'TX' mode
  // exiting TESTMODE is only possible by powering up again SSPA
  
     if (((digitalRead(IN_reset) == LOW) or (TestMode == 1))and (FirstLoop == 1) ) { 
     TestMode = 1;
     lcd.setCursor(10,0 );   
     lcd.print                    ("T E S T    M O D E  !");
     lcd.setCursor( 7,1 );   
     lcd.print                 ("(N O   + 5 0 v   T O   P A)");
     
     digitalWrite(Buzzer,HIGH);
     delay (200);    
     digitalWrite(Buzzer,LOW);
     
     lcd.setCursor(10,0 );   
     lcd.print                    ("                     ");
     lcd.setCursor( 7,1 );   
     lcd.print                 ("                           ");    
      
     delay (200); 
     digitalWrite(Buzzer,HIGH);
     
     lcd.setCursor(10,0 );   
     lcd.print                    ("T E S T    M O D E  !");
     lcd.setCursor( 7,1 );   
     lcd.print                 ("(N O   + 5 0 v   T O   P A)");    
     
     delay (200);    
     digitalWrite(Buzzer,LOW);
     
     lcd.setCursor(10,0 );   
     lcd.print                    ("                     ");
     lcd.setCursor( 7,1 );   
     lcd.print                 ("                           ");    
          
     delay (200); 
     digitalWrite(Buzzer,HIGH);
     lcd.setCursor(10,0 );   
     lcd.print                    ("T E S T    M O D E  !");
     lcd.setCursor( 7,1 );   
     lcd.print                 ("(N O   + 5 0 v   T O   P A)");    
       
     
     delay (200);    
     digitalWrite(Buzzer,LOW);
     
     delay (500);
     printtemplate();
          
     }
       
  // update OPERATE / STBY mode
 
      if (digitalRead(IN_OPERATE) == HIGH) Operate = 1;
      else Operate = 0;
      
      
 // update PTT status
 
      if (analogRead(IN_PTT) < 512)  PTT = 1;
      else PTT = 0;
  
  
  // Check if no PTT & operate mode at startup of our PA
  
      if ((Operate == 1) and (FirstLoop == 1) and (PTT == 1)) {
       digitalWrite(Buzzer,HIGH);
       lcd.setCursor(5,0 );   
       lcd.print                    ("SET TRANSCEIVER IN RX PLEASE !");
       delay (500);
       digitalWrite(Buzzer,LOW); 
       printtemplate();
       delay (500);
       loop();
       }
 
  
  // perform every loop some measurement we need for critical faults detection
  //         __________

      // Current 50v
      amp_50  = map (analogRead(IN_50vI),0,1023,0,1680);      //90 Amp ATTOPILOT board - convert to 0,1 A
   
           if (amp_50 < 31) amp_50 = amp_50 + (amp_50/4) ; // Arduino not linear at very low voltages ?
           if (amp_50 < 51) amp_50 = amp_50 + (amp_50/7) ;
           if (amp_50 < 101) amp_50 = amp_50 + (amp_50/10) ;
           if (amp_50 < 150) amp_50 = amp_50 + (amp_50/20) ;
           
           if (amp_50 > amp_50_max) amp_50_max = amp_50;             // measure peak current every T1 to display max every T2
  
  
  //Detect current peak (instantaneous, 'never exceed' value) :
  
           if (amp_50 >= 400) {     // Critical overcurrent ! peak with more than 40A !
                  FAULT = 1;
                  fault();           // Jump to FAULT handling
                  }
                  
   //Detect current 'long lasting' peak (overdrive of PA - protect LDMOS) :
                   
           if (amp_50 >= 350) {      // more than 35A
                  if (HiAmp50 == 0) HiAmp50Time =  millis();  // timestamp reading
                  HiAmp50 = 1;
                  }
               else (HiAmp50 = 0);
                   
            if ((HiAmp50 == 1) and ((millis() - HiAmp50Time) > 30)) {  // we have a peak lasting more than 30 ms
                 HiAmp50 = 0;
                 FAULT = 1;
                 fault();           // Jump to FAULT handling
                 }           
      
      
      // Voltage 50v 
      volt_50 = map (analogRead(IN_50vv),0,1023,0,788);       //90 Amp ATTOPILOT board - convert to 0,1 V

           if ((volt_50 >= 550)           ){             // Overvoltage, always alarm !     
                  FAULT = 2;
                  fault();           // Jump to FAULT handling
                  }
                  
                                    
           if ((volt_50 <= 430) and (Operate == 1) and  (TestMode == 0)  and (PTT == 0) ){  // Undervoltage while in operate mode, in RX ! Fault in pallet ?    
                  FAULT = 2;
                  fault();           // Jump to FAULT handling
                  }                 
           
           // detect long lasting voltage dip
           
           if ((volt_50 <= 450) and (Operate == 1) and (TestMode == 0)  and (PTT == 1) ){  // Undervoltage while in TX mode    
                  if (LoVolt50 == 0) LoVolt50Time = millis ();
                  LoVolt50 = (1);          
                    }                 
                    else (LoVolt50 = 0);
                    
           if ((LoVolt50 == 1) and ((millis() - LoVolt50Time) > 100)) {  // we have a dip >100 ms !
                 LoVolt50 = 0;
                 FAULT = 2;
                 fault();           // Jump to FAULT handling
                 }      
                             
                
      // Voltage 26v.  Make sure RF relays can be energized !                
      volt_26 = map(analogRead(IN_26v), 0,1023,0,(50*(R2+R1)/R1)) + 1; 
       
      if(volt_26 == 1) volt_26 = 0;  // 'zero volt' rounding orreection
     
           if ((volt_26 >= 300) or (volt_26 <= 230)          ){     // Under or overvoltage, always alarm !
                  FAULT = 3;
                  fault();           // Jump to FAULT handling                 
                  }

       // Voltage 12v.       
       volt_12 = map(analogRead(IN_12v), 0,1023,0,(50*(R4+R3)/R3));
           
           if ((volt_12 >= 150) or (volt_12 <= 110)          ){     // Under or overvoltage, always alarm !
                  FAULT = 4;
                  fault();           // Jump to FAULT handling                 
                  }
 
   
    // All critical parameters checked, if selftest on startup we can now release PTT block and allow +50v supply PA   
   
     if ((SelfTest == 1) ) {
    
       if (TestMode == 0) digitalWrite(supply_50v,HIGH);   // +50v only when not in testmode
       
       // test current 50v, should be 0 !
       amp_50  = map (analogRead(IN_50vI),0,1023,0,1680);      //90 Amp ATTOPILOT board - convert to 0,1 A
       
       if ((amp_50 > 1) and (TestMode == 0) ) {            // more than 0.1 A idle current !
                  digitalWrite(supply_50v,LOW);  // immediately switch off 50v !
                  FAULT = 9;
                  fault();           // Jump to FAULT handling
                  } 
    
        lcd.setCursor( 13,0 );   
        lcd.print  ("SELFTEST = OK !");
        
        if (FirstLoop == 1) analogWrite (FANcontrol,255); // Fan test
         // Morse code
         // Oscar
        digitalWrite(Buzzer,HIGH);
        delay (240);
        digitalWrite(Buzzer,LOW);
        delay (80);
        digitalWrite(Buzzer,HIGH);
        delay (240);
        digitalWrite(Buzzer,LOW);
        delay (80);        
        digitalWrite(Buzzer,HIGH);
        delay (240);
        digitalWrite(Buzzer,LOW);
        // space 
        analogWrite (FANcontrol,0);
        
        delay (240);        
        // Kilo
        digitalWrite(Buzzer,HIGH);
        delay (240);
        digitalWrite(Buzzer,LOW);
        delay (80);         
        digitalWrite(Buzzer,HIGH);        
        delay (80);            
        digitalWrite(Buzzer,LOW);        
        delay (80); 
        digitalWrite(Buzzer,HIGH);
        delay (240);
        digitalWrite(Buzzer,LOW);
        delay (500);
        // end CW tune
                
 
        
        // NOW PA ready to go !
        digitalWrite(PTT_block,LOW);
        SelfTest = (0);
        
        LastTXtime = millis() - 35000;    // avoid FAN running at startup
        
        
         } // end of selftest        


///// we must now update screen for timer T1 = short timer //////
     
 if (( millis()<lastT1 ) and (FirstLoop == 0))  return;
  
 lastT1 = millis() + T_REFRESH1;
    
 pow_fwd = analogRead(IN_FWD);
 
       if (pow_fwd > 5) {         // only correct for diode voltage when more than zero
         pow_fwd = (pow_fwd + Vdiode)*(pow_fwd + Vdiode) / calibrP; 
         }

        // detect PA overdrive

        if (pow_fwd > 1300) {        // PA overdriven !
                      FAULT = 5;
                      fault();           // Jump to FAULT handling                       
                      }
 
 
 
 // detect if SSB or EME CW/JT65 condition (more than 7 seconds continuous CW/JT65)
 
        if ((pow_fwd < 600)) {        // less than 600w continuous
                      EMEmode = 0;
                      HiPowerTime = millis();                              
                      }
   
    
        if ((pow_fwd > 600) and (millis() - HiPowerTime > 7000)) {        // 
                      EMEmode = (1);
                      }
   
     
  
  pow_ref = analogRead(IN_REFL);

 if (pow_ref > 5) {             // only correct for diode voltage when more than zero
      pow_ref = (pow_ref + Vdiode)*(pow_ref + Vdiode) / calibrP;
       }   

  
   // detect SWR error / load mismatch 
        
    Pratio = pow_fwd / pow_ref;           // calculate ratio with raw data
    SWR = abs ((1+sqrt(Pratio)) / (1-sqrt(Pratio))) ; 
    
    if ((SWR > 2) and (pow_fwd > 100)) {  //  only when forward power > 100w
             FAULT = 6;                
             fault();           // Jump to FAULT handling    
             }
 
   // Detect if W6PQL is in SWR error 
     if ((pow_ref > 5) and (pow_fwd < 2)) {  //  When W6PQL trips on SWR fault, REFL power steady approx 20w
             FAULT = 10;                
             fault();           // Jump to FAULT handling    
             }
 
 
  
   
  // bargraph display
  
    anF = map( pow_fwd,0,1250,0,150 );    // 150 = 30 x 5 colums, full scale 1250w
    anR = map( pow_ref,0,125 ,0,150 );    // 150 = 30 x 5 colums, full scale 125 w
       
    bar( 0,anF );
    bar( 1,anR );
    
  
  // digital readout
 
 
  pow_fwd = ((pow_fwd+5)/10)*10;             // only up to 10w precision

 
  pow_ref =  ((pow_ref+3)/5)*5;              // only up to 5 w precision
  
  // update PEP¨meter ?
  
  if (pow_fwd >= pow_fwd_max) {             // we have a peak !
        lastTpep = millis();
        pow_fwd_max = pow_fwd;
        pow_ref_max = pow_ref;
         }
         
  if (millis() > (lastTpep + T_pepHOLD))  { // clear the peak after hold time
          pow_fwd_max = pow_fwd; 
          pow_ref_max = pow_ref; 
         }
   
    lcd.setCursor( 34,0 );                  // print forward power max
     if (pow_fwd_max > 999) {
       lcd.print( "1.");
       if ((pow_fwd_max - 1000) < 100)  lcd.print( "0");
       if ((pow_fwd_max - 1000) < 10)   lcd.print( "0");
       lcd.print( (pow_fwd_max - 1000),DEC);
         }
     else {
     if (pow_fwd_max < 1000) lcd.print( "  ");
     if (pow_fwd_max < 100)  lcd.print( " ");
     if (pow_fwd_max < 10)   lcd.print( " ");  
     lcd.print(pow_fwd_max,DEC);
     }
     
    lcd.setCursor( 34,1 );   // print reflected power
      if (pow_ref_max > 999) {
       lcd.print( "1.");
       if ((pow_ref_max - 1000) < 100)  lcd.print( "0");
       if ((pow_ref_max - 1000) < 10)   lcd.print( "0");
       lcd.print( (pow_ref_max - 1000),DEC);
    }
     else {
     if (pow_ref_max < 1000) lcd.print( "  ");
     if (pow_ref_max < 100)  lcd.print( " ");
     if (pow_ref_max < 10)   lcd.print( " ");  
     lcd.print(pow_ref_max,DEC);
     }

  // update PREAMP STATUS
  
  if (digitalRead(IN_PREAMP) == LOW){
        lcd.setCursor( 31,2 );
        lcd.print( "PRE"); 
        lcd.setCursor( 31,3 );
        lcd.print( "AMP"); 
        }
  else  {
        lcd.setCursor( 31,2 );
        lcd.print( "   "); 
        lcd.setCursor( 31,3 );
        lcd.print( "   "); 
        }
  
// update OPER / STBY STATUS
   
   if (Operate == HIGH){      // OPERATE
       lcd.setCursor( 36,2 );
       lcd.print( "OPER"); 
       }
  else  {                     // STANDBY
       lcd.setCursor( 36,2 );
       lcd.print( "stby"); 
       lcd.setCursor( 36,3 );
       lcd.print( "    ");    // Clear TX / RX indicator
       LastTXtime = millis() - 31000;    // reset FAN timer
        }
       
// update TX / RX STATUS
   
   if (Operate == 1){
       lcd.setCursor( 36,3 );
        if (PTT == 1){
          lcd.print( "-TX-");
          wasPTT = 1;
          }
        if (PTT == 0){
          lcd.print( "-RX-");
            if (wasPTT == 1) {  // previous state was TX !
            wasPTT = 0;
            LastTXtime = millis();
            }
          }
       }
      


//////////// we must update screen for timer T2 = long timer //////////////

  if ((millis()-lastT2)>T_REFRESH2) {   
      lastT2 = millis();
   

// SWR calculation & display

// SWR = abs ((1+sqrt(Pratio)) / (1-sqrt(Pratio))) ; // already calculated !

 SWRDis = (SWR * 10) + 0.5;   // display SWR one figure after DP
 
  if (SWRDis < 10){    // SWR cannot be lower than 1.0
    SWRDis = 10 ;
    }
  
  lcd.setCursor( 0,3 );

  if (SWRDis >= 50) {
      lcd.print(">5!");
      }
    
  if (pow_fwd < 5) {
      lcd.print("-.-");
      }
 
  else if (SWRDis < 50){
         lcd.print((SWRDis/10), DEC);lcd.print(".");
         lcd.print((SWRDis)%10, DEC); 
      }   
  
    
 // Temperature 
  
        temp = ((Thermister(1023 - analogRead(IN_NTC))) + 0.5);
      
 
          
        // if (temp >= 0) lcd.print(" ");       // If temp positive, print space, else a '-' will show up
        
        lcd.setCursor( 27,2 );
        
        if ((temp < 0) or (temp > 99)){
              lcd.print("??");
              FAULT = 7;      // NTC error !
              fault();
              } 
        else {
              constrain (temp,0,99);
              if ((temp)<10) lcd.print("0"); 
              lcd.print(temp, DEC); 
              }
         if (((temp >= 0) and (temp <=  5)) or (temp >= 65))   {  // FAULT check     
              FAULT = 8;      // temperature error, too cold or hot  !
              fault();        
              }            
              
              
 // + 50v SUPPLY  Volt & Amp measurements display
  
       lcd.setCursor( 9,2 );                    // Volts
       if (volt_50 > 999) volt_50 = 999;        // smoke in the shack !
       
       if (volt_50<100) lcd.print(" ");
       lcd.print((volt_50/10), DEC);lcd.print(".");
       lcd.print((volt_50)%10, DEC); 
     
       lcd.setCursor( 9,3 );                    // Amps
       if (amp_50_max > 999) amp_50_max = 999;
       
       if (amp_50_max<100) lcd.print(" ");
       
       lcd.print((amp_50_max/10), DEC);
       lcd.print(".");
       lcd.print((amp_50_max)%10, DEC); 
       amp_50_max = 0;        // reset after display
       
 // + 28v Relay SUPPLY measurements print      
       
    //volt_26 = map(analogRead(IN_26v), 0,1023,0,(50*(R2+R1)/R1));

    lcd.setCursor( 19,2 );   
    if (volt_26 < 100) lcd.print(" ");  // less than 10v
    lcd.print((volt_26/10), DEC);
    lcd.print(".");
    lcd.print((volt_26)%10, DEC); 

  // + 12v  SUPPLY  measurements  print    
       
    //volt_12 = map(analogRead(IN_12v), 0,1023,0,(50*(R4+R3)/R3));

    lcd.setCursor( 19,3 );   
    if (volt_12 < 100) lcd.print(" ");  // less than 10v
    lcd.print((volt_12/10), DEC);
    lcd.print(".");
    lcd.print((volt_12)%10, DEC); 

 ////// Perform some less critical controls
 
  // Temp check & FAN CONTROL
 
          
         if (temp < 35) FAN = 0;      // start condition
     
         // If SSPA in OPERATE mode, FAN to run very slow if temp between 35 - 40°and in RX mode
        
         if ((Operate == 1) and (temp>=35) and (temp < 40) and (PTT == 0)) FAN = fan_vy_slow;
                         
         // If SSPA in OPERATE mode, FAN to run slow if temp < 40°and in TX mode 
         
         if ((temp < 40) and (Operate == 1) and (PTT == 1)) FAN = fan_slow;
         
         // keep fan running for 30 sec after last TX and operate mode
         
         if ((temp < 40) and (Operate == 1) and (abs (millis()- LastTXtime) < 31000)) FAN = fan_slow; 
         
         if ((temp >= 40) and (temp < 45)) FAN = fan_med_lo;
        
         if ((temp >= 45) and (FirstLoop == 1)) FAN = 255;   //Temp high at power up, immediately cool down !       
         
         if (temp < 45)  HiTemp = 0;
         
         // Temp at least 45°, now activate PID control !
         
          if (temp >= 45) {
  
                if (HiTemp == 0) {        // detect temp passing PID temp level to start PID correction with no delay...
                  HiTemp = 1;
                  FAN = fan_med_hi;
                  lastTempTime = millis() - 5100;
                  }
            
                if (((millis() - lastTempTime) > 5000)) {                       // every 5 seconds, update PID
                      lastTempTime = millis();                                  // recover timer for fan control
                      
                            if (temp > lasttemp + 1) {                          // rising temp, at least 1° !
                                           
                                FAN = FAN + ((temp - 45)*(temp-45)*3);
                                if (FAN > 255) FAN = 255;                       // constrain FAN value
                                lasttemp = temp;  
                              }
                                
                             if (temp < lasttemp) {                             // temp going down !
                                                      
                                FAN = FAN - ((temp - 45)*10);
                                if (FAN < fan_med_hi) FAN = fan_med_hi;         // constrain FAN value
                                lasttemp = temp;                               
                              }
                                
                      } // End PID update
                } // end temp > 45°
         
      
         if (FAULT == 7)  FAN = 0;         // we have a NTC error, stop FAN !
         
          
         lcd.setCursor (26,3);
         
         /*    // 000 - 255 indication
         if ((FAN) < 100) lcd.print("0");
         if ((FAN) < 10)  lcd.print("0");
         lcd.print(FAN, DEC); 
         // end debug
         */
         
                if (EMEmode ==1) FAN = 255;    

                if (FAN == 0) lcd.print("fan");  
                if (FAN == fan_vy_slow) lcd.print("20%"); 
                if (FAN == fan_slow) lcd.print("30%"); 
                if (FAN == fan_med_lo) lcd.print("40%");  
                if (FAN >= fan_med_hi) {
                 lcd.print (map(FAN,fan_med_hi,255,50,99),DEC);
                 lcd.print("%");      
                  }
 
                analogWrite (FANcontrol,FAN);   // PWM output for FAN control by HEXFET
                 
  // end FAN control routine
   
   FirstLoop = 0;      // first loop completely run
           
   } 
      
/////////////////// END T2 Refresh ///////////////////////// 
  
 
      
}  ////// this is end of loop 

/////////////////////   FAULT CONDITIONS /////////////////////

// when FAULT = 0 : NO FAULT 
// when FAULT = 1 : Overcurrent
// when FAULT = 2 : voltage error 50v
// when FAULT = 3 : voltage error 26v
// when FAULT = 4 : voltage error 12v
// when FAULT = 5 : power out error 
// when FAULT = 6 : SWR error 
// when FAULT = 7 : NTC read error 
// when FAULT = 8 : Temperature error
// when FAULT = 9 : 50v idle current error
// when FAULT = 10: W6PQL control PCB SWR exceed fault  

void fault() {

  if (FAULT == 0) return;          // No fault !
  
  // take vital actions !
 
  digitalWrite(PTT_block,HIGH);     // signal to sequencer that we force RX mode !
  digitalWrite(supply_50v, LOW);    // remove + 50v supply from pallet
  
  // update TX / RX STATUS while in FAULT void
   
   if (Operate == 1){
       lcd.setCursor( 36,3 );
        lcd.print( "-RX-");
        }
  else  {
       lcd.setCursor( 36,3 );
       lcd.print( "    "); 
        }      
 

  digitalWrite(Buzzer,HIGH);        // Sound buzzer

  // Signal on display
  
  lcd.setCursor( 0,0 );   
  lcd.print                    ("         F   A   U   L   T    !         ");
  lcd.setCursor( 0,1 ); 
  if (FAULT == 1){
       lcd.print("         OVERCURRENT 50v SUPPLY         "); 
  // Show Error value
       lcd.setCursor( 9,3 );                    // Amps
       if (amp_50 > 999) amp_50 = 999;
       
       if (amp_50<100) lcd.print(" ");
       
       lcd.print((amp_50/10), DEC);
       lcd.print(".");
       lcd.print((amp_50)%10, DEC); 
   }
   
  if (FAULT == 2)   {
   lcd.print("      UNDER/OVERVOLTAGE 50v SUPPLY      ");    
    // Show Volt_50 error value
   lcd.setCursor( 9,2 );
   if (volt_50 > 999) volt_50 = 999;
   if (volt_50<100) lcd.print(" ");
   lcd.print((volt_50/10), DEC);lcd.print(".");
   lcd.print((volt_50)%10, DEC); 
   }; 
  
  if (FAULT == 3)   {
    lcd.print("      UNDER/OVERVOLTAGE 26v SUPPLY      "); 
    // Show Volt_26 error value
    lcd.setCursor( 19,2 );   
    if (volt_26 < 100) lcd.print(" ");  // less than 10v
    lcd.print((volt_26/10), DEC);
    lcd.print(".");
    lcd.print((volt_26)%10, DEC); 
    };    
    
  if (FAULT == 4)   {
    lcd.print("     UNDER/OVERVOLTAGE 13.8v SUPPLY     ");    
    // Show Volt_12 error value 
    lcd.setCursor( 19,3 );   
    if (volt_12 < 100) lcd.print(" ");  // less than 10v
    lcd.print((volt_12/10), DEC);
    lcd.print(".");
    lcd.print((volt_12)%10, DEC); 
    };    
    
  if (FAULT == 5)     lcd.print("       POWER AMPLIFIER OVERDRIVE        ");  
  if (FAULT == 6)     lcd.print("        LOAD MISMATCH (SWR EXCEED)      ");    
  if (FAULT == 7)     lcd.print("     NTC  TEMP. PROBE CIRCUIT ERROR     ");   
  if (FAULT == 8)     lcd.print("         UNDER/OVER TEMPERATURE         ");
  if((FAULT == 8) and (temp > 40 )) analogWrite (FANcontrol,255);      // cooling down ...                    
  if (FAULT == 9)     lcd.print("     50v SUPPLY IDLE CURRENT EXCEED     ");
  if (FAULT ==10)     lcd.print("    SWR ERROR - POWER OFF & ON SSPA     ");
  
  delay (500);
 
  digitalWrite(Buzzer,LOW);        
  
  lcd.setCursor( 0,0 );
  lcd.print  ("                                        ");
  delay (500);
  
  if (digitalRead(IN_reset) == LOW) ResetFault(); 
  
  fault();
  }
/////////////////// END FAULT CONDITION ////////////////////


//////////////////   RECOVER PROCEDURE   /////////////////////

void ResetFault() {
  
  digitalWrite(Buzzer,LOW);
  lcd.setCursor( 0,1 );   
  lcd.print                    ("                                        ");  
  lcd.setCursor( 0,0 );   
  if (PTT ==0) lcd.print                    ("         R E S E T T I N G              ");
  if (PTT ==1) lcd.print                    ("       R E S E T T I N G    B U T       ");
 
  
  
  if (PTT ==0)delay (800);
  lcd.setCursor( 0,0 );   
  if (PTT ==0)lcd.print                    ("         R E S E T T I N G .            ");
  delay (800);  
  lcd.setCursor( 0,0 );   
  if (PTT ==0)lcd.print                    ("         R E S E T T I N G . .          ");
  if (PTT ==0)delay (800);  
  lcd.setCursor( 0,0 );   
  if (PTT ==0)lcd.print                    ("         R E S E T T I N G . . .        ");
  if (PTT ==0)delay (1500);      
  
 // update PTT status : restart / recover only in RX mode
 
 if (analogRead(IN_PTT) < 512) {      // We are still in TX mode !
   PTT = 1;
   digitalWrite(Buzzer,HIGH);
   lcd.setCursor( 0,0 );   
   lcd.print                   ("      SET TRANSCEIVER IN RX PLEASE !    ");
   delay (800);
   ResetFault();                            // stay in loop till RX mode
   }
  
  
 // Recovery
     digitalWrite(Buzzer,LOW);
     digitalWrite(PTT_block,LOW);         // signal to sequencer that we allow again PTT mode !
   
     FAULT = 0;                   //recover fault condition
     printtemplate ();
     SelfTest = 1;                // Force Selftest
    }