Project in progress
Air Compressor Controller

Air Compressor Controller

Replace an unreliable cheap pressure switch with an Arduino-controlled high current relay.

  • 3,487 views
  • 15 comments
  • 15 respects

Components and supplies

Necessary tools and machines

84y7278 40
Drill / Driver, 20V
09507 01
Soldering iron (generic)

About this project

This whole project started when one of my air compressor at work started eating pressure switches and burning out the compressor unit, I replaced the first one with a cheap ($17) off-the-shelf switch and it lasted about a month. The next switch I installed was a higher end model ($215) and it lasted about 2 months. After the third switches, I decided to make my own switch using an Arduino controller and a high current rated relay. The factory switches ranged in price anywhere from $17 to $215, I built this unit for $187 total (not including time). This air compressor has a high cycle rate, that is why it was destroying so many switches. It will cycle anywhere from 30 to 50 times a day.

The Arduino controller had to be fail safe or it would be a hazard to run, so I built into the controller safety interlocks and monitors that if any problem were to present its self the Arduino would shut down. I also reused the original pressure switch as a redundant fail safe that just sits in the background and monitors pressure, if the pressure rises to a set amount it opens the power circuit and shuts down the air compressor.

The Arduino controller also has a 4-line display I use to set the start/stop values and also the high/low analog raw input values I use to monitor the health of the pressure transmitter. I programmed the controller using a menu based program, this way I can use this program for other systems by making a few simple changes to the different menu's.

After building and programming the unit and running it through a series of test, I had a major controller fault after I installed the solenoid, every time the solenoid would de-energize the Arduino would reboot. I fixed that by simply using plastic stand off to mount the Arduino to the aluminium project box, seems that there was some stray voltage or a voltage spike introduced by the solenoid every time it de-energized. Another problem I had during the development phase of this project were my set points. How do I keep my set points if there is a power failure? in comes EEPROM! so after much trial and error using EEPROM for the first time I managed to get the EEPROM to work and work very well. I used the "Get" and "Put" commands so that the EEPROM will last that much longer, see the get and put commands only write to EEPROM if it has changed instead of every loop pass. EEPROM only has so many cycles before they fail, 150000 cycles on average.

I also monitor the raw analog input value from the transmitter, if it drops below .5 vdc that indicates the transmitter or the wires have shorted and that will initiate an alarm on the display "Xmtr Short" and shut down the controller. If the raw analog value goes above 4.5 vdc that indicates the transmitter or the wires have opened and that will initiate and alarm on the display "Xmtr Open" and shut down the controller.

When you go through the 5 menu's you will find that the first 4 menus let you adjust the start/stop/alarm low and alarm high set points, the last menu is a maintenance menu, I put that in their for testing and troubleshooting purposes. You can turn on and off the compressor motor via the +/- buttons. But this menu does not time out and go to menu 6 like the other 4 menus do so heed this warning! "Do not leave the controller in menu 5 with the compressor running because the controller is paused and not watching or monitoring the pressure." Menu 5 is strictly for testing purposes and you must exit to run the compressor with the safeties enabled.

I also added a reset button to the front panel so the unit could be reset without having to open the box. I haven't had to use it but I thought it would be convenient to have. I just paralleled the external reset button over the Arduino's internal reset button.

The compressor unload solenoid is mounted next to the old pressure switch and I used the old switch to hold the solenoid in place with a big tye wrap. then I moved the copper tube from the old switch mechanical unload-er to the new solenoid valve. this was something I completely forgot about when I built the unit and had to rewrite the code to add the second out put for the unload valve, live and learn I guess!

I apologize for the lack of a real print, I just didn't have time to learn to use the Fritzle schematic drawing software, I also apologize for the quality of this work, I'm brand new to Arduino and to this Project Hub, this is my first time publishing anything...lol.

As of this writing the compressor has been working flawlessly for 9 months!

<Please feel free to comment or ask me any questions>

