Lluis Nadal
/
Alcoholmeter
Alcoholmeter with MQ3 sensor
main.cpp@0:05d4673b2832, 2011-04-09 (annotated)
- Committer:
- lnadal
- Date:
- Sat Apr 09 14:04:34 2011 +0000
- Revision:
- 0:05d4673b2832
- Child:
- 1:5a58f03abfe9
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
lnadal | 0:05d4673b2832 | 1 | /* Alcoholmeter |
lnadal | 0:05d4673b2832 | 2 | |
lnadal | 0:05d4673b2832 | 3 | Range: 0-3000 ppm. |
lnadal | 0:05d4673b2832 | 4 | |
lnadal | 0:05d4673b2832 | 5 | ************************************************************************************************************************* |
lnadal | 0:05d4673b2832 | 6 | |
lnadal | 0:05d4673b2832 | 7 | This project was done in 2010-11 school year in Molins de Rei (Barcelona, Spain) in Lluis de Requesens Secondari School. |
lnadal | 0:05d4673b2832 | 8 | |
lnadal | 0:05d4673b2832 | 9 | Tacher: Lluis Nadal. |
lnadal | 0:05d4673b2832 | 10 | Students: Agnes Garriga, Manel Tuells. |
lnadal | 0:05d4673b2832 | 11 | |
lnadal | 0:05d4673b2832 | 12 | ************************************************************************************************************************** |
lnadal | 0:05d4673b2832 | 13 | |
lnadal | 0:05d4673b2832 | 14 | Sensor: MQ-3 alcohol sensor.(http://www.sparkfun.com/; http://www.bricogeek.com/shop/). |
lnadal | 0:05d4673b2832 | 15 | |
lnadal | 0:05d4673b2832 | 16 | MQ-3 sensor wiring: |
lnadal | 0:05d4673b2832 | 17 | H1(5V), H2(ground), A(5V), B (R load = 500 Ohm - 1%), R load (mBed pin 20 - ground). |
lnadal | 0:05d4673b2832 | 18 | |
lnadal | 0:05d4673b2832 | 19 | Analog out (for datalogger or multimeter): mBed pin 18 (1V = 1000 ppm). |
lnadal | 0:05d4673b2832 | 20 | |
lnadal | 0:05d4673b2832 | 21 | |
lnadal | 0:05d4673b2832 | 22 | |
lnadal | 0:05d4673b2832 | 23 | MQ-3 sensor response is not linear. Calibration was done by passing air (with a fishtank air pump) through a dilute solution of ethanol in water. |
lnadal | 0:05d4673b2832 | 24 | The resulting air saturated of ethanol vapor and water vapor, was continuously introduced in an open container containing the sensor. Th voltage across a load resistor was measured. |
lnadal | 0:05d4673b2832 | 25 | |
lnadal | 0:05d4673b2832 | 26 | A 500 Ohm-1% load resistor was selected in order to achieve a measuring range of 3000 ppm. |
lnadal | 0:05d4673b2832 | 27 | |
lnadal | 0:05d4673b2832 | 28 | After a bit difficult calculations, the sensor response was aproximated in a five degree polynomial: |
lnadal | 0:05d4673b2832 | 29 | |
lnadal | 0:05d4673b2832 | 30 | X = V(in) |
lnadal | 0:05d4673b2832 | 31 | ppm = 2.71494E+02*X - 3.10999E+02*X^2 + 6.85051E+02*X^3 - 3.47587E+02*X^4 + 7.47499E+01*X^5 |
lnadal | 0:05d4673b2832 | 32 | |
lnadal | 0:05d4673b2832 | 33 | |
lnadal | 0:05d4673b2832 | 34 | For more accurate readins is recommended a minimum heating time of 5 minutes (24 hours is recommended for the manufacturer) but 1 minute give satisfactory results. |
lnadal | 0:05d4673b2832 | 35 | After heating, the sensor is autozeroed by averaging 5 measures. |
lnadal | 0:05d4673b2832 | 36 | |
lnadal | 0:05d4673b2832 | 37 | In heating and in autozero operations the sensor must be left away of alcohol vapors! |
lnadal | 0:05d4673b2832 | 38 | |
lnadal | 0:05d4673b2832 | 39 | |
lnadal | 0:05d4673b2832 | 40 | THIS FREE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. ENJOY IT. |
lnadal | 0:05d4673b2832 | 41 | |
lnadal | 0:05d4673b2832 | 42 | */ |
lnadal | 0:05d4673b2832 | 43 | //********************************************************************************************** |
lnadal | 0:05d4673b2832 | 44 | |
lnadal | 0:05d4673b2832 | 45 | |
lnadal | 0:05d4673b2832 | 46 | #include "mbed.h" |
lnadal | 0:05d4673b2832 | 47 | #include "TextLCD.h" |
lnadal | 0:05d4673b2832 | 48 | #include <sstream> |
lnadal | 0:05d4673b2832 | 49 | using namespace std; |
lnadal | 0:05d4673b2832 | 50 | |
lnadal | 0:05d4673b2832 | 51 | |
lnadal | 0:05d4673b2832 | 52 | |
lnadal | 0:05d4673b2832 | 53 | |
lnadal | 0:05d4673b2832 | 54 | float concentration (float x){ |
lnadal | 0:05d4673b2832 | 55 | |
lnadal | 0:05d4673b2832 | 56 | const float A[] = { 2.71494E+02, -3.10999E+02, 6.85051E+02, -3.47587E+02, 7.47499E+01}; |
lnadal | 0:05d4673b2832 | 57 | float result; |
lnadal | 0:05d4673b2832 | 58 | float B[4]; |
lnadal | 0:05d4673b2832 | 59 | B[0] = x*x; |
lnadal | 0:05d4673b2832 | 60 | B[1] = B[0]*x; |
lnadal | 0:05d4673b2832 | 61 | B[2] = B[1]*x; |
lnadal | 0:05d4673b2832 | 62 | B[3] = B[2]*x; |
lnadal | 0:05d4673b2832 | 63 | |
lnadal | 0:05d4673b2832 | 64 | result = A[0]*x+A[1]*B[0]+A[2]*B[1]+A[3]*B[2]+A[4]*B[3]; |
lnadal | 0:05d4673b2832 | 65 | return result; |
lnadal | 0:05d4673b2832 | 66 | } |
lnadal | 0:05d4673b2832 | 67 | |
lnadal | 0:05d4673b2832 | 68 | |
lnadal | 0:05d4673b2832 | 69 | |
lnadal | 0:05d4673b2832 | 70 | |
lnadal | 0:05d4673b2832 | 71 | char messages[5][16] = {"Heating sensor", "64 seconds", "Autozero", "Ready!", "ppm"}; |
lnadal | 0:05d4673b2832 | 72 | |
lnadal | 0:05d4673b2832 | 73 | |
lnadal | 0:05d4673b2832 | 74 | int L; |
lnadal | 0:05d4673b2832 | 75 | float x; |
lnadal | 0:05d4673b2832 | 76 | float x0[5]; |
lnadal | 0:05d4673b2832 | 77 | float x_initial; |
lnadal | 0:05d4673b2832 | 78 | |
lnadal | 0:05d4673b2832 | 79 | TextLCD lcd(p24, p26, p27, p28, p29, p30); // Wiring microcontroller - LCD: |
lnadal | 0:05d4673b2832 | 80 | // rs(4), e(6), d4(11), d5(12), d6(13), d7(14) (R/W(5) a 0) |
lnadal | 0:05d4673b2832 | 81 | |
lnadal | 0:05d4673b2832 | 82 | DigitalIn pushbutton = p8; // Pushbutton (normally open). Stops initial heating time when it is pressed. . |
lnadal | 0:05d4673b2832 | 83 | AnalogOut output(p18); // Analog output to a multimeter or datalogger (1V = 1000 ppm). |
lnadal | 0:05d4673b2832 | 84 | |
lnadal | 0:05d4673b2832 | 85 | |
lnadal | 0:05d4673b2832 | 86 | |
lnadal | 0:05d4673b2832 | 87 | void text_screen( char message[], int colum, int row) { |
lnadal | 0:05d4673b2832 | 88 | |
lnadal | 0:05d4673b2832 | 89 | lcd.locate( colum, row); |
lnadal | 0:05d4673b2832 | 90 | lcd.printf(message); |
lnadal | 0:05d4673b2832 | 91 | |
lnadal | 0:05d4673b2832 | 92 | } |
lnadal | 0:05d4673b2832 | 93 | |
lnadal | 0:05d4673b2832 | 94 | |
lnadal | 0:05d4673b2832 | 95 | |
lnadal | 0:05d4673b2832 | 96 | int main() { |
lnadal | 0:05d4673b2832 | 97 | |
lnadal | 0:05d4673b2832 | 98 | output = 0.0; |
lnadal | 0:05d4673b2832 | 99 | lcd.cls(); |
lnadal | 0:05d4673b2832 | 100 | |
lnadal | 0:05d4673b2832 | 101 | text_screen(messages[0], 0, 0); |
lnadal | 0:05d4673b2832 | 102 | text_screen(messages[1], 0, 1); |
lnadal | 0:05d4673b2832 | 103 | wait(2); |
lnadal | 0:05d4673b2832 | 104 | |
lnadal | 0:05d4673b2832 | 105 | |
lnadal | 0:05d4673b2832 | 106 | |
lnadal | 0:05d4673b2832 | 107 | for (int j = 0; j<4; j++){ |
lnadal | 0:05d4673b2832 | 108 | |
lnadal | 0:05d4673b2832 | 109 | lcd.cls(); // Heating sensor 4x16 = 64 seconds |
lnadal | 0:05d4673b2832 | 110 | text_screen(messages[0], 0, 0); |
lnadal | 0:05d4673b2832 | 111 | |
lnadal | 0:05d4673b2832 | 112 | |
lnadal | 0:05d4673b2832 | 113 | for (int i = 0; i<16; i++){ |
lnadal | 0:05d4673b2832 | 114 | if (pushbutton == 1){ // Pressing pushbutton stops initial heating and enters in measuring mode. |
lnadal | 0:05d4673b2832 | 115 | break; |
lnadal | 0:05d4673b2832 | 116 | } |
lnadal | 0:05d4673b2832 | 117 | lcd.locate(i, 1); |
lnadal | 0:05d4673b2832 | 118 | lcd.putc(62); |
lnadal | 0:05d4673b2832 | 119 | wait(1); |
lnadal | 0:05d4673b2832 | 120 | } |
lnadal | 0:05d4673b2832 | 121 | } |
lnadal | 0:05d4673b2832 | 122 | |
lnadal | 0:05d4673b2832 | 123 | lcd.cls(); |
lnadal | 0:05d4673b2832 | 124 | text_screen(messages[2], 0, 0); |
lnadal | 0:05d4673b2832 | 125 | wait(2); |
lnadal | 0:05d4673b2832 | 126 | |
lnadal | 0:05d4673b2832 | 127 | |
lnadal | 0:05d4673b2832 | 128 | AnalogIn value(p20); // Reads sensor voltage as a float int the interval (0-1) corresponding to (0 - 3.3V). |
lnadal | 0:05d4673b2832 | 129 | |
lnadal | 0:05d4673b2832 | 130 | |
lnadal | 0:05d4673b2832 | 131 | |
lnadal | 0:05d4673b2832 | 132 | for (int i=0; i<5; i++){ |
lnadal | 0:05d4673b2832 | 133 | |
lnadal | 0:05d4673b2832 | 134 | x0[i] = value; |
lnadal | 0:05d4673b2832 | 135 | wait(2); |
lnadal | 0:05d4673b2832 | 136 | |
lnadal | 0:05d4673b2832 | 137 | } |
lnadal | 0:05d4673b2832 | 138 | x_initial = (x0[0]+x0[1]+x0[2]+x0[3]+x0[4])/5.0; // Autozero. Average of 5 initial measures. |
lnadal | 0:05d4673b2832 | 139 | |
lnadal | 0:05d4673b2832 | 140 | lcd.cls(); |
lnadal | 0:05d4673b2832 | 141 | text_screen(messages[3], 0, 0); |
lnadal | 0:05d4673b2832 | 142 | wait(2); |
lnadal | 0:05d4673b2832 | 143 | |
lnadal | 0:05d4673b2832 | 144 | while(1) { |
lnadal | 0:05d4673b2832 | 145 | x = value; |
lnadal | 0:05d4673b2832 | 146 | x = (x-x_initial)*3.3; // Calculate real voltage. |
lnadal | 0:05d4673b2832 | 147 | |
lnadal | 0:05d4673b2832 | 148 | if(x<0) x = 0; |
lnadal | 0:05d4673b2832 | 149 | |
lnadal | 0:05d4673b2832 | 150 | x = concentration(x); |
lnadal | 0:05d4673b2832 | 151 | |
lnadal | 0:05d4673b2832 | 152 | output = x/3000; // If analog readings do not match digital readings you can adjust this value (1000 ppm = 1V). |
lnadal | 0:05d4673b2832 | 153 | |
lnadal | 0:05d4673b2832 | 154 | wait(0.1); |
lnadal | 0:05d4673b2832 | 155 | |
lnadal | 0:05d4673b2832 | 156 | // Float to string conversion. |
lnadal | 0:05d4673b2832 | 157 | stringstream ss (stringstream::in | stringstream::out); |
lnadal | 0:05d4673b2832 | 158 | ss <<x; |
lnadal | 0:05d4673b2832 | 159 | string reading = ss.str(); |
lnadal | 0:05d4673b2832 | 160 | L = reading.length(); |
lnadal | 0:05d4673b2832 | 161 | |
lnadal | 0:05d4673b2832 | 162 | |
lnadal | 0:05d4673b2832 | 163 | lcd.cls(); |
lnadal | 0:05d4673b2832 | 164 | for (int i=0; i<L; i+=1){ |
lnadal | 0:05d4673b2832 | 165 | lcd.locate(i,0); |
lnadal | 0:05d4673b2832 | 166 | lcd.putc(reading[i]); |
lnadal | 0:05d4673b2832 | 167 | |
lnadal | 0:05d4673b2832 | 168 | |
lnadal | 0:05d4673b2832 | 169 | |
lnadal | 0:05d4673b2832 | 170 | text_screen(messages[4], 10, 0); |
lnadal | 0:05d4673b2832 | 171 | } |
lnadal | 0:05d4673b2832 | 172 | wait(2); |
lnadal | 0:05d4673b2832 | 173 | } |
lnadal | 0:05d4673b2832 | 174 | } |