// https://forum.arduino.cc/t/analog-sensor-setpoint/314885?page=7
/*
Analog Air Pressue Device:
Hi all, so I have an analog air pressure sensor hooked up to my Arduino converting the read voltage to PSI.
Basically I want it to read the PSI then when I press a button I want it to use that PSI as a set point then have actions triggering two output dependant if the read value drops below
or above the set PSI.
Is this possible and if so can anyone point me in the right direction?
*/
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
//#include <LiquidCrystal.h> // default Arduino LCD library
//LiquidCrystal lcd(12, 11, 5, 4, 3, 2); // pins for RS,E,D4,D5,D6,D7
byte mattressEmpty[8] = {
B00000,
B00000,
B00000,
B00000,
B00000,
B11111,
B10001,
B00000,
};
byte mattressPerson[8] = {
B00000,
B01110,
B11111,
B11111,
B00000,
B11111,
B10001,
B00000,
};
byte iconPump[8] = {
B00000,
B10011,
B10100,
B01110,
B00101,
B11001,
B00000,
B00000,
};
// Running average.
// -------------------
// http://www.arduino.cc/en/Tutorial/Smoothing
// The "s_" is added to show which variables are used for smoothing.
//
// Define the number of samples to keep track of. The higher the number,
// the more the readings will be smoothed, but the slower the output will
// respond to the input. Using a constant rather than a normal variable lets
// use this value to determine the size of the readings array.
//
// WARNING : the s_total will overflow with 100 samples and psi is 3 or higher.
//
const int s_numReadings = 40;
int s_readings[s_numReadings]; // the readings from the analog input
int s_index = 0; // the index of the current reading
int s_total = 0; // the running total
int s_average = 0; // the average
float smoothPSI = 0.2; // running average
float pressureDelta = 0.2; // an average delta (absolute, positive) of four smoothPSI readings
boolean flagPerson = false; // true if someone is led on the mattress *or sat* false if otherwise
float psi = 0.0; // The current air pressure in psi
float setpoint; // The setpoint is the pressure in psi when someone is on the mattress
float targetPSI; // target psi that is used to control the pump, either '0.2' or 'setpoint'.
boolean pumpOn = false; // A flag to indicate that the pump is on.
boolean prvOpen = false;
unsigned long previousMillis; // previous millis for the main 100ms software timer
const int transistorPin = 9; // PWM Pin 9~
const int pushButton = 8; // Pin 8
//*******************************************************************
const int pinSensor = A0; // Analog pressure sensor
//*******************************************************************
//========================================================================
const int pinPRV = 10; // Release valve. Which pin is it ?
//========================================================================
void setup() {
// ---------------------------------
// Initialize the pins and variables
// ---------------------------------
pinMode(13, OUTPUT); // for led
pinMode(transistorPin, OUTPUT);
pinMode(pinPRV, OUTPUT);
pinMode(pushButton, INPUT_PULLUP); // or INPUT_PULLUP ? !!!!!!!! changed !!!!!!!!!!
pumpOn = false; // set initial state
prvOpen = false;
setpoint = 0.2; // initial setpoint for the pressure.
targetPSI = 0.2; // assume no one is on the mattress
// ---------------------------------
// initialize serial communication at 9600 bits per second:
// ---------------------------------
Serial.begin(9600);
Serial.println(F("\nInflator"));
Serial.print(F("Compilation date="));
Serial.println(__DATE__);
// ---------------------------------
// Initialize the lcd
// ---------------------------------
// Display 16x2:
// +----------------+
// 0 |XXXXXXXXXXXXXXXX| row = 0
// 1 |XXXXXXXXXXXXXXXX| row = 1
// +----------------+
// 0123456789012345 column
//
// Display during startup (during one second):
// +----------------+
// 0 |Inflator | Name.
// 1 |Aug 06 2015 | Date when the sketch was compiled
// +----------------+
// 0123456789012345
//
// Display during runtime:
// +----------------+
// 0 |PSI=P.PP MB| P.PP is smooth pressure. M is motor on. B is icon for person on bed.
// 1 |T=Q.QQ S=R.RR | Q.QQ is target pressure. R.RR is setpoint
// +----------------+
// 0123456789012345
//
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0); // top row
lcd.print(F("Inflator")); // print special characters for test
lcd.setCursor(0, 1); // lower row, column=0, row=1
lcd.print(F(__DATE__)); // The date the sketch was compiled
// Show the compilation date for 1000ms
delay(1000);
lcd.setCursor(0, 1);
lcd.print(F(" ")); // clear the lower line
// Create the special custom characters
lcd.createChar( 1, mattressEmpty);
lcd.createChar( 2, mattressPerson);
lcd.createChar( 3, iconPump);
// Clear display
lcd.clear();
// Write static text
lcd.setCursor(0, 0);
lcd.print(F("PSI="));
lcd.setCursor(0, 1);
lcd.print(F("T="));
lcd.setCursor(8, 1);
lcd.print(F("S="));
// ---------------------------------
// Initialize the smooth array
// ---------------------------------
// This is dangerous, one small mistake and the result will never be okay.
int s_init_pressure = (int) (getPSI() * 100.0); // initial pressure in 1/100 psi
for ( int i = 0; i < s_numReadings; i++)
s_readings[i] = s_init_pressure;
s_total = s_numReadings * s_init_pressure;
smoothPSI = (float) s_init_pressure / 100.0;
// At the end of setup(), set the previous millis value
previousMillis = millis();
}
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= 100) {
previousMillis = currentMillis; // a delay in code will delay the timing
// This part of the sketch is run every 100 ms (10Hz)
// ---------------------------------
// Blink the led
// ---------------------------------
// Count 0...9. Turn the led on at 0, and off at 2.
static int countLed = 0;
if (countLed == 0)
digitalWrite(13, HIGH);
else if ( countLed == 2)
digitalWrite(13, LOW);
countLed++;
if (countLed == 10)
countLed = 0;
// ---------------------------------
// Get the pressure
// ---------------------------------
// Update the pressure variable every 100ms.
// The getPSI uses the average of a number of samples,
// but the pressure still changes a lot when the pump is on.
psi = getPSI();
// ---------------------------------
// Smooth psi with running average
// ---------------------------------
s_total = s_total - s_readings[s_index]; // substract the oldest one
// read from the sensor:
s_readings[s_index] = (int) (psi * 100.0); // new data (in 1/100 psi units)
// add the reading to the total:
s_total = s_total + s_readings[s_index]; // add the newest one
// advance to the next position in the array:
s_index = s_index + 1;
// if we're at the end of the array...
if (s_index >= s_numReadings)
// ...wrap around to the beginning:
s_index = 0;
// calculate the average:
s_average = s_total / s_numReadings;
smoothPSI = ((float) s_average) / 100.0; // it was in 1/100 units.
// ---------------------------------
// Delta pressure, check how stable the pressure is.
// ---------------------------------
// The smoothPSI is checked if it is a stable value.
// The change was too small when tested 10 times a second.
// A library could be used for mathematical calculations,
// but adding a few delta variables was easier.
static float previousSmoothPSI = 0.0; // initialize to the init value of smoothPSI.
static int countDelta = 0;
static float delta1 = 0.0; // absolute (positive) difference between current and previous smooth psi
static float delta2 = 0.0; // absolute (positive) difference between previous en psi before that
static float delta3 = 0.0;
countDelta++;
if (countDelta >= 5) // running at 10Hz, so '5' is two times per second
{
countDelta = 0;
delta3 = delta2;
delta2 = delta1;
delta1 = fabs( previousSmoothPSI - smoothPSI);
previousSmoothPSI = smoothPSI;
pressureDelta = ( delta1 + delta2 + delta3 ) / 3.0; // average delta.
}
// ---------------------------------
// Update the display
// ---------------------------------
// Update the display every 300ms.
// The display is not updated when a variable did change, but
// the whole status is updated to the display every 300ms.
// That might not seem very efficient, but it is no problem,
// and it makes the sketch easier.
//
// Display during runtime:
// +----------------+
// 0 |PSI=P.PP MB| P.PP is smooth pressure. M is motor on. B is icon for person on bed.
// 1 |T=Q.QQ S=R.RR | Q.QQ is target pressure. R.RR is setpoint
// +----------------+
// 0123456789012345
static int countLCD = 0;
countLCD++;
if (countLCD >= 3)
{
countLCD = 0;
// This part inside the countLCD counter runs every 300ms.
lcd.setCursor(4, 0);
lcd.print(smoothPSI); // Show smooth psi as P.PP. When it is negative, the last digit will stay at (8,0)
lcd.setCursor(2, 1);
lcd.print(targetPSI); // Show target as Q.QQ
lcd.setCursor(10, 1);
lcd.print(setpoint); // Show setpoint as R.RR
// Show icon for person on bed
// ascii 1 is no person, ascii 2 is person
lcd.setCursor(15, 0);
if ( flagPerson)
lcd.print(F("\2"));
else
lcd.print(F("\1"));
// Show icon for pump on or off
// ascii 3 is pump on, a space is pump off
lcd.setCursor(14, 0);
if ( pumpOn)
lcd.print(F("\3"));
else
lcd.print(F(" "));
}
// ---------------------------------
// Show data to serial monitor
// ---------------------------------
// WARNING:
// Because the printed line is so very long, it will not fit in the buffer of the Serial library.
// Example:
// psi=0.07, smoothPSI=0.06, setpoint=0.20, pressureDelta=0.00, flagPerson=false, pumpOn=false
// The Serial output will be slowed down by the baudrate.
// Printing this line takes about 30ms ((92-64 bytes) / 9600 baud * 10 bits per byte).
// It is better to use shorter text (abreviate the variable names) or print less data or increase the baudrate.
// For example:
// psi=0.07, s_=0.06, set=0.20, Delta=0.00, Person=0, pump=0
// For now this shorter line (takes about 13ms):
// psi=0.07, smooth=0.06, setpoint=0.20, Delta=0.00, Person=false, pump=false
//
// Update the serial monitor every 800ms.
static int countSerialMonitor = 0;
countSerialMonitor++;
if (countSerialMonitor >= 8) // count 8 'ticks' of 100ms.
{
countSerialMonitor = 0;
// This part inside the countSerialMonitor counter runs every 800ms.
// Print about everything in a single line.
Serial.print(F("psi="));
Serial.print(psi);
Serial.print(F(", smooth="));
Serial.print(smoothPSI);
Serial.print(F(", setpoint="));
Serial.print(setpoint);
Serial.print(F(", Delta="));
Serial.print(pressureDelta);
Serial.print(F(", Person="));
Serial.print( flagPerson ? F("true") : F("false"));
Serial.print(F(", pump="));
Serial.print( pumpOn ? F("true") : F("false"));
Serial.println(); // at last, a new line
}
// ---------------------------------
// Read the button/switch
// ---------------------------------
// This part runs every 100ms.
// get the button/switch
boolean switchActive;
int buttonState = digitalRead(pushButton);
if (buttonState == LOW)
switchActive = true;
else
switchActive = false;
if (switchActive)
{
setpoint = smoothPSI; // remember this setpoint. Use smoothPSI or normal psi ?
targetPSI = setpoint; // new target.
flagPerson = true; // set true for extra safety, it should already be set when the pressure changes.
}
// ---------------------------------
// Detect person on the mattress
// ---------------------------------
// Someone could sit on the mattress with feet on the floor,
// then only a part of the weight is on the mattress.
// At this moment the button must be pressed for the setpoint.
// No automatic setpoint yet.
//
// The detection for a person is not at the same trigger pressure,
// to make it more stable. The trigger pressures are now
// below 0.5 or above 0.6 (a hysteresis of 0.1 psi).
//
static int countLowPressure = 0; // count if smooth pressure is lower than target
static int countHighPressure = 0; // count if smooth pressure is higher than target
if (flagPerson)
{
if ((smoothPSI < 0.5) && (pressureDelta < 0.18)) // use psi or smoothPSI ?
{
// Someone was on the mattress, but the pressure is now low and stable.
flagPerson = false;
targetPSI = 0.2; // new target
// reset the variables that count if pressure is too high or too low
countLowPressure = 0;
countHighPressure = 0;
}
}
else
{
// flagPerson is now false, detect if pressure is high.
if (smoothPSI > 0.6)
{
flagPerson = true;
targetPSI = setpoint; // set to the stored setpoint
// reset the variables that count if pressure is too high or too low
countLowPressure = 0;
countHighPressure = 0;
}
}
// ---------------------------------
// Control the pump
// ---------------------------------
// This part runs every 100ms.
// If that is too slow, perhaps this should be run outside the millis() software counter,
// or another millis() software counter could be used.
//
// The motor does not need to turn on very quick. I think it can wait for 5 seconds.
// Turning off should be quick.
//
// To make it more stable, add an extra offset to be sure that the pressure is really lower.
if (smoothPSI < (targetPSI - 0.02)) // determine when to start counting. Use offset (now 0.02)
{
countLowPressure++;
if (countLowPressure > 1000) // safety check, don't count foever
countLowPressure = 1000;
}
else
{
countLowPressure = 0; // reset counter
}
// Turning on the pump
// -------------------
// If the pressure is really too low for a while, turn on the pump
// The counter counts at 10Hz, so '50' is 5 seconds.
if (countLowPressure > 50 && !pumpOn)
{
digitalWrite(transistorPin, HIGH);
pumpOn = true; // remember that the pump is on
}
// Turning off the pump
// -------------------
// Turning off the pump is the most dificult part, since the pressure is not stable
// when the pump is on.
// To test if the pump should be turned off, pause the pump to read the pressure.
if ( pumpOn && smoothPSI > targetPSI) // use smoothPSI or psi ?
{
digitalWrite(transistorPin, LOW);
pumpOn = false;
countLowPressure = 0; // reset counter
}
// Maybe add this section later.
// This is to pause the pump for half a second to read the current pressure
/*
static int pumpCountOn = 0;
// The pressure was too low, but the pump is already on.
// Use a counter for 2 seconds on, and 500ms off.
// Count the time that the pump is on.
// The counter is used like this:
// start with 0
// stop pump at 20 (2 seconds)
// read pressure at 25 (200ms later)
// start all over.
if (pumpCountOn == 20)
{
digitalWrite(transistorPin, LOW);
}
else if (pumpCountOn == 25 )
{
// The pump is off for 500ms, read the pressure.
psi = getPSI();
digitalWrite(transistorPin, HIGH);
pumpCountOn = 0; // start all over again.
}
pumpCountOn++;
*/
// ---------------------------------
// Control the release valve
// ---------------------------------
// This is almost the same as the pump.
// Be slow to turn the release valve on, and turn it off quick.
if (smoothPSI > (targetPSI + 0.02)) // determine when to start counting. Use offset (now 0.02)
{
countHighPressure++;
if (countHighPressure > 1000) // safety check, don't count foever
countHighPressure = 1000;
}
else
{
countHighPressure = 0; // reset counter
}
// Turning on the release valve
// ----------------------------
// If the pressure is really too high for a while, then release some air
// The counter counts at 10Hz, so '50' is 5 seconds.
if (countHighPressure > 50 && !prvOpen) // too much pressure and prvOpen is false ?
{
// turn on the release valve
digitalWrite(pinPRV, HIGH);
prvOpen = true; // remember that the release valve is open
}
// Turning off the release valve
// -----------------------------
// Use the actual psi, to turn it off quickly.
// The pump is not active anyway, so the actual psi is valid.
if ( psi < targetPSI && prvOpen)
{
// turn off the release valve
digitalWrite(pinPRV, LOW);
prvOpen = false; // remember that the release valve is now closed
countHighPressure = 0; // reset counter
}
}
}
float getPSI() {
// Calculate the average of a number of samples.
// The analogRead() is 0...1023.
// When they are all added into a total value, the total should be able to fit in the choosen variable.
// The sketch has a software timing with millis() of 100ms.
// That means a delay below 100ms will have only little influence on the timing of the sketch.
// Perhaps 100 samples during 50ms will improve the result.
// That is 500us per sample. The analogRead() takes 120us on a Arduino Uno.
// So an extra delay of 500-120 = 380us is needed.
// The 100 samples will fit in a unsigned long.
const int NumberOfSamples = 100;
unsigned long total = 0UL; // start with zero. The 'UL' means "unsigned long"
for (int i = 0; i < NumberOfSamples; i++) {
total += analogRead(pinSensor);
delayMicroseconds(150); // about 500us per loop
}
// Calculating the formula used to convert voltage to pressure in KPA
// Calculate the voltage with floating point
float Vout = (float) total / (float) NumberOfSamples * 5.0 / 1024.0; // 1024 steps from 0V to 5V.
float alpha = 0.036; //Alpha Value
float beta = 0.04; //Beta Value
float Pe = 0.375; //Pressure Error Value
float Te = -70; // Temprature Error Value
float Vdd = 4.95; // Voltage in
float pressure = ( Vout / (alpha * Vdd) ) + ( beta * Pe * Te ); // Calculating Pressure from given variables in KPA
float p = (pressure * 0.145037738); // Calculating PSI from the given pressure
return ( p); // return the pressure in psi
}