/*
*
* 2022-09-09: LCD interface Added
* 2022-09-09: Buzzer with frequecy alteration added.
* 2022-09-19: 2 Ultra sonic sensors - added
* 2022-12-23: average calculation corrected.
* 2022-12-24: percetage calculation added
* 2022-12-25: display functionality added
*/
// include the library code:
#include <LiquidCrystal.h>
#include <stdio.h>
#include <string.h>
// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
/*Board specific values*/
const int rs = 7, en = 6, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
const int buzzer = 8;
#define echoPin_S1 10 // attach pin D10 Arduino to pin Echo of S1 HC-SR04
#define trigPin_S1 11
#define trigPin_S2 13 // attach pin D13 Arduino to pin Trigger of S2 HC-SR04
#define echoPin_S2 12
/*Program specific values*/
#define NUMBER_OF_SENSORS 2
#define ARRAY_SIZE 2
//#define DEBUG_PRINT_MAIN
//#define DEBUG_PRINT_CP
//#define DEBUG_PRINT
//#define DEBUG_PRINT_SENSOR
//#define DEBUG_PRINT_ULTASONIC_READ
//#define DEBUG_PRINT_BUZZER
#define DEBUG_DISP_FN
#define ULTASONIC_TEST_CODE
#define lower_th 35
#define upper_th 94
#define DISPLAY_TIME 1 /*seconds*/
#define MAXIMUM_NO_OF_DISP_STATES 3
/*Environment specific values*/
#define MINIMUM_DISTANCE_TOSENSOR 65 /*cm*/
#define MAXIMUM_DISTANCE_TOSENSOR 110 /*cm*/
#define MINIMUM_DISTANCE (MINIMUM_DISTANCE_TOSENSOR +5) /*cm*/
#define MAXIMUM_DISTANCE (MAXIMUM_DISTANCE_TOSENSOR-5) /*cm*/
enum sensor_ret{
SENSOR_OK = 0,
SENSOR_NOT_OK =1,
SENSOR_NOT_PRESET =2,
SENSOR_DAMAGED = 3,
};
enum motor_state{
MOTOR_OFF,
MOTOR_ON,
MOTOR_IDLE,
MOTOR_ERROR,
MOTOR_STATE_MAXIMUM
};
typedef struct
{
int triggerPin;
int echoPin;
}sensor_config_t;
typedef struct
{
sensor_config_t sensor_config;
}sensor_array_config_t;
sensor_array_config_t sensor_array_config[]=
{ {trigPin_S1,echoPin_S1},
{trigPin_S2,echoPin_S2}
};
struct motor_t
{
bool motor_refelling_requested;
bool motor_error_detected;
int distance; /*distance from sensor */
int water_percentage;
int last_min_water_percentage;
int motor_refelling_start_val;
int motor_refelling_start_time;
motor_state state_triggered;
motor_state prev_state;
motor_state motor_run_val;
};
struct motor_t motor_s;
int read_ultrasensor(int sensor)
{
long duration; // variable for the duration of sound wave travel
int distance; // variable for the distance measurement
int triggerPin = sensor_array_config[sensor].sensor_config.triggerPin;
int echoPin= sensor_array_config[sensor].sensor_config.echoPin;
digitalWrite(triggerPin, LOW);
delayMicroseconds(2);
// Sets the trigPin HIGH (ACTIVE) for 10 microseconds
digitalWrite(triggerPin, HIGH);
delayMicroseconds(10);
digitalWrite(triggerPin, LOW);
// Reads the echoPin, returns the sound wave travel time in microseconds
duration = pulseIn(echoPin, HIGH);
// Calculating the distance
distance = duration * 0.034 / 2; // Speed of sound wave divided by 2 (go and back)
#ifdef ULTASONIC_TEST_CODE
static int test_distance = MAXIMUM_DISTANCE+15;
if (test_distance > (MINIMUM_DISTANCE -15))
{
test_distance= test_distance-1;
}
else
{
test_distance = MAXIMUM_DISTANCE+15;
}
distance = test_distance;
#endif
#ifdef DEBUG_PRINT_ULTASONIC_READ
Serial.print("Distance: ");
Serial.print(distance);
Serial.println(" cm");
#endif
return distance;
}
void buzzer_musical_function(int seconds)
{
for ( int freq = 5;freq < 14 ; freq++)
{
tone(buzzer, freq*50); // Send 1KHz sound signal...
delay(seconds*100);
}
return;
}
int get_sensor_average(int * distance)
{
#ifdef DEBUG_PRINT_SENSOR
Serial.print("get_sensor_average fn - START\n ");
#endif
int ret = SENSOR_OK;
int avg_distance = 0;
int avg_distance1 = 0;
int avg_distance2 = 0;
if (NUMBER_OF_SENSORS ==2 )
{
avg_distance1 = read_ultrasensor(0);
avg_distance2 = read_ultrasensor(1);
avg_distance = (avg_distance1 + avg_distance2)/2;
#ifdef DEBUG_PRINT_SENSOR
Serial.print("2 sensors method:\n ");
Serial.print(avg_distance1);
Serial.print(",");
Serial.print(avg_distance2);
Serial.print(",");
Serial.print(" average = ");
Serial.print(avg_distance);
Serial.print(" \n");
#endif
}
else
{
avg_distance = read_ultrasensor(0); //single sensor number mark here
#ifdef DEBUG_PRINT_SENSOR
Serial.print("One2 sensors method:\n ");
Serial.print(" average = ");
Serial.print(avg_distance);
Serial.print(" \n");
#endif
}
if ((avg_distance > MINIMUM_DISTANCE) && (avg_distance < MAXIMUM_DISTANCE))
{
/*Sensor values correct*/
*distance = avg_distance;
#ifdef DEBUG_PRINT_SENSOR
Serial.print("Sensor values verified, and OK \n");
#endif
}
else
{
/*Sensor values incorrect, update error*/
ret = SENSOR_NOT_OK;
#ifdef DEBUG_PRINT_SENSOR
Serial.print("Sensor values incorrect, and NOT_OK \n");
#endif
}
#ifdef DEBUG_PRINT_SENSOR
Serial.print("get_sensor_average fn - END\n ");
#endif
return ret;
}
/**/
int get_accurate_distance()
{
#ifdef DEBUG_PRINT
Serial.print("get_accurate_distance fn - START\n ");
#endif
static int distance_array[ARRAY_SIZE];
static int loopindex= 0;
static bool array_empty = HIGH;
static int calulated_average = 0;
int distance = 0;
int array_sum = 0;
bool sensor_data_avaialble = LOW;
if (array_empty == HIGH)
{
#ifdef DEBUG_PRINT
Serial.print("array empty \n ");
#endif
/*Initialise array first time*/
for (loopindex = 0; loopindex<ARRAY_SIZE; loopindex++)
{
distance_array[loopindex] = 0;
}
#ifdef DEBUG_PRINT
Serial.print("array initialized to 0\n ");
#endif
sensor_data_avaialble = HIGH;
for (loopindex = 0; loopindex<ARRAY_SIZE; loopindex++)
{
#ifdef DEBUG_PRINT
Serial.print("Read sensor data ");
Serial.print(loopindex);
Serial.print("\n");
#endif
if (SENSOR_OK == get_sensor_average(&distance))
{
distance_array[loopindex] = distance;
#ifdef DEBUG_PRINT
Serial.print("sensor data ");
Serial.print(loopindex);
Serial.print(" = ");
Serial.print(distance);
Serial.print("cm \n");
#endif
}
else
{
sensor_data_avaialble = LOW;
#ifdef DEBUG_PRINT
Serial.print("sensor data NOT_AVAILABLE \n");
#endif
}
#ifdef DEBUG_PRINT
Serial.print("Read sensor data ");
Serial.print(loopindex);
Serial.print(" completed \n");
#endif
}
if (sensor_data_avaialble == HIGH)
{
array_empty = LOW;
#ifdef DEBUG_PRINT
Serial.print("array filled \n ");
#endif
}
}
else /*Non empty array*/
{
#ifdef DEBUG_PRINT
Serial.print("Array Non empty \n ");
Serial.print("Loop Index: ");
Serial.print(loopindex);
Serial.print(" \n");
#endif
if (loopindex > ARRAY_SIZE-1)
{
loopindex = 0;
#ifdef DEBUG_PRINT
Serial.print("Loop Index greater than array size, set to 0 \n ");
#endif
}
if (SENSOR_OK == get_sensor_average(&distance))
{
distance_array[loopindex] = distance;
sensor_data_avaialble = HIGH;
loopindex++;
#ifdef DEBUG_PRINT
Serial.print("New data read completed \n ");
#endif
}
else
{
sensor_data_avaialble = LOW;
#ifdef DEBUG_PRINT
Serial.print("Data read Failed \n ");
#endif
}
}
if (sensor_data_avaialble == HIGH)
{
for (int arrayindex = 0; arrayindex<ARRAY_SIZE; arrayindex++)
{
array_sum += distance_array[arrayindex];
}
#ifdef DEBUG_PRINT
Serial.print(" SUM of array indexes = ");
Serial.print(array_sum);
Serial.print("\n");
#endif
calulated_average = array_sum/ARRAY_SIZE;
#ifdef DEBUG_PRINT
Serial.print("average calculation: \n ");
for(int i =0; i<ARRAY_SIZE;i++)
{
Serial.print("| ");
Serial.print(i);
Serial.print(" ");
}
Serial.print("| \n ");
for(int i =0; i<ARRAY_SIZE;i++)
{
Serial.print("| ");
Serial.print(distance_array[i]);
Serial.print(" ");
}
Serial.print("| \n");
Serial.print(calulated_average);
Serial.print("\n");
#endif
}
#ifdef DEBUG_PRINT
Serial.print("get_accurate_distance fn - END\n ");
#endif
return calulated_average;
}
int calculate_percentage(int dist_in_cm)
{
#ifdef DEBUG_PRINT_CP
Serial.print("calculate_percentage fn - START\n ");
#endif
int percentage = 0;
#ifdef DEBUG_PRINT_CP
Serial.print("dist_in_cm = ");
Serial.print(dist_in_cm);
Serial.print(" \n");
#endif
if ((dist_in_cm >= MINIMUM_DISTANCE) && (dist_in_cm <= MAXIMUM_DISTANCE))
{
percentage = (((MAXIMUM_DISTANCE-MINIMUM_DISTANCE) -
(dist_in_cm-MINIMUM_DISTANCE)))*100 /
(MAXIMUM_DISTANCE-MINIMUM_DISTANCE);
}
#ifdef DEBUG_PRINT_CP
Serial.print("Percentage = ");
Serial.print(percentage);
Serial.print(" \n");
#endif
#ifdef DEBUG_PRINT_CP
Serial.print("calculate_percentage fn - END\n ");
#endif
return percentage;
}
/**/
void setup() {
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
pinMode(buzzer, OUTPUT); // Set buzzer - pin 8 as an output
// Print a message to the LCD.
lcd.print("shadow coders");
digitalWrite(buzzer,HIGH);
pinMode(trigPin_S2, OUTPUT); // Sets the trigPin as an OUTPUT
pinMode(echoPin_S2, INPUT); // Sets the echoPin as an INPUT
Serial.begin(9600); // // Serial Communication is starting with 9600 of baudrate speed
#ifdef DEBUG_PRINT
Serial.println("shadow coders"); // print some text in Serial Monitor
Serial.println("Ultrasonic sensor used motor controller with Arduino UNO R3");
#endif
digitalWrite(buzzer,HIGH);
delay(500);
digitalWrite(buzzer,LOW);
delay(500);
}
/**/
char * messagePadded1 = "water fills from 35% to 95%.";
//char * messagePadded2 = "% to";
char * messagePadded3 = "% to";
char * messagePadded5 = "%.";
void showLetters(int printStart, int startLetter)
{
//char * messagePadded
//strcat(messagePadded1, messagePadded3);
lcd.setCursor(printStart, 1);
for (int letter = startLetter; letter <= startLetter + 15; letter++) // Print only 16 chars in Line #2 starting 'startLetter'
{
lcd.print(messagePadded1[letter]);
/* lcd.print(lower_th);
lcd.print("% to ");
lcd.print(upper_th);
lcd.print("%. \n");*/
}
lcd.print(" ");
delay(250);
}
/*Display function
displays varios operating mode info
*/
void display_function()
{
/*each display shall be 8 seconds*/
static int display_state = 0;
static int seconds = 0;
static int last_transition = 0;
#ifdef DEBUG_DISP_FN
Serial.println("display_function - START");
#endif
seconds= (millis() / 1000);
if (seconds > (last_transition+DISPLAY_TIME))
{
last_transition = seconds;
display_state++;
#ifdef DEBUG_DISP_FN
Serial.println("display state changed, new state: ");
Serial.println(display_state);
Serial.println("\n ");
#endif
}
else
{
#ifdef DEBUG_DISP_FN
Serial.println("display state NOT changed, current state: ");
Serial.println(display_state);
Serial.println("\n ");
#endif
/*Do nothing, display less than prefined time*/
}
if (display_state > MAXIMUM_NO_OF_DISP_STATES)
{
display_state = 0;
}
if ((motor_s.motor_error_detected = HIGH) ||
(motor_s.motor_refelling_requested == HIGH))
{
display_state = 2;
}
switch (display_state)
{
case 0:
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("water level:");
lcd.print(motor_s.water_percentage);
lcd.print("%");
break;
case 1:
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("WATER CONTROLS");
lcd.setCursor(0, 1);
lcd.print("FROM ");
lcd.print(lower_th);
lcd.print("% to ");
lcd.print(upper_th);
lcd.print("%.");
//for (int letter = 0; letter <= strlen(messagePadded1) - 16; letter++) //From 0 to upto n-16 characters supply to below function
//{
//showLetters(0, letter);
//}
break;
case 2:
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("motor ");
switch (motor_s.state_triggered)
{
case MOTOR_ON:
lcd.print("is ON");
break;
case MOTOR_OFF:
lcd.print("is OFF");
break;
case MOTOR_IDLE:
if (motor_s.motor_refelling_requested == HIGH)
{
lcd.print("is ON");
lcd.setCursor(0, 1);
lcd.print("[");
for(int i = 0;i< (motor_s.water_percentage/10);i++)
{
lcd.print("*");
}
for(int i = (motor_s.water_percentage/10); i<10 ;i++)
{
lcd.print("_");
}
lcd.print("] ");
lcd.print(motor_s.water_percentage);
lcd.print("%");
}
else
{
lcd.print("is OFF");
}
break;
case MOTOR_ERROR:
lcd.print("ERROR !!");
break;
default:
lcd.print("is OFF");
break;
}
break;
default:
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("shadow coders");
lcd.setCursor(0, 1);
lcd.print("_____1Byte_______");
break;
}
#ifdef DEBUG_DISP_FN
Serial.println("display_function - END");
#endif
}
motor_state threshould_check()
{
motor_state triggered;
if (motor_s.water_percentage < lower_th)
{
triggered = MOTOR_ON;
if (motor_s.motor_refelling_requested != HIGH)
{
motor_s.motor_refelling_requested = HIGH;
motor_s.motor_refelling_start_time = millis()/1000;
motor_s.motor_refelling_start_val = motor_s.water_percentage;
}
}
else if (motor_s.water_percentage > upper_th)
{
triggered = MOTOR_OFF;
motor_s.motor_refelling_requested = LOW;
}
else
{
triggered = MOTOR_IDLE;
}
return triggered;
}
motor_state motor_error_check()
{
static int seconds = 0;
#ifdef DEBUG_MOTOR_CHECK_FN
Serial.println("motor_error_check - START");
#endif
if (((motor_s.motor_refelling_requested = HIGH)
&& (motor_s.state_triggered = MOTOR_IDLE))
|| (motor_s.state_triggered = MOTOR_ON))
{
#ifdef DEBUG_MOTOR_CHECK_FN
Serial.println("MOTOR REFELLING REQUEST PRESENT\n");
#endif
seconds= (millis() / 1000);
if (seconds > motor_s.motor_refelling_start_time)
{
/*At least 1 min elapsed after motor triggered*/
if (motor_s.motor_refelling_start_val < motor_s.water_percentage)
{
/*change in water level detected, motor working*/
#ifdef DEBUG_MOTOR_CHECK_FN
Serial.println("MOTOR WORKING PROPERLY IN LAST 1 MIN\n");
#endif
}
else
{
motor_s.motor_error_detected = HIGH;
motor_s.state_triggered = MOTOR_ERROR;
/*NO change in water level detected after 1 min, motor NOT working*/
#ifdef DEBUG_MOTOR_CHECK_FN
Serial.println("MOTOR NOT WORKING, NO CHNAGE IN WATER LEVEL AFTER 1 MIN OF START.\n");
#endif
}
}
else
{
/*Do nothing, motor working less than 1 min only, hold on*/
}
}
#ifdef DEBUG_MOTOR_CHECK_FN
Serial.println("motor_error_check - END");
#endif
}
void buzzer_control(void)
{
#ifdef DEBUG_PRINT_BUZZER
Serial.println("buzzer_control - START");
#endif
if (motor_s.prev_state != motor_s.state_triggered)
{
switch( motor_s.state_triggered)
{
case MOTOR_OFF:
/*turning off audio*/
#ifdef DEBUG_PRINT_BUZZER
Serial.println("MOTOR_OFF AUDIO");
#endif
break;
case MOTOR_ON:
/*turning on audio*/
#ifdef DEBUG_PRINT_BUZZER
Serial.println("MOTOR_ON AUDIO");
#endif
break;
case MOTOR_IDLE:
/*No audio*/
#ifdef DEBUG_PRINT_BUZZER
Serial.println("MOTOR_IDLE: NO AUDIO");
#endif
break;
case MOTOR_ERROR:
break;
default:
break;
}
}
#ifdef DEBUG_PRINT_BUZZER
Serial.println("buzzer_control - END");
#endif
return;
}
void loop() {
int distance;
static int starpos;
//buzzer_musical_function(1);
distance = get_accurate_distance();
#ifdef DEBUG_PRINT_MAIN
Serial.println("distance = ");
Serial.println(distance);
Serial.println("\n");
#endif
motor_s.water_percentage = calculate_percentage(distance);
#ifdef DEBUG_PRINT_MAIN
Serial.println("percentage = ");
Serial.println(motor_s.water_percentage);
Serial.println("\n");
#endif
motor_s.state_triggered = threshould_check();
buzzer_control();
motor_error_check();
/*identify auto trigger coditions*/
/*manage buzzer*/
/*save in eep*/
/*once filled/empty, turn on buzzer format, save fill count in eeprom, time to fill display.*/
display_function();
delay(500);
}