Code

WWTP_Compressor_controller_rev21_finalArduino
This is the air compressor code
/*
  WWTP_Compressor_Controller_rev 21/ Final
   RedTar4
   2/20/19-2/29/19

   Analog Resolution is 0-1023 (204.8 per volt or 0.004883v per resolution)
    Pressure sensor is linear:
     0 psi = 0.5v = 102.4
     150 psi = 4.5v = 921.6
*/

// this is the libraries section
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
  LiquidCrystal_I2C lcd(0x27,20,4); // Display address 0x27, I2C 20 x 4
#include <EEPROM.h>
#include <Bounce2.h>
  Bounce debouncer = Bounce(); // instantiate a bounce object
 
// this is the digital input button variables section
int P1 = 2; // button SET MENU'     // this sets the tag as a discreate input to pin 2
int P2 = 3; // button +             // this sets the tag as a discreate input to pin 3
int P3 = 4; // button -             // this sets the tag as a discreate input to pin 4

// this is the analog input variable section
int CompPSIRaw = A0;                // this sets the tag as an analog input to pin A0

// this is the digital output variables section
int CompAlarm = 9;                  // this sets the tag as a discrete output to pin 12
int CompRun = 10;                   // this sets the tag as a discrete output to pin 10
int CompUnloadValve = 11;           // this sets the tag as a discrete output to pin 11

// this is the variables section
int Start = LOW;                    // this sets the variable tag that stores the start psi set point tolow
int Stop = LOW;                     // this sets the variable tag that stores the stop psi set point to low
int CompRunBit = LOW;               // this sets the variable tag CompRunBit to low
int CompUnloadValveBit = LOW;       // this sets the variable tag CompUnloadValveBit to low
int CompAlarmBitL = LOW;            // this sets the variable tag CompAlarmBitL to low
int CompAlarmBitH = LOW;            // this sets the variable tag CompAlarmBitH to low
int menu = 0;                       // this sets the variable tag menu to 0
int manual = 0;                     // this sets the variable tag manual to 0
int eeAddress1 = 50;                // this sets the eeAddress1 tag and sets the address to 50, eeprom
int eeAddress2 = 60;                // this sets the eeAddress2 tag and sets the address to 60, eeprom
int eeAddress3 = 70;                // this sets the eeAddress2 tag and sets the address to 70, eeprom
int eeAddress4 = 80;                // this sets the eeAddress2 tag and sets the address to 80, eeprom
int CompPSIValue = 0;               // this sets the tag CompPSIAlarmL, that will store the value coming from the sensor
int CompPSIAlarmL;                  // this sets the tag CompPSIAlarmL, that will store the value for the raw alarm low setpoint
int CompPSIAlarmH;                  // this sets the tag CompPSIAlarmH, that will store the value for the raw alarm high setpoint
long previousMillis = 0;            // this sets the tag previousMillis, that will store the last time "delay" that was updated
long interval = 30000;              // this sets the tag interval, that will store the delay interval (milliseconds)
int buttonState;                    // this set the tag buttonState, to the current button state from the input pin
int lastButtonState = LOW;          // this set the tag lastButtonState, to the previous button state from the input pin
unsigned long lastDebounceTime = 0; // the last time the output pin was toggled
unsigned long debounceDelay = 100;  // the debounce time; increase if the output flickers

