1
2#include <SoftwareSerial.h>
3
4#include <SPI.h>
5#include <SD.h>
6
7#include <DHT.h>
8
9
10Sd2Card card;
11SdVolume volume;
12
13
14SoftwareSerial SDS_Serial(8, 9);
15SoftwareSerial GPS_Serial(5, 6);
16
17
18const unsigned int id_lo = 802;
19const unsigned int id_hi = 32;
20
21const byte Select0 = 2;
22const byte Select1 = 3;
23const byte Select2 = 4;
24const byte Warning = 6;
25const byte Write2LogLED = 7;
26const byte DHT_pin = 9;
27const byte chipSelect = 10;
28const byte Data0 = 14;
29const byte Data1 = 15;
30const byte Data2 = 16;
31const byte Data3 = 17;
32const byte Latch_data = 18;
33const byte Power_hold = 19;
34const int NO2_pin = A6;
35const int Switch_status = A7;
36const byte Vmin = 43;
37static char line[40];
38char datafile[] = "log_000.txt";
39unsigned int filecount = 0;
40unsigned int lowpowercount = 0;
41unsigned int Pm25 = 0;
42unsigned int Pm10 = 0;
43uint8_t byteGPS;
44uint8_t display_mode = 0;
45uint32_t currentTime = 0;
46uint32_t hash = 0;
47char cmd[7] = "$GPRMC";
48int counter1 = 0;
49int counter2 = 0;
50int offsets[13];
51char buf[200] = " ";
52float hum;
53float temp;
54boolean proceed;
55boolean switch_off;
56boolean logging_flag;
57boolean hash_written;
58boolean data_present;
59DHT dht(DHT_pin, DHT22);
60char digit100(unsigned int v) {return '0' + v / 100 - (v/1000) * 10;}
61char digit10(unsigned int v) {return '0' + v / 10 - (v/100) * 10;}
62char digit1(unsigned int v) {return '0' + v / 1 - (v/10) * 10;}
63
64void ProcessSerialSDSData() {
65 uint8_t mData = 0;
66 uint8_t i = 0;
67 uint8_t mPkt[10] = {0};
68 uint8_t mCheck = 0;
69 while (SDS_Serial.available() > 0) {
70
71
72 mData = SDS_Serial.read();
73 delay(2);
74 if(mData == 0xAA) {
75 mPkt[0] = mData;
76 mData = SDS_Serial.read();
77 if(mData == 0xC0) {
78 mPkt[1] = mData;
79 mCheck = 0;
80 for(i=0;i<6;i++) {
81 mPkt[i+2] = SDS_Serial.read();
82 delay(2);
83 mCheck += mPkt[i+2];
84 }
85 mPkt[8] = SDS_Serial.read();
86 delay(1);
87 mPkt[9] = SDS_Serial.read();
88 if(mCheck == mPkt[8]) {
89 Pm25 = (uint16_t)mPkt[2] | (uint16_t)(mPkt[3]<<8);
90 Pm10 = (uint16_t)mPkt[4] | (uint16_t)(mPkt[5]<<8);
91
92 return;
93 }
94 }
95 }
96 Check_power_switch_status();
97 }
98}
99
100void reset() {
101 counter1 = 0;
102 counter2 = 0;
103}
104
105void write_nibble(byte regnbr, byte regval) {
106 byte i;
107 for (i=0; i<4; i++)
108 { if ((regval & (1 << i)) == 0) { digitalWrite(Data0 + i, LOW); } else { digitalWrite(Data0 + i, HIGH); } }
109 for (i=0; i<3; i++)
110 { if ((regnbr & (1 << i)) == 0) { digitalWrite(Select0 + i, LOW); } else { digitalWrite(Select0 + i, HIGH); } }
111 digitalWrite(Latch_data, HIGH);
112 delay(1);
113 digitalWrite(Latch_data, LOW);
114}
115
116void write_data(byte regnbr, int regval) {
117 if (regval > 999) {
118 write_nibble(regnbr, 9);
119 write_nibble(regnbr + 1, 9);
120 write_nibble(regnbr + 2, 9);
121 }
122 else {
123 byte val_units = regval % 10;
124 byte val_tens = (regval / 10) % 10;
125 byte val_hundreds = regval / 100;
126 write_nibble(regnbr, val_units);
127 if (val_tens == 0 && val_hundreds == 0) {
128 write_nibble(regnbr + 1, 15);
129 } else {
130 write_nibble(regnbr + 1, val_tens);
131 }
132 if (val_hundreds == 0) {
133 write_nibble(regnbr + 2, 15);
134 } else {
135 write_nibble(regnbr + 2, val_hundreds);
136 }
137 }
138}
139
140void error_idling(byte error_nbr) {
141 while(1) {
142 write_data(1, 888);
143 write_data(4, 888);
144 delay(1000);
145 write_data(1, error_nbr);
146 write_data(4, 0);
147 delay(1000);
148 Check_power_switch_status();
149 }
150}
151
152int get_size(int offset) {
153 return offsets[offset+1] - offsets[offset] - 1;
154}
155
156boolean handle_byte(int byteGPS) {
157 uint8_t checksum = 0;
158 char str_check[2];
159 uint8_t chksum[2];
160 buf[counter1] = byteGPS;
161 counter1++;
162 if (counter1 == 200) {
163 return false;
164 }
165 if (byteGPS == ',') {
166 counter2++;
167 offsets[counter2] = counter1;
168 if (counter2 == 13) {
169 return false;
170 }
171 }
172 if (byteGPS == '*') {
173 offsets[12] = counter1;
174 }
175
176 if (byteGPS == 10) {
177
178 if (counter2 != 12 || (get_size(0) != 6)) {
179 return false;
180 }
181
182 for (int j=0; j<6; j++) {
183 if (buf[j] != cmd[j]) {
184 return false;
185 }
186 }
187
188 for (int j=1; j<offsets[12]-1; j++) {
189 checksum ^= buf[j];
190 }
191 chksum[0] = buf[offsets[12]];
192 if (chksum[0] > 47 && chksum[0] < 58) {
193 chksum[0] -= 48;
194 } else {
195 chksum[0] -= 55;
196 }
197 chksum[1] = buf[offsets[12]+1];
198 if (chksum[1] > 47 && chksum[1] < 58) {
199 chksum[1] -= 48;
200 } else {
201 chksum[1] -= 55;
202 }
203 if (checksum != chksum[0]*16 + chksum[1]) {
204 return false;
205 }
206 proceed = true;
207 return false;
208 }
209 return true;
210}
211
212void calc_hash(char c) {
213 hash += (byte)c;
214 hash += (hash << 10);
215 hash ^= (hash >> 6);
216}
217
218void write_file_hash() {
219 typedef union {
220 uint32_t v;
221 unsigned char b[4];
222 } short4bytes_t;
223 if (SD.exists(datafile)) {
224 if (data_present) {
225 hash += (hash << 3);
226 hash ^= (hash >> 11);
227 hash += (hash << 15);
228 short4bytes_t s4b;
229 s4b.v = hash;
230 File dataFile = SD.open(datafile, FILE_WRITE);
231 if (dataFile) {
232 digitalWrite(Write2LogLED, HIGH);
233 sprintf(buf, "Signature: 0x%02X 0x%02X 0x%02X 0x%02X", s4b.b[3], s4b.b[2], s4b.b[1], s4b.b[0]);
234 dataFile.println(buf);
235 dataFile.close();
236 digitalWrite(Write2LogLED, LOW);
237 }
238 }
239 else {
240 SD.remove(datafile);
241 }
242 }
243}
244
245void Check_power_switch_status() {
246
247
248 uint16_t v = analogRead(Switch_status);
249 if (v < 100 && !switch_off) {
250 currentTime = millis();
251 switch_off = true;
252 delay(200);
253 }
254 if (v < 100 && switch_off && abs(millis() - currentTime) > 2000 && !hash_written) {
255 logging_flag = false;
256 write_file_hash();
257 hash_written = true;
258 delay(1000);
259 digitalWrite(Power_hold, LOW);
260 }
261 if (v > 100 && switch_off) {
262 switch_off = false;
263 ++display_mode %= 4;
264 delay(200);
265 write_data(1, display_mode);
266 for (byte i=4; i<7; i++) write_nibble(i, 15);
267 delay(1000);
268 }
269}
270
271byte hour_inc(int date_offset) {
272 byte year;
273 byte month;
274 byte day;
275 byte i;
276 char datebuf[3];
277 uint8_t days[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30 };
278 uint8_t hours[] = { 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1 };
279 datebuf[2] = '\\0';
280 for (i=0; i<2; i++) datebuf[i] = buf[date_offset+i+2];
281 month = atoi(datebuf) - 1;
282 if (month == 2 || month == 9) {
283 for (i=0; i<2; i++) datebuf[i] = buf[date_offset+i+4];
284 year = atoi(datebuf);
285 for (i=0; i<2; i++) datebuf[i] = buf[date_offset+i];
286 day = atoi(datebuf);
287
288 uint16_t nbr_days = (year - 18)*365;
289 for (i=0; i<=month; i++) nbr_days += days[i];
290
291 if (year > 20) nbr_days += (year - 21)/4 + 1;
292 if (year % 4 == 0) nbr_days++;
293
294 uint8_t last_sunday = 31;
295 while ((nbr_days + last_sunday) % 7 > 0) last_sunday--;
296
297 if (month == 2) { if (day < last_sunday) return 1; else return 2; }
298 if (month == 9) { if (day < last_sunday) return 2; else return 1; }
299 } else {
300 return hours[month];
301 }
302}
303
304void setup() {
305 pinMode(Data0, OUTPUT);
306 pinMode(Data1, OUTPUT);
307 pinMode(Data2, OUTPUT);
308 pinMode(Data3, OUTPUT);
309 pinMode(Select0, OUTPUT);
310 pinMode(Select1, OUTPUT);
311 pinMode(Select2, OUTPUT);
312 pinMode(Latch_data, OUTPUT);
313 pinMode(Power_hold, OUTPUT);
314 pinMode(Write2LogLED, OUTPUT);
315 digitalWrite(Warning, LOW);
316 digitalWrite(Power_hold, LOW);
317 digitalWrite(Latch_data, LOW);
318 analogReference(EXTERNAL);
319
320 SDS_Serial.begin(9600);
321
322 GPS_Serial.begin(9600);
323 Serial.begin(9600);
324 dht.begin();
325 delay(500);
326
327 write_data(1, id_lo);
328 write_data(4, id_hi);
329 strcpy_P(line,PSTR("Initializing SD card..."));
330 Serial.println(line);
331 if (!card.init(SPI_HALF_SPEED, chipSelect)) {
332 strcpy_P(line,PSTR("Initialization failed."));
333 Serial.println(line);
334 error_idling(1);
335 } else {
336 strcpy_P(line,PSTR("SD card is OK. "));
337 Serial.println(line);
338 }
339
340 strcpy_P(line,PSTR("Card type: "));
341 Serial.print(line);
342 switch (card.type()) {
343 case SD_CARD_TYPE_SD1:
344 Serial.println("SD1");
345 break;
346 case SD_CARD_TYPE_SD2:
347 Serial.println("SD2");
348 break;
349 case SD_CARD_TYPE_SDHC:
350 Serial.println("SDHC");
351 break;
352 default:
353 Serial.println("?");
354 }
355 delay(2000);
356
357 if (!volume.init(card)) {
358 strcpy_P(line,PSTR("No FAT partition found. Card formatted?"));
359 Serial.println(line);
360 error_idling(2);
361 }
362
363 uint32_t volumesize;
364 strcpy_P(line,PSTR("Volume type: FAT"));
365 Serial.print(line);
366 Serial.println(volume.fatType(), DEC);
367 delay(2000);
368 volumesize = volume.blocksPerCluster();
369 volumesize *= volume.clusterCount();
370 volumesize *= 512;
371 strcpy_P(line,PSTR("Volume size (kbytes): "));
372 Serial.print(line);
373 volumesize /= 1024;
374 Serial.println(volumesize);
375 delay(2000);
376 offsets[0] = 0;
377 proceed = false;
378 switch_off = false;
379 logging_flag = false;
380 hash_written = false;
381 data_present = false;
382
383 if (!SD.begin(chipSelect)) {
384 strcpy_P(line,PSTR("Initialization failed!"));
385 Serial.println(line);
386 error_idling(3);
387 }
388
389 do {
390 filecount++;
391 datafile[4] = digit100(filecount);
392 datafile[5] = digit10(filecount);
393 datafile[6] = digit1(filecount);
394 } while (SD.exists(datafile));
395 Serial.print("Logging to: ");
396 Serial.println(datafile);
397
398 digitalWrite(Power_hold, HIGH);
399 File dataFile = SD.open(datafile, FILE_WRITE);
400
401 if (dataFile) {
402 digitalWrite(Write2LogLED, HIGH);
403 sprintf(buf, "ID: %03d%03d", id_hi, id_lo);
404 dataFile.println(buf);
405 dataFile.close();
406 for (byte i=0; i<strlen(buf); i++) calc_hash(buf[i]);
407 calc_hash('\r'); calc_hash('\n');
408 delay(100);
409 digitalWrite(Write2LogLED, LOW);
410 }
411
412 else {
413 strcpy_P(line,PSTR("Cannot open the logfile!"));
414 error_idling(6);
415 }
416
417 write_data(1, Pm25);
418 write_data(4, Pm10);
419}
420
421void loop() {
422 SDS_Serial.listen();
423 while (SDS_Serial.available() == 0) {
424 Check_power_switch_status();
425 }
426
427 ProcessSerialSDSData();
428 if (display_mode == 0) {
429
430 write_data(1, int(Pm25/10+0.5));
431 write_data(4, int(Pm10/10+0.5));
432 }
433
434 if (Pm10 > 2000) { digitalWrite(Warning, HIGH); } else { digitalWrite(Warning, LOW); }
435
436 String dataString1 = "PM2.5: " + String(Pm25/10) + ", PM10: " + String(Pm10/10);
437 Serial.println(dataString1);
438
439 temp = dht.readTemperature();
440 hum = dht.readHumidity();
441 if (display_mode == 2) {
442
443 write_data(1, int(temp+0.5));
444 write_data(4, int(hum+0.5));
445 }
446
447 String dataString2 = "Temp: " + String(temp) + ", Humid: " + String(hum);
448 Serial.println(dataString2);
449 GPS_Serial.listen();
450 while (GPS_Serial.available() == 0) {
451 Check_power_switch_status();
452 }
453
454 while (!proceed) {
455 if (GPS_Serial.available() > 0) {
456 byteGPS=GPS_Serial.read();
457 if (!handle_byte(byteGPS)) {
458 reset();
459 }
460 }
461 delay(5);
462 Check_power_switch_status();
463 }
464 for (int i=0; i<offsets[12]+2; i++) {
465 Serial.print(buf[i]);
466 }
467 Serial.println(buf[offsets[12]+2]);
468 if (display_mode == 3) {
469 if (buf[offsets[2]] == 'V') {
470 write_data(1, 88);
471 write_data(4, 88);
472 delay(500);
473 write_data(1, 11);
474 write_data(4, 11);
475 } else {
476
477 char timebuf[3];
478 timebuf[2] = '\\0';
479 for (int i=0; i<2; i++) timebuf[i] = buf[offsets[1]+i];
480 write_data(4, atoi(timebuf) + hour_inc(offsets[9]));
481 for (int i=0; i<2; i++) timebuf[i] = buf[offsets[1]+i+2];
482 write_data(1, atoi(timebuf));
483 }
484 }
485 uint16_t v = analogRead(Switch_status);
486 uint8_t Vin = int(v/15.5+0.5);
487 Serial.println("Vin: " + String(Vin));
488 v = analogRead(NO2_pin);
489 float Vout = v/207.5;
490 Serial.println("Vout: " + String(Vout));
491 float RlRs = Vout/(Vin/10-Vout);
492 float ppmNO2 = pow(10, 0.9682*log(RlRs)/log(10)-0.8108);
493 Serial.println("ppm NO2: " + String(ppmNO2));
494 float mgNO2 = (560.5/(273.15+temp))*ppmNO2;
495 if (display_mode == 1) {
496 write_data(1, int(1000*mgNO2+0.5));
497 write_data(4, Vin);
498 }
499 dataString1 += ", NO2: " + String(1000*mgNO2);
500
501 if (logging_flag && !switch_off && (Vin < Vmin || buf[offsets[2]] == 'V')) {
502
503 write_data(1, 888);
504 write_data(4, 888);
505 delay(1000);
506 write_data(4, 0);
507 if (Vin < Vmin) {
508 write_data(1, 4);
509 lowpowercount++;
510 if (lowpowercount > 5) {
511 logging_flag = false;
512 write_file_hash();
513 delay(1000);
514 digitalWrite(Power_hold, LOW);
515 }
516 }
517 if (buf[offsets[2]] == 'V') {
518 logging_flag = false;
519 write_data(1, 5);
520 }
521 delay(1000);
522 }
523
524 if (!logging_flag && Vin > Vmin && buf[offsets[2]] == 'A') {
525 logging_flag = true;
526 delay(1000);
527 }
528 Check_power_switch_status();
529 if (logging_flag && !hash_written) {
530 File dataFile = SD.open(datafile, FILE_WRITE);
531
532 if (dataFile) {
533 digitalWrite(Write2LogLED, HIGH);
534 dataFile.println(dataString1);
535 for (int i=0; i<dataString1.length(); i++) calc_hash(dataString1.charAt(i));
536 calc_hash('\r'); calc_hash('\n');
537 dataFile.println(dataString2);
538 for (int i=0; i<dataString2.length(); i++) calc_hash(dataString2.charAt(i));
539 calc_hash('\r'); calc_hash('\n');
540 for (int i=0; i<offsets[12]-1; i++) {
541 dataFile.print(buf[i]);
542 calc_hash(buf[i]);
543 }
544 dataFile.println(buf[offsets[12]-1]);
545 calc_hash(buf[offsets[12]-1]);
546 calc_hash('\r'); calc_hash('\n');
547 dataFile.close();
548 data_present = true;
549 delay(100);
550 digitalWrite(Write2LogLED, LOW);
551 }
552
553 else {
554 strcpy_P(line,PSTR("Cannot open the logfile!"));
555 Serial.println(line);
556
557 write_data(1, 888);
558 write_data(4, 888);
559 delay(1000);
560 write_data(1, 6);
561 write_data(4, 0);
562 delay(1000);
563 }
564 }
565
566 for (int i=0; i<200; i++) {
567 buf[i] = ' ';
568 }
569 proceed = false;
570}
571
572