// Arduino Uno Medidor de liquidos Ultrassônico / Sistema de Sensor de Nível de Líquido
// include the library code:
#include <LiquidCrystal.h>
#include <math.h>
//Vars de inicialização
// Lembrar de analisar o tamanho do local
const int tankEmptyDepth = 35;//(comprimento do sensor ate o fundo da caixa)NÃO DEVE exceder 450 cm (500 cm para o HC-SR04)!
const int tankFullDepth = 10; //(distânciado sensor ao liquido) Deve ser pelo menos 25 cm, se possível (2 cm para o HC-SR04)
// Varoiavel para analisar se está seco ou cheio
const int tank50Percent = 23;// distância minima para acionar a bomba
const int tank90Percent = 10;// distância maxima para desligar a bomba
// Altere as constantes tankEmptyDepth e tankFullDepth acima para a distância (em centímetros):
// - tankEmptyDepth = entre o sensor ultrassônico e o nível vazio (ou seja, parte inferior do tanque)
// - tankFullDepth = entre o sensor e o líquido quando o tanque está cheio (MÍNIMO 25 cm)
// Observe que o sensor ultrassônico funciona apenas entre 25cm e 450cm, portanto o tankFullDepth mínimo = 25.
// Para o meu tanque, tankFullDepth = 15, o que é bom ... MAS significa que quando o tanque está cheio,
// Provavelmente receberei leituras incorretas até o nível cair 10 cm. Isso não é um problema no meu caso
// Como não me preocupo com leituras precisas de nível quando o tanque de combustível está cheio! Mas isso significa que
// depois de encher o tanque, o nível mostrará quase vazio ou "Error: Timeout".
// Você também pode usar o HC-SR04, que é maior, mas tem uma profundidade mínima de 2 cm e máxima de 500 cm.
// Observe, no entanto, que não é à prova d'água! Eu escolhi o JSN-SR04T-2.0 por esse motivo.
// Observe também que você pode definir tankEmptyDepth como menor que o fundo do seu tanque,
// esp se você tiver um líquido de sucção de alimentação vertical, digamos, 5 cm acima do fundo do tanque.
// Por exemplo, meu tanque tem 163 cm de profundidade do sensor, então defino tankEmptyDepth como 153. Isso garante
// que quando meu LevelMatic ler 0%, eu devo ter 10 cm de combustível restante no tanque.
// Se medir em polegadas: 1 polegada = 2,54 cm
// Esses vars mantêm o nível atual e o último medido do sensor
int currentLevel = 0; //nível atual
int lastLevel = 0; //último nível
// Esses vars são para showPartialBarChar () e showCurrentLevel ()
int done = 0;
String levelTxt = "BOMBA AUT ???";
// Var for showError
// This error means the ultrasound unit couldn't do a measurement, and timed out
// Usually that means the sensor is at a weird angle, too close to the liquid, or
// the ultrasound waves are bouncing off the walls of the tank.
char timeoutErrorTxt[] = "ERROR: Timeout";
// Var para resposta de eco da placa do sensor ultrassônico
unsigned long timeHigh;
// Caracteres personalizados para LCD
byte barEmpty[8] = {
B11111,
B00000,
B00000,
B00000,
B00000,
B00000,
B00000,
B11111,
};
byte barOne[8] = {
B11111,
B10000,
B10000,
B10000,
B10000,
B10000,
B10000,
B11111,
};
byte barTwo[8] = {
B11111,
B11000,
B11000,
B11000,
B11000,
B11000,
B11000,
B11111,
};
byte barThree[8] = {
B11111,
B11100,
B11100,
B11100,
B11100,
B11100,
B11100,
B11111,
};
byte barFour[8] = {
B11111,
B11110,
B11110,
B11110,
B11110,
B11110,
B11110,
B11111,
};
// constantes para o pino do LED
const int ledPin = 6;
// Booleanos para controle manual
bool isManual = false;
bool isOn = false;
// constantes para os botões
const int buttonOnOffPin = 10, buttonManualPin = 13;
// constantes para pinos IO da placa do sensor ultrassônico
const int trigPin = 8, echoPin = 9;
// constantes para pinos IO do LCD
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
void setup() {
// Set up pins for leds
pinMode(ledPin, OUTPUT);
// Set up pins for buttons
pinMode(buttonOnOffPin, INPUT);
pinMode(buttonManualPin, INPUT);
// Set up IO pins for ultrasonic sensor board
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
// Set trigger pin for sensor to 0 (aka "do nothing yet")
digitalWrite(trigPin, LOW);
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
// Set custom chars 0-7
lcd.createChar(0, barEmpty);
lcd.createChar(1, barOne);
lcd.createChar(2, barTwo);
lcd.createChar(3, barThree);
lcd.createChar(4, barFour);
lcd.clear();
}
void loop() {
// Do level scan with ultrasonic board
// Start a scan - trigger pin must be high for at least 10us
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
// Get time echo pin is high using pulseIn, which returns value in microseconds
// Default timeout of 1s is more than enough for 8 pulses, and we're not in a hurry
timeHigh = pulseIn(echoPin, HIGH);
if (timeHigh == 0) {
// Oops! Timeout...
showError();
} else {
// Calculate level
// Assume 343 m/s for the speed of the 40kHz sound waves in air at standard temperature and pressure
// It's 58 us/cm, which we get from:
// (343 m/s * (1s / 1000000 us) * (100cm / 1m)) / 2 = 0.01715 cm / us
// Must divide by 2 because sound wave travels to liquid, and back
// Invert that to get:
// 1 / 0.01715 cm/us = 58.309038 us/cm
// Note resolution of ultrasonic sensor is +/- 0.5cm
currentLevel = round(timeHigh / 58);
if (currentLevel > tankEmptyDepth) {
// If level is lower than empty, show 0%
// This is useful if you want to have "empty" be "still 10cm of liquid left in tank"
currentLevel = tankEmptyDepth;
} else if (currentLevel < tankFullDepth) {
// If level is higher than full, show 100%
// This is useful since "full" level may vary when tank is refilled
currentLevel = tankFullDepth;
}
if (digitalRead(buttonManualPin) == HIGH) {
isManual = !isManual;
}
if (!isManual) {
// Condicionais para controle do tanque
if (currentLevel >= tank50Percent) {
digitalWrite(ledPin, HIGH);
}
if (currentLevel <= tank90Percent) {
digitalWrite(ledPin, LOW);
}
} else {
// Ligar e desligar pino pro enchimento da caixa
// de forma automática
if (digitalRead(buttonOnOffPin) == HIGH) {
if (digitalRead(ledPin) == HIGH) {
digitalWrite(ledPin, LOW);
} else {
digitalWrite(ledPin, HIGH);
}
}
}
// Don't redraw screen if level is the same as last time
if (currentLevel != lastLevel) {
lastLevel = currentLevel;
}
showCurrentLevel();
}
// Delay 2s between scans
delay(2000);
}
void showError() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(timeoutErrorTxt);
}
void showPartialBarChar(int val) {
switch (val) {
case 0:
lcd.write(byte(0)); // barEmpty
++done;
break;
case 1:
lcd.write(byte(1)); // one bar
++done;
break;
case 2:
lcd.write(byte(2)); // two bars
++done;
break;
case 3:
lcd.write(byte(3)); // three bars
++done;
break;
case 4:
lcd.write(byte(4)); // four bars
++done;
break;
}
}
void showCurrentLevel() {
// Get integer between 0 and 50 for bar graph
// We have 10 progress bar characters, and each character can have 0-5 vertical columns of pixels
// Subtracting tankFullDepth gives us a precise ratio between full/empty, as if the ultrasonic sensor
// would be 0 cm away from the liquid level when the tank is full.
// Also, currentLevel contains height of "emptiness" above the liquid, so to get liquid level we do:
// abs(1 - currentLevel/tankEmptyDepth).
float ratio = 1 - ((float)currentLevel - (float)tankFullDepth) / ((float)tankEmptyDepth - (float)tankFullDepth);
ratio = abs(ratio);
int textLevelInt = round(ratio * 100.0);
int levelInt = round(ratio * 50.0);
int fulls = 0;
// Reset done
done = 0;
// Monta mensagem do LCD
String manual = isManual ? "MAN" : "AUT";
String onOff = digitalRead(ledPin) == HIGH ? "ON" : "OFF";
levelTxt = "BOMBA " + manual + " " + onOff;
// Display text above progress bar
lcd.clear();
lcd.setCursor(0, 1);
lcd.print(levelTxt);
// Display progress bar based on levelInt
lcd.setCursor(0, 0);
// Draw progress bar for XX%
fulls = levelInt / 5;
if (fulls == 0) {
// First char on bar is a partial char with 0-4 vertical columns of pixels
showPartialBarChar(levelInt);
} else {
for (int i = 0; i < fulls; ++i) {
lcd.write(255); // full
++done;
}
}
if (done < 10) {
if (fulls > 0) {
// Here we may have a partial char with 0-4 vertical columns of pixels
showPartialBarChar(levelInt - (fulls * 5));
}
// Here we may have blank boxes left
if (done < 10) {
// We have empty boxes to draw
for (int i = 0; i < 10 - done; ++i) {
lcd.write(byte(0)); // barEmpty
}
}
}
// Lastly, print percentage:
if (textLevelInt == 100) {
lcd.setCursor(12, 0);
lcd.print("100%");
} else if (textLevelInt < 10) {
lcd.setCursor(14, 0);
lcd.print(textLevelInt);
lcd.print("%");
} else {
lcd.setCursor(13, 0);
lcd.print(textLevelInt);
lcd.print("%");
}
}