-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.c
354 lines (310 loc) · 8.94 KB
/
main.c
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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
/*
September 15, 2020
ATMEGA32 MQ135 Air Quality Sensor
The levels of CO2 in the air and potential health problems are:
250 - 400 ppm: background (normal) outdoor air level.
400 - 1,000 ppm: typical level found in occupied spaces with good air exchange.
1,000 - 2,000 ppm: level associated with complaints of drowsiness and poor air.
2,000 - 5,000 ppm: level associated with headaches, sleepiness, and stagnant, stale, stuffy air. Poor concentration, loss of attention, increased heart rate and slight nausea may also be present.
5,000 ppm: this indicates unusual air conditions where high levels of other gases could also be present. Toxicity or oxygen deprivation could occur. This is the permissible exposure limit for daily workplace exposures.
40,000 ppm: this level is immediately harmful due to oxygen deprivation.
*/
#define F_CPU 8000000UL //8Mhz CPU
#define RLOAD 22.0 //Resistor put on the MQ135 instead of original 1K its now 22K
#define RZERO 60.52
#include <avr.c>
#include <lcd.c> // 2x16 LCD
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <util/delay.h>
#include <MQ135.h>
unsigned char _DEBUG = 0; //Toggle debug
unsigned short measurements[120];
unsigned short one_hr_measurements[60];
unsigned short twenty_four_hr_measurements[24];
unsigned short rzero_measurements[30];
unsigned short high = 0, low = 8888, hours_elapsed = 0, counter = 0, rzerocounter = 0;
float one_min_avg = 0, one_hr_avg = 0, twenty_four_hr_avg = 0;
unsigned char been_min = 0, been_hour = 0, been_day = 0;
float rzero = 0;
//Clock
unsigned char sec = 0, min = 0, hours = 0;
/* LCD lines 1 and 2 */
char line1[17];
char line2[17];
int ADC_Read(char channel)
{
int Ain,AinLow;
ADMUX=ADMUX|(channel & 0x0f); /* Set input channel to read */
ADCSRA |= (1<<ADSC); /* Start conversion */
while((ADCSRA&(1<<ADIF))==0); /* Monitor end of conversion interrupt */
_delay_us(10);
AinLow = (int)ADCL; /* Read lower byte*/
Ain = (int)ADCH*256; /* Read higher 2 bits and
Multiply with weight */
Ain = Ain + AinLow;
return(Ain); /* Return digital value*/
}
void averages(){
one_min_avg = 0;
short i;
for (i = 0; i < 120; i++)
{
one_min_avg += measurements[i];
}
one_min_avg = one_min_avg/120.;
one_hr_measurements[min] = one_min_avg;
if (been_hour == 1 && sec == 0){ //Do calc every minute
for (i = 0; i < 60; ++i)
{
one_hr_avg += one_hr_measurements[i];
}
one_hr_avg = one_hr_avg / 60.;
twenty_four_hr_measurements[hours] = one_hr_avg;
//Average for the 24 hours
if (been_day == 1 && hours == 0) //Do calc once an hour
{
for (i = 0; i < 24; ++i)
{
twenty_four_hr_avg += twenty_four_hr_measurements[i];
}
twenty_four_hr_avg = twenty_four_hr_avg / 24.;
}
}
}
void clock(){
sec++;
if (sec >= 60){
been_min = 1;
sec = 0;
min += 1;
if (min >= 60){
been_hour = 1;
min = 0;
hours += 1;
hours_elapsed += 1;
if (hours == 24) {
been_day = 1;
hours = 0;
}
}
}
averages();
}
void light_up_led(){
}
/* PORTED CODE FROM https://github.com/GeorgK/MQ135 */
/**************************************************************************/
/*!
@brief Get the resistance of the sensor, ie. the measurement value
@return The sensor resistance in kOhm
*/
/**************************************************************************/
float getResistance() {
int val = ADC_Read(4);
return ((1023./(float)val) - 1.)*RLOAD;
}
/**************************************************************************/
/*!
@brief Get the resistance RZero of the sensor for calibration purposes
@return The sensor resistance RZero in kOhm
*/
/**************************************************************************/
float getRZero() {
return getResistance() * pow((ATMOCO2/PARA), (1./PARB));
}
/**************************************************************************/
/*!
@brief Get the ppm
*/
/**************************************************************************/
float getPPM() {
return PARA * pow((getResistance()/RZERO), -PARB);
}
/* END IMPORTED CODE */
void warm_up(){ // Runs once on boot to warm up sensor
char counter = 120;
while (counter > 0){
sprintf(line1, "Warming Up: %d",counter);
sprintf(line2, "PPM: %.1f", getPPM());
clr_lcd();
puts_lcd2(line1);
pos_lcd(1,0);
puts_lcd2(line2);
_delay_us(1000000);
counter--;
//Alternate LEDS while warming up
if (counter % 2 == 0)
{
SET_BIT(PORTC,7); //Red
_delay_us(240);
CLR_BIT(PORTC,7); //Red
}
else
{
SET_BIT(PORTC,6); //Yellow
_delay_us(240);
CLR_BIT(PORTC,6); //Yellow
}
}
measurements[0] = getPPM(); //Baseline for Hi/Lo
}
void debug()
{
//Rzero averaging
if (been_min == 0)
{
rzero = getRZero(); //If you want to print floats need to add -lprintf_flt under Misc in project settings and enable vprintf library under General more info https://startingelectronics.org/articles/atmel-AVR-8-bit/print-float-atmel-studio-7/
rzero_measurements[rzerocounter] = rzero;
rzerocounter++;
if (rzerocounter >= 30) rzerocounter = 0;
}
else
{
rzero = 0;
for (unsigned char i = 0; i < 30; ++i)
{
rzero += rzero_measurements[i];
}
rzero = rzero / 30;
rzero_measurements[rzerocounter] = getRZero();
rzerocounter++;
if (rzerocounter >= 30) rzerocounter = 0;
}
sprintf(line1, "PPM:%.1f r%.1f", getPPM(), getResistance());
sprintf(line2, "RAW:%i R0:%.2f", ADC_Read(4), rzero);
}
int main(void)
{
ini_lcd();
DDRA=0x0; /* Make ADC ports as input */
DDRC=0xFF; /* Make C ports as output for LEDs */
SET_BIT(DDRC,7);
SET_BIT(DDRC,6);
ADCSRA = 0x87; /* Enable ADC, fr/128 */
ADMUX = 0x40; /* Vref: Avcc, ADC channel: 0 */
if (_DEBUG == 0) warm_up();
counter = 0;
while (1)
{
if (_DEBUG == 0)
{
if (been_min == 0) sprintf(line1, "PPM:%.1f Hr: --", getPPM());
else if (been_hour == 1){
sprintf(line1, "PPM:%i H:%.1f", (unsigned int)one_min_avg, one_hr_avg);
}
else {
sprintf(line1, "PPM:%d Hr: --", (unsigned int)one_min_avg);
}
//Line 2
if (been_day == 1 && (sec < 15 || (sec > 30 && sec < 45))) sprintf(line2, "24H Avg: %.1f", twenty_four_hr_avg);
else if (been_day == 1 && (sec >= 45 || (sec >= 15 && sec <= 30)))
{
sprintf(line2, "%iH Hi:%i Lo:%i", hours_elapsed, high, low);
if (strlen(line2) > 16) sprintf(line2, "%iH Hi%i L%i", hours_elapsed, high, low);
}
else if (been_min == 1)
{
sprintf(line2, "%iH Hi:%i Lo:%i", hours_elapsed, high, low);
if (strlen(line2) > 16) sprintf(line2, "%iH Hi%i L%i", hours_elapsed, high, low);
}
}
else debug();
clr_lcd();
puts_lcd2(line1);
pos_lcd(1,0);
puts_lcd2(line2);
// LOGIC TO LOWER LED VOLTAGE I NEEDED THIS TO PREVENT 2.2v LEDS from BURNING OUT at 5v, below logic delivers ~2v to LEDS
//Wait a bit
if (_DEBUG == 0)
{
if (one_min_avg <= 1000) //LEDS OFF
{
_delay_us(500000); //Read every 0.5 sec, u_sec is 1/1 millionth of a sec
}
else if (one_min_avg > 1000 && one_min_avg < 1500) //Yellow LED Blink
{
float wait_timer = 2000;
while (wait_timer > 0)
{
if(sec % 2 == 0)
{
SET_BIT(PORTC,6); //Yellow
_delay_us(240);
CLR_BIT(PORTC,6); //Yellow
}
else _delay_us((240));
wait_timer--;
}
}
else if (one_min_avg >= 1500 && one_min_avg < 2000) //Yellow LED ON
{
float wait_timer = 2000;
while (wait_timer > 0){
SET_BIT(PORTC,6); //Yellow
_delay_us(240);
CLR_BIT(PORTC,6); //Yellow
wait_timer--;
}
}
else if (one_min_avg >= 2000 && one_min_avg < 5000) //Red LED ON
{
float wait_timer = 2000;
while (wait_timer > 0){
SET_BIT(PORTC,7); //Red
_delay_us(240);
CLR_BIT(PORTC,7); //Red
wait_timer--;
}
}
else if (one_min_avg >= 5000) //Red LED on and Yellow flashing
{
float wait_timer = 1000;
while (wait_timer > 0)
{
if (sec % 2 == 0)
{
SET_BIT(PORTC,6); //Yellow
_delay_us(240);
CLR_BIT(PORTC,6); //Yellow
}
else _delay_us((240));
SET_BIT(PORTC,7); //Red
_delay_us(240);
CLR_BIT(PORTC,7); //Red
wait_timer--;
}
}
}
/*
else //Debug mode both LEDs are blinking every 10 sec
{
float wait_timer = 2000;
while (wait_timer > 0)
{
if (sec % 10 == 0){
SET_BIT(PORTC,7); //Red
SET_BIT(PORTC,6); //Yellow
_delay_us(240);
CLR_BIT(PORTC,7); //Red
CLR_BIT(PORTC,6); //Yellow
}
else _delay_us(240);
wait_timer--;
}
}
*/
measurements[counter] = getPPM();
//High,low
if (been_min == 1)
{
if (one_min_avg > high) high = one_min_avg;
if (one_min_avg < low ) low = one_min_avg;
}
counter++;
if (counter % 2 == 0) clock();
if (counter >= 120) counter = 0;
}
return 0;
}