void setup()
{

//  this section assigns the inputs and outputs
  pinMode(P1,INPUT_PULLUP);         // button "MENU"
    debouncer.attach(PI);           // Attach the debouncer to P1 with INPUT_PULLUP mode
    debouncer.interval(debounceDelay);         // interval in ms
  pinMode(P2,INPUT_PULLUP);         // button "+" advance
    debouncer.attach(P2);           // Attach the debouncer to P2 with INPUT_PULLUP mode
    debouncer.interval(debounceDelay);         // interval in ms
  pinMode(P3,INPUT_PULLUP);         // button "-" decrease
    debouncer.attach(P3);           // Attach the debouncer to P3 with INPUT_PULLUP mode
    debouncer.interval(debounceDelay);         // interval in ms
  pinMode(CompRun, OUTPUT);         // comperssor run relay
  pinMode(CompUnloadValve, OUTPUT); // comperssor unloader valve relay

// this section retrieves the setpoints from the arduino eeprom so the unit can start up after a power fail
  EEPROM.get(eeAddress1, Start);         // retrieves the start psi from eeprom
  EEPROM.get(eeAddress2, Stop);          // retrieves the stop psi from eeprom
  EEPROM.get(eeAddress3, CompPSIAlarmL); // retrieves the lo alarm from eeprom
  EEPROM.get(eeAddress4, CompPSIAlarmH); // retrieves the hi alarm from eeprom

//  this section starts up the different libraries
  Serial.begin(9600); // start the serial monitor at 9600 baud
  lcd.init();         // start the lcd library
  lcd.backlight();    // turn on the lcd backlight
  lcd.clear();        // clear the cld screen
  Wire.begin();       // start the I2C library
}

void loop()
{

// Update the Bounce instance :
  debouncer.update();

// this is the menu selection section
  if(digitalRead(P1)){menu=menu+1;}            // press the MENU(P1)button and advance through the menu index by 1
   debouncer.attach(PI);                       // Attach the debouncer to P1 with INPUT_PULLUP mode
   debouncer.interval(debounceDelay);          // interval in ms
  if(menu==0){DisplayPSI();}                   // go to the DisplayPSI subroutine
  if(menu==1){DisplayStart();}                 // go to the DisplayStart subroutine
  if(menu==2){DisplayStop();}                  // go to the DisplayStop subroutine
  if(menu==3){DisplayAlarmLow();}              // go to the DisplayAlarmLow subroutine
  if(menu==4){DisplayAlarmHigh();}             // go to the DisplayAlarmHigh subroutine
  if(menu==5){DisplayAutoMan();}               // go to the DisplayAlarmHigh subroutine
  if(menu==6){StoreAll(); delay(500); menu=0;} // go to the StoreAll subroutine and set the menu tag to 0
    delay(100);
}

void DisplayPSI () // main display
{

// this section monitors the live psi and turns the compressor run bit on or off based off setpoints
  int psi = analogRead(0); // this reads the analog input(A0) and scales it
  psi = map(psi, 102, 921, 0, 150);             // this maps the raw analog input value to the converted PSI value
  if (psi <= Start){CompRunBit = HIGH;}         // if psi is less than start setpoint turn on run bit
  if (psi >= Stop ){CompRunBit =  LOW;}         // if psi is greater than stop setpoint turn off run bit
  if (psi <= Start){CompUnloadValveBit =  LOW;} // if psi is less than start setpoint turn off unload bit
  if (psi >= Stop ){CompUnloadValveBit = HIGH;} // if psi is greater than stop setpoint turn on unload bit

// this section monitors the raw analog input for errors
  CompPSIValue = analogRead(CompPSIRaw);
  if (CompPSIValue < CompPSIAlarmL){CompAlarmBitL = HIGH;} // if comppsivalue is less than the CompPSIAlarmL bit, set alarm low bit high
  if (CompPSIValue > CompPSIAlarmL){CompAlarmBitL =  LOW;} // if comppsivalue is greater than the CompPSIAlarmL bit, set alarm low bit low
  if (CompPSIValue < CompPSIAlarmH){CompAlarmBitH =  LOW;} // if comppsivalue is less than the CompAlarmBitH bit, set alarm high bit low
  if (CompPSIValue > CompPSIAlarmH){CompAlarmBitH = HIGH;} // if comppsivalue is greater than the CompAlarmBitH bit, set alarm high bit high

// this section displays the alarm status on lcd line 4
  if (CompAlarmBitL == HIGH){lcd.setCursor(0,  3); lcd.print("Alarm "); lcd.setCursor(6,  3); lcd.print("Xmtr Open ");}
  if (CompAlarmBitH == HIGH){lcd.setCursor(0,  3); lcd.print("Alarm "); lcd.setCursor(6,  3); lcd.print("Xmtr Short");}
  if (CompAlarmBitL ==  LOW && CompAlarmBitH ==  LOW)
  {
  lcd.setCursor(0,  3); lcd.print("      ");
  lcd.setCursor(2,  3); lcd.print("Sensor OK   ");
  lcd.setCursor(11, 3); lcd.print(" Raw ");
  }

// this section turns on or off the digital outputs based on the control and alarm bits
  digitalWrite(CompRun, CompRunBit && !CompAlarmBitL && !CompAlarmBitH);               // comp run safety interlock if sensor fails
  digitalWrite(CompUnloadValve, CompUnloadValveBit || CompAlarmBitL || CompAlarmBitH); // comp unload valve safety interlock if sensor fails
  digitalWrite(CompAlarm, CompAlarmBitL || CompAlarmBitH);                             // alarm if sensor fails

// this is the serial monitor setion for testing and debug
  Serial.print(" Sensor raw value ");
  Serial.print(CompPSIValue);
  Serial.print(" Sensor psi value ");
  Serial.print(psi);
  Serial.print(", Start @ ");
  Serial.print(Start);
  Serial.print(", Stop @ ");
  Serial.print(Stop);
  Serial.print(",");
  Serial.print(" Low alarm ");
  Serial.print(CompPSIAlarmL);
  Serial.print(",");
  Serial.print(" High alarm ");
  Serial.print(CompPSIAlarmH);
  Serial.print(", CRB ");
  Serial.print(CompRunBit);
  Serial.print(", CUL ");
  Serial.print(CompUnloadValveBit);
  Serial.print(", CAL ");
  Serial.print(CompAlarmBitL);
  Serial.print(", CAH ");
  Serial.println(CompAlarmBitH);
//  Serial.print(", Current Millis ");
//  Serial.println(currentMillis);
  Serial.print(", Previous Millis ");
  Serial.println(previousMillis);

// this is the lcd section
  // line 1
  lcd.setCursor(0,  0); lcd.print("Currently  @ "); // this prints whats in between the quotes
  lcd.setCursor(13, 0); lcd.print("   ");           // this clears the display field so anything left is deleted
  lcd.setCursor(13, 0); lcd.print(psi);             // this prints the tag value
  lcd.setCursor(17, 0); lcd.print("PSI");           // this prints whats in between the quotes
  // line 2
  lcd.setCursor(0,  1); lcd.print("    Start  @ "); // this prints whats in between the quotes
  lcd.setCursor(13, 1); lcd.print("   ");           // this clears the display field so anything left is deleted
  lcd.setCursor(13, 1); lcd.print(Start);           // this prints the tag value
  lcd.setCursor(17, 1); lcd.print("PSI");           // this prints whats in between the quotes
  // line 3
  lcd.setCursor(0,  2); lcd.print("    Stop   @ "); // this prints whats in between the quotes
  lcd.setCursor(13, 2); lcd.print("   ");           // this clears the display field so anything left is deleted
  lcd.setCursor(13, 2); lcd.print(Stop);            // this prints the tag value
  lcd.setCursor(17, 2); lcd.print("PSI");           // this prints whats in between the quotes
  // line 4
  lcd.setCursor(16, 3); lcd.print("   ");          // this clears the display field so anything left is deleted
  lcd.setCursor(16, 3); lcd.print(CompPSIValue);   // this prints the tag value
}

