1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
| /* Regenput niveaumeting
*
* Saturday March 22, 2014
* Ruben Theys
*
* Sonar colors: VCC = brown / GND = Orange / TRIG = BLUE / ECHO = GREEN
*/
#include <LiquidCrystal.h>
const int sonar_trig = 3;
const int sonar_echo = 2;
const int LCD_backlight = 14;
const int LCD_RS = 12;
const int LCD_RW = 11;
const int LCD_EN = 10;
const int LCD_D4 = 15;
const int LCD_D5 = 16;
const int LCD_D6 = 17;
const int LCD_D7 = 18;
LiquidCrystal lcd(LCD_RS, LCD_RW, LCD_EN, LCD_D4, LCD_D5, LCD_D6, LCD_D7);
const float tank_sonar_offset = 20; // Sonar meauring point offset from tank's ceiling [mm]
const float tank_inner_radius = 1315; // Inner radius of the tank [mm]
const float tank_inner_height = 1860; // Inner height of the tank [mm]
const float tank_overflow_height = 320; // Distance from tank ceiling to overflow spillway
const float tank_min_measured_distance = tank_overflow_height - tank_sonar_offset;
const float tank_max_measured_distance = tank_inner_height - tank_sonar_offset;
const float tank_max_water_level = tank_max_measured_distance - tank_min_measured_distance;
const long LCD_backlight_time = 8000; // Timespan the backlight should remain on [ms]
long LCD_backlight_activation_millis; // Point in time when the green button was last pressed
const int LCD_backlight_activate = 7; // Pin 7 senses the button
// Progressbar characters
byte p20[8] = {B10000, B10000, B10000, B10000, B10000, B10000, B10000,}; // 20 pct block
byte p40[8] = {B11000, B11000, B11000, B11000, B11000, B11000, B11000,}; // 40 pct block
byte p60[8] = {B11100, B11100, B11100, B11100, B11100, B11100, B11100,}; // 60 pct block
byte p80[8] = {B11110, B11110, B11110, B11110, B11110, B11110, B11110,}; // 80 pct block
byte p100[8]= {B11111, B11111, B11111, B11111, B11111, B11111, B11111,}; // 100 pct block
// Running average
const int average_numReadings = 100;
float average_readings[average_numReadings];
int average_index = 0;
float average_total = 0;
float averageDistance = 0;
void setup() {
// 1. Configure backlight pin
pinMode(LCD_backlight, OUTPUT);
digitalWrite(LCD_backlight, HIGH);
// 2. Initialize LCD
lcd.begin(16,2);
lcd.clear();
lcd.setCursor(0,0);
// 3. Create percentagebar characters
lcd.createChar(0, p20);
lcd.createChar(1, p40);
lcd.createChar(2, p60);
lcd.createChar(3, p80);
lcd.createChar(4, p100);
// 4. Configure the sonar pins
pinMode(sonar_trig, OUTPUT);
pinMode(sonar_echo, INPUT);
// 5. Running-average setup
for (int i = 0; i < average_numReadings; i++)
average_readings[i] = 0;
// 6. LCD backlight-button setup.
pinMode(LCD_backlight_activate, INPUT_PULLUP);
LCD_backlight_activation_millis = millis();
}
void loop() {
float duration, sonarDistance;
int volume, percentage;
// 1. Generate a clean pulse for the sonar
digitalWrite(sonar_trig, LOW);
delayMicroseconds(2);
digitalWrite(sonar_trig, HIGH);
delayMicroseconds(10);
digitalWrite(sonar_trig, LOW);
// 2. Receive the sensor's echo.
duration = pulseIn(sonar_echo, HIGH);
// 3. Convert the echo's duration to a distance
sonarDistance = microsecondsToMillimeters(duration);
// 4. Calculate new running average
average_total -= average_readings[average_index]; // Substract oldest reading from total
average_readings[average_index] = sonarDistance; // Store new reading
average_total += average_readings[average_index]; // Add new reading to total
average_index++; // Move up one index in the circular buffer.
if (average_index >= average_numReadings) // If we reached the end, go back to the beginning
average_index = 0;
averageDistance = average_total / average_numReadings;
// 5. Calculate volume and percentage from new average
volume = sonarDistanceToVolume(averageDistance);
percentage = sonarDistanceToPercent(averageDistance);
// 6. Update LCD
lcd.setCursor(0,0);
lcd.print(volume);
lcd.print(" l. "); // Use enough spaced so the previous characters get erased when going from
// 4 to 3 to 2 to 1 digits (1000 -> 999, 100 -> 99, 10 -> 9)
lcd.setCursor(12,0);
if (percentage < 100)
lcd.write(" ");
if (percentage < 10)
lcd.write(" ");
if (percentage < 0) percentage = 0; // Never show negative volumes. Op=op
lcd.print(percentage);
lcd.print("%");
//// 7. Percentage bar
// 7.1 Whole blocks
lcd.setCursor(0,1);
double amountOfBlocks = 16.0/100 * percentage; // 16 is the amount of characters the LCD can handle.
int partialBlocks = amountOfBlocks * 5;
if (amountOfBlocks >= 1 ) {
int i = 1;
for (i = 1 ; i < amountOfBlocks ; i++) {
lcd.setCursor(i-1, 1);
lcd.write(4);
}
partialBlocks = (amountOfBlocks - i + 1)*5 ;
}
// 7.2 Partial blocks.
switch (partialBlocks) {
case 0: break;
case 1: lcd.print((char)0); break;
case 2: lcd.write(1); break;
case 3: lcd.write(2); break;
case 4: lcd.write(3); break;
case 5: lcd.write(4); break;
}
// 7.3 Empty rest of progress bar
for (int j=0 ; j<(16-amountOfBlocks+1) ; j++) {
lcd.print(" ");
}
unsigned long currentMillis = millis();
// 8. Check if we need to activate the backlight
int buttonState = digitalRead(LCD_backlight_activate);
if (buttonState == LOW) { // LOW because I'm using the internal pull-up resistor for this pin.
LCD_backlight_activation_millis = currentMillis; // Store the time of activation
digitalWrite(LCD_backlight, HIGH);
}
// 9. Check if we need to deactivate the backlight
if (currentMillis - LCD_backlight_activation_millis > LCD_backlight_time) {
digitalWrite(LCD_backlight, LOW);
}
// 10. Delay so the sonar has the time to properly send his echo-pulse
delay(100);
}
float microsecondsToMillimeters(float microseconds) {
return microseconds / 2.938 / 2;
}
int sonarDistanceToVolume(float current_measured_distance) {
// Volume of a cylindrical tank:
// V = height * pi * radius^2
return (tank_inner_height - current_measured_distance) * PI * pow((tank_inner_radius)/1000, 2);
}
int sonarDistanceToPercent(float current_measured_distance) {
float water_level = tank_max_measured_distance - current_measured_distance;
return (water_level / tank_max_water_level) * 100;
} |