Hardware components | ||||||
| × | 2 | ||||
| × | 2 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
| ||||||
Hand tools and fabrication machines | ||||||
| ||||||
|
I've been rebuilding an old motorhome and added a second fuel tank. For simplicity sake, I chose to use one tank for fueling the motor and then use the other to transfer fuel to the primary tank when needed.
There are 2 screen modes. The primary screen displays the fuel level in the reserve tank and the second mode displays a screen for setting transfer volume, flow rates, and delivered volume. A couple safety features are included: Transfer volume cannot be set greater than or equal to volume in reserve tank and, pump relay is turned off when transfer volume is acquired.
I'm only have intermediate experience with Arduino coding so I'm sure there is a better way to skin this cat. Could have use SPI protocol, but decided on I2C as I was thinking the display would be remote from the processor and wanted as few wires as possible. Note that circuit changes on the back of the display are necessary to use I2C protocol. I've noted the required changes on the schematic.
Transfer Mode Screen
A little messy, but it works :)
// set variables needed to translate button states and delay timer
#include <U8g2lib.h> //Include library for 128x64 OLED 2.42 display
#include <Wire.h>
U8G2_SSD1309_128X64_NONAME0_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ 10);
int fuelPin = A0;
int fuelValue = 0;
int level = 0; //29 represents the tank displaying as FULL
int pumpState = 0;
int gallons = 0;
float volume = 0;
byte onPin = 9;
byte modePin = 11; //selects transfer screen with on button
const byte enterPin = 12; //Starts pump with momentary button
byte enterState = 0; //state of button for committing pump start
byte sensorPin = 2; //flow meter signal input and initiates interrupt
byte upButton = 4;//Up botton to incement fuel volume request
byte downButton = 3;//down button to decrement fuel volume request
int counter = 0;
const byte pumpPin = 13; //output pin to BOB for relay
int dt = 100;
byte sensorInterrupt = 0; // 0 = digital pin 2 flow meter input
float pulseFactor = 2.5; //milliters per pulse
float gallonFactor = 0.26417; //amount of gallons/Liter
volatile int pulseCount; //variable must be volitile when used in interrupt routine
float flowRate;
unsigned long int flowMilliLitres;
unsigned long totalMilliLitres;
unsigned long previousTime;
unsigned long elapsedTime;
void setup() {
Serial.begin(9600);
u8g2.begin();
u8g2.clearBuffer();
pinMode (pumpPin, OUTPUT);
pinMode (onPin, OUTPUT);
pinMode (modePin, INPUT_PULLUP); //just need to ground pin to activate (LOW is active)
pinMode (enterPin, INPUT_PULLUP); //just need to ground pin to activate (LOW is active)
pinMode (upButton, INPUT_PULLUP); //just need to ground pin to activate (LOW is active)
pinMode (downButton, INPUT_PULLUP); //just need to ground pin to activate (LOW is active)
digitalWrite(pumpPin, LOW);
pinMode(sensorPin, INPUT);
digitalWrite(sensorPin, HIGH);
pulseCount = 0;
flowRate = 0.0;
flowMilliLitres = 0;
totalMilliLitres = 0;
previousTime = 0;
// The Hall-effect sensor is connected to pin 2 which uses interrupt 0.
// Configured to trigger on a FALLING state change (transition from HIGH
// state to LOW state)
attachInterrupt(sensorInterrupt, pulseCounter, FALLING);
}
void loop() {
checkEnterButton();
if(digitalRead(modePin) == LOW)
{
calculateAndDisplayFlow();
checkUpDownButtonCount();
}
else
{
readAndDisplayLevel(); //run subroutine when pump switch is off
}
}//end loop
void readAndDisplayLevel()
{
fuelValue = analogRead(fuelPin);
level = map(fuelValue, 510, 119, 0, 29); //510 and 119 represent full and empty levels through voltage divider
if(level < 0){ //29 represent toal number of bars (|) for full on display
level = 0;
}
if(level > 29){
level = 29;
}
gallons = map(fuelValue, 510, 119, 0, 40); //Map same levels to gallon levels in a 40 gallon tank
if(gallons < 0){
gallons = 0;
}
if(gallons > 40){
gallons = 40;
}
counter = 0; //set trasfer volume and target back to zero if transfer operation switched back to fuel level
totalMilliLitres = 0;
u8g2.sendBuffer();
char buffer[32] = "";
for(int i=0; i<=level;i++) //this for loop will string indicator bars together to match level set by mapping tank float resistance
{
strcat(buffer, "|");
}
digitalWrite(onPin, LOW);
u8g2.clearBuffer();
u8g2.setCursor(1, 10); //position display cursor for first line of text
u8g2.setFont(u8g2_font_t0_13_mf); //set font size to 13
u8g2.print("Fuel Level Reserve");
u8g2.setCursor(2, 30); //position display cursor for second line of text
u8g2.setFont(u8g2_font_helvB08_tf); //set font size to 11 bold
u8g2.print(" E---------1/2---------F");
u8g2.setCursor(4, 40); //position display cursor for third line of text
u8g2.setFont(u8g2_font_4x6_tf);
u8g2.print(buffer); //print the level bars
u8g2.setFont(u8g2_font_t0_13_mf); //set font size to 13
u8g2.setCursor(40, 60); //position display cursor for forth line of text
u8g2.print(gallons); //print fuel level in gallons
u8g2.print(" Gal.");
u8g2.sendBuffer(); //send buffered text to display
delay(2000);
};//end readAndDisplayLevel
void pulseCounter() //Measure the number of pulses for 1 second into pulse counter
{
pulseCount++;
}//end pulseCounter
void checkEnterButton()
{
// read the state of the pushbutton value:
enterState = digitalRead(enterPin);
// check if the enter button is pressed to start pump. Flow sensor will then pulse interrrupt pin 2
if (enterState == LOW) {
digitalWrite(pumpPin, !digitalRead(pumpPin));
// debounce:
delay(300);
}
} //end checkEnterButton
void calculateAndDisplayFlow() // if on switch latched, transfer mode screen displayed
{
if((millis() - previousTime) > 1000) // Only process counters once per second
{
detachInterrupt(sensorInterrupt);
digitalWrite(onPin, HIGH);
flowMilliLitres = (pulseFactor * pulseCount); //flow volume (mls) during sampling imperval
flowRate = flowMilliLitres/((millis() - previousTime)/1000); //flow milliliters/sec
flowRate = flowRate * 0.016; // conversion factor to gallons/min.
previousTime = millis();
totalMilliLitres += flowMilliLitres;
volume = (totalMilliLitres/1000)*gallonFactor;
volume = (totalMilliLitres/1000)*.26417; //Convert milliters to Gallons
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_t0_13_mf);
u8g2.setCursor(4, 33);
u8g2.print("Target Vol: ");
u8g2.print(counter);
u8g2.print(" Gal");
u8g2.setCursor(4, 50);
u8g2.print("Flow: ");
u8g2.print(flowRate,1); // print to one decimal
u8g2.print(" Gal/min");
u8g2.setCursor(4, 18);
u8g2.print("Volume: ");
u8g2.print(volume);
u8g2.print(" Gal");
u8g2.drawFrame(1,1,126,60); //draw a frame around displayed data
u8g2.sendBuffer();
// Reset the pulse counter so we can start incrementing again
pulseCount = 0;
if(volume >= counter)//turn off pump if pumped volume is greater than set counted volume
{
enterState == LOW;
digitalWrite(pumpPin, LOW);//turn off pump
}
// Enable the interrupt again now that we've finished sending output to display
attachInterrupt(sensorInterrupt, pulseCounter, FALLING);
}
} //end calculateAndDisplayFlow
void checkUpDownButtonCount() // read the up and down pins:
{
byte upState = digitalRead(upButton);
byte downState = digitalRead(downButton)*-1;
if (upState == 0) {
counter++;
delay(400); //delay to prevent bounce from momentary switch
}
else if (downState == 0) {
counter--;
delay(400);
}
if (counter < 0){
counter = 0;
}
else if(counter >= gallons) { //turn off pump when transferred volume reaches request volume
counter = (gallons - 1);
}
} //end checkUpDownButtonCount
Comments