void DisplayStart() // this menu is for adjusting the start psi setpoint
{

// this section advances to menu 6 after 30 sec's, so the program doesn't stall if left on this menu selection
  unsigned long currentMillis = millis();
  if(currentMillis - previousMillis > interval)
    {previousMillis = currentMillis; menu = 6;}

// this section reads the +/- buttons and adjusts the start value
  if(digitalRead(P2)== HIGH)
    {
    if (Start == 150){Start = 0;}     // 150 is the max, sets back to 0
    else {Start = Start + 1;}         // incremnent by 1 per button press
    }

  if(digitalRead(P3) == HIGH)
    {
    if (Start == 0){Start = 150;}     // 0 is the min, sets back to 150
    else {Start = Start - 1;}         // decrease by 1 per button press
    }

// this is the lcd section
  lcd.clear();
  lcd.setCursor(0, 0); lcd.print("   Start PSI Menu:");
  lcd.setCursor(0, 1); lcd.print("  Set Start PSI to");
  lcd.setCursor(8, 3); lcd.print(Start,DEC);
    delay(100);
}

void DisplayStop() // this menu is for adjusting the stop psi setpoint
{

// this section advances to menu 6 after 30 sec's, so the program doesn't stall if left on this menu selection
unsigned long currentMillis = millis();
  if(currentMillis - previousMillis > interval)
    {previousMillis = currentMillis; menu = 6;}

// this section reads the +/- buttons and adjusts the stop value
  if(digitalRead(P2) == HIGH)
    {
    if (Stop == 150){Stop = 0;}       // 150 is the max, sets back to 0
    else {Stop = Stop + 1;}           // incremnent by 1 per button press
    }

  if(digitalRead(P3) == HIGH)
    {
    if (Stop == 0){Stop = 150;}       // 0 is the min, sets back to 150
    else {Stop = Stop - 1;}           // decrease by 1 per button press
    }

// this is the lcd section
  lcd.clear();
  lcd.setCursor(0, 0); lcd.print("   Stop PSI Menu:");
  lcd.setCursor(0, 1); lcd.print("   Set Stop PSI to");
  lcd.setCursor(8, 3); lcd.print(Stop,DEC);
    delay(100);
}

void DisplayAlarmLow() // this menu is for adjusting the raw alarm low setpoint
{

// this section advances to menu 6 after 30 sec's, so the program doesn't stall if left on this menu selection
unsigned long currentMillis = millis();
  if(currentMillis - previousMillis > interval)
    {previousMillis = currentMillis; menu = 6;}

// this section reads the +/- buttons and adjusts the raw low alarm setpoint
  if(digitalRead(P2) == HIGH)
    {
    if (CompPSIAlarmL == 1023){CompPSIAlarmL = 0;} // 1023 is the max, sets to 0
    else {CompPSIAlarmL = CompPSIAlarmL + 1;}      // increase by 1 per button press
    }

  if(digitalRead(P3) == HIGH)
    {
    if (CompPSIAlarmL == 0){CompPSIAlarmL = 1023;} // 0 is the min, sets to 1023
    else {CompPSIAlarmL = CompPSIAlarmL - 1;}      // decrease by 1 per button press
    }

// this is the lcd section
  lcd.clear();
  lcd.setCursor(0, 0); lcd.print(" Raw Alarm Lo Menu:");
  lcd.setCursor(0, 1); lcd.print("   Set Lo Alarm to");
  lcd.setCursor(8, 3); lcd.print(CompPSIAlarmL,DEC);
    delay(100);
}

void DisplayAlarmHigh() // this menu is for adjusting the raw alarm high setpoint
{

// this section advances to menu 6 after 30 sec's, so the program doesn't stall if left on this menu selection
unsigned long currentMillis = millis();
  if(currentMillis - previousMillis > interval)
    {previousMillis = currentMillis; menu = 6;}

// this section reads the +/- buttons and adjusts the raw high alarm setpoint
  if(digitalRead(P2) == HIGH)
    {
    if (CompPSIAlarmH == 1023){CompPSIAlarmH = 0;} // 1023 is the max, sets to 0
    else {CompPSIAlarmH = CompPSIAlarmH + 1;}      // increase by 1 per button press
    }

  if(digitalRead(P3) == HIGH)
    {
    if (CompPSIAlarmH == 0){CompPSIAlarmH = 1023;} // 0 is the min, sets to 1023
    else {CompPSIAlarmH = CompPSIAlarmH - 1;}      // decrease by 1 per button press
    }

// this is the lcd section
  lcd.clear();
  lcd.setCursor(0, 0); lcd.print(" Raw Alarm Hi Menu:");
  lcd.setCursor(0, 1); lcd.print("   Set Hi Alarm to");
  lcd.setCursor(8, 3); lcd.print(CompPSIAlarmH,DEC);
    delay(100);
}

