#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <ESP32Servo.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
Servo mainservo;
const int pir = 18; //PIR sensor pin
const int r_led = 2; //RED LED pin
const int g_led = 4; //GREEN LED pin
const int Push_Button = 15;
const int servoPin = 19;
#define trigPin 13
#define echoPin 12
int Button_State = 0;
int pirState = LOW;
int threshold = 0;
boolean bin_empty = true;
boolean set_empty_level = false;
byte gauge_empty[8] = {B11111, B00000, B00000, B00000, B00000, B00000, B00000, B11111}; // empty middle piece
byte gauge_fill_1[8] = {B11111, B10000, B10000, B10000, B10000, B10000, B10000, B11111}; // filled gauge - 1 column
byte gauge_fill_2[8] = {B11111, B11000, B11000, B11000, B11000, B11000, B11000, B11111}; // filled gauge - 2 columns
byte gauge_fill_3[8] = {B11111, B11100, B11100, B11100, B11100, B11100, B11100, B11111}; // filled gauge - 3 columns
byte gauge_fill_4[8] = {B11111, B11110, B11110, B11110, B11110, B11110, B11110, B11111}; // filled gauge - 4 columns
byte gauge_fill_5[8] = {B11111, B11111, B11111, B11111, B11111, B11111, B11111, B11111}; // filled gauge - 5 columns
byte gauge_left[8] = {B11111, B10000, B10000, B10000, B10000, B10000, B10000, B11111}; // left part of gauge - empty
byte gauge_right[8] = {B11110, B00010, B00011, B00001, B00001, B00011, B00010, B11110}; // right part of gauge - empty
byte gauge_mask_left[8] = {B11111, B11111, B11111, B11111, B11111, B11111, B11111, B11111}; // mask for rounded corners for leftmost character
byte gauge_mask_right[8] = {B11110, B11110, B11111, B11111, B11111, B11111, B11110, B11110}; // mask for rounded corners for rightmost character
byte warning_icon[8] = {B00100, B00100, B01110, B01010, B11011, B11111, B11011, B11111}; // warning icon - just because we still have one custom character left
byte gauge_left_dynamic[8]; // left part of gauge dynamic - will be set in the loop function
byte gauge_right_dynamic[8]; // right part of gauge dynamic - will be set in the loop function
int empty_level = 0; // value for the CPU gauge
char buffer[16]; // helper buffer to store C-style strings (generated with sprintf function)
int move_offset = 0; // used to shift bits for the custom characters
const int gauge_size_chars = 16; // width of the gauge in number of characters
char gauge_string[gauge_size_chars + 1]; // string that will include all the gauge character to be printed
int parsed_int_from_serial;
int max_value = 0;
int level_value = 0;
int percentage = 0;
void setup() {
lcd.init(); // Initialize the LCD display
lcd.backlight();
lcd.createChar(7, gauge_empty); // middle empty gauge
lcd.createChar(1, gauge_fill_1); // filled gauge - 1 column
lcd.createChar(2, gauge_fill_2); // filled gauge - 2 columns
lcd.createChar(3, gauge_fill_3); // filled gauge - 3 columns
lcd.createChar(4, gauge_fill_4); // filled gauge - 4 columns
lcd.createChar(0, warning_icon); // warning icon - just because we have one more custom character that we could use
pinMode(pir, INPUT);
pinMode(r_led, OUTPUT);
pinMode(g_led, OUTPUT);
pinMode(Push_Button, INPUT);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
mainservo.attach(servoPin);
mainservo.write(20);
Serial.begin(9600);
}
void loop() {
Button_State = digitalRead(Push_Button);
int pirS = digitalRead(pir);
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
long duration = pulseIn(echoPin, HIGH);
long distance_cm = duration * 0.034 / 2;
empty_level = percentage - 5;
percentage = map(distance_cm, 5, threshold, 100, 0);
/*----------------------------------------------------------------------------------------*/
if (set_empty_level == false) {
lcd.setCursor(0, 0);
lcd.print(">>> Please Set EmptyLevel Using Button..");
if (Button_State == HIGH) {
lcd.clear();
threshold = distance_cm ;
lcd.setCursor(0, 0);
lcd.print("Empty Level Adding success...");
set_empty_level = true ;
delay(1000);
lcd.clear();
}
}
if (set_empty_level == true) {
if (distance_cm < 5) {
bin_empty = false;
}
if (distance_cm > 5) {
bin_empty = true;
}
if (bin_empty == false) {
digitalWrite(r_led, HIGH);
digitalWrite(g_led, LOW);
lcd.setCursor(0, 4);
lcd.print("> <");
delay(100);
lcd.setCursor(0, 4);
lcd.print(">> <<");
delay(100);
lcd.setCursor(0, 4);
lcd.print(">>> <<<");
delay(100);
lcd.setCursor(0, 4);
lcd.print(">>> Bin is Full <<<");
delay(100);
}
}
/*----------------------------------------------------------------------------------------- */
if (bin_empty == true && set_empty_level == true) {
if (pirS == HIGH) {
digitalWrite(r_led, HIGH);
digitalWrite(g_led, LOW);
mainservo.attach(servoPin);
if (pirState == LOW) {
Serial.println("Motion detected!");
bin_open();
pirState = HIGH;
lcd.clear();
}
} else {
mainservo.attach(servoPin);
digitalWrite(r_led, LOW);
digitalWrite(g_led, HIGH);
level();
if (pirState == HIGH) {
bin_close();
pirState = LOW;
}
}
}
}
void bin_open() {
for (int pos = 20; pos <= 180; pos += 1) {
mainservo.write(pos);
delay(15);
if (pos == 180) {
mainservo.detach();
}
}
}
void bin_close() {
for (int pos = 180; pos >= 20; pos -= 1) {
mainservo.write(pos);
delay(15);
if (pos == 20) {
mainservo.detach();
}
}
}
void level() {
float units_per_pixel = ((gauge_size_chars * 5.0) / threshold);
int value_in_pixels = round(empty_level * units_per_pixel);
int tip_position = 0;
if (value_in_pixels < 5) {
tip_position = 1; // tip is inside the first character
}
else if (value_in_pixels > gauge_size_chars * 5.0 - 5) {
tip_position = 3; // tip is inside the last character
}
else {
tip_position = 2; // tip is somewhere in the middle
}
move_offset = 4 - ((value_in_pixels - 1) % 5); // value for offseting the pixels for the smooth filling
for (int i = 0; i < 8; i++) { // dynamically create left part of the gauge
if (tip_position == 1) {
gauge_left_dynamic[i] = (gauge_fill_5[i] << move_offset) | gauge_left[i]; // tip on the first character
}
else {
gauge_left_dynamic[i] = gauge_fill_5[i]; // tip not on the first character
}
gauge_left_dynamic[i] = gauge_left_dynamic[i] & gauge_mask_left[i]; // apply mask for rounded corners
}
for (int i = 0; i < 8; i++) { // dynamically create right part of the gauge
if (tip_position == 3) {
gauge_right_dynamic[i] = (gauge_fill_5[i] << move_offset) | gauge_right[i]; // tip on the last character
}
else {
gauge_right_dynamic[i] = gauge_right[i]; // tip not on the last character
}
gauge_right_dynamic[i] = gauge_right_dynamic[i] & gauge_mask_right[i]; // apply mask for rounded corners
}
lcd.createChar(5, gauge_left_dynamic); // create custom character for the left part of the gauge
lcd.createChar(6, gauge_right_dynamic); // create custom character for the right part of the gauge
for (int i = 0; i < gauge_size_chars; i++) { // set all the characters for the gauge
if (i == 0) {
gauge_string[i] = byte(5); // first character = custom left piece
}
else if (i == gauge_size_chars - 1) {
gauge_string[i] = byte(6); // last character = custom right piece
}
else { // character in the middle, could be empty, tip or fill
if (value_in_pixels <= i * 5) {
gauge_string[i] = byte(7); // empty character
}
else if (value_in_pixels > i * 5 && value_in_pixels < (i + 1) * 5) {
gauge_string[i] = byte(5 - move_offset); // tip
}
else {
gauge_string[i] = byte(255); // filled character
}
}
}
// gauge drawing
lcd.setCursor(0, 0);
sprintf(buffer, "Level:%3d%% ", percentage);
lcd.print(buffer);
lcd.write(byte(0));
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print(gauge_string);
}