void DisplayAutoMan() // this menu is for manually turning on and off the compressor relay and unload valve relay
{

// this section reads the +/- buttons and adjusts the raw high alarm setpoint
  if(digitalRead(P2) == HIGH)
    {
    if (manual == 1){manual = 0;}           // 1 is the max, sets to 0
    else {manual = manual + 1;}             // increase by 1 per button press
    }

  if(digitalRead(P3) == HIGH)
    {
    if (manual == 0){manual = 1;}           // 0 is the min, sets to 1
    else {manual = manual - 1;}             // decrease by 1 per button press
    }
    digitalWrite(CompRun, manual);          // turns on comp run relay if manual = 1
    digitalWrite(CompUnloadValve, !manual); // turns off comp unload relay if manual != 1

// this is the lcd section

  lcd.clear();
  lcd.setCursor(0, 0); lcd.print(" Maintenance menu:");
  lcd.setCursor(0, 1); lcd.print(" UP to turn On/Off");
  lcd.setCursor(0, 2); lcd.print("Dont forget to exit!");
  if (manual == HIGH){ lcd.setCursor(0, 3); lcd.print("Comp Run, Unload Off");}
  if (manual ==  LOW){ lcd.setCursor(0, 3); lcd.print("Comp Off, Unload Run");}
    delay(100);
}

// this section stores the setpoints into the arduino eeprom so the unit can start up after a power fail
void StoreAll()
{

  lcd.clear();
  EEPROM.put(eeAddress1, Start);          // saves the start psi setpoint to eeprom if different from before
  EEPROM.put(eeAddress2, Stop);           // saves the stop psi setpoint to eeprom if different from before
  EEPROM.put(eeAddress3, CompPSIAlarmL);  // saves the raw low alarm setpoint to eeprom if different from before
  EEPROM.put(eeAddress4, CompPSIAlarmH);  // saves the raw high alarm setpoint to eeprom if different from before

// this is the lcd section
  lcd.setCursor(0,0); lcd.print("SAVING IN");
  lcd.setCursor(0,1); lcd.print("PROGRESS");
    delay(400);
}

Custom parts and enclosures

The Circuit
This is the low voltage side of the circuit
the_circuit_yLCtPSkYtP.ino

Schematics

The Circuit
Controller inside ekvkktxrix
Schematic
Arduino compressor controller print gccefdg4qn

Comments

Similar projects you might like

IR Controller for Air Conditioner

Project in progress by BuddyC

  • 36,554 views
  • 9 comments
  • 25 respects

RGB LED Strips Controller

Project tutorial by Philippe Libioulle

  • 19,927 views
  • 2 comments
  • 19 respects

Reef Controller

Project showcase by Shawn Leclair

  • 15,634 views
  • 0 comments
  • 24 respects

Arduino GrowBox Controller

Project tutorial by Michele Valentini

  • 11,163 views
  • 5 comments
  • 44 respects

Arduino - Drawing via Web Using Step Motor Controller

Project tutorial by IoT_lover

  • 8,377 views
  • 7 comments
  • 78 respects

IoT@School: An IoT Implementation for Monitoring Air Quality

Project showcase by Koen Kempeneers

  • 6,703 views
  • 1 comment
  • 27 respects
Add projectSign up / Login