Devices & Components
Arduino Nano
Resistor 2.21k ohm
7-segment single digit
Resistor 1k ohm
Capacitor 100 nF
74LS138 3-to-8 line decoder TTL IC
Dual Pole Dual Throw (DPDT) Switch
555 Timers
Open Smart GPS-module
Resistor 33k ohm
General Purpose Transistor NPN
SDcard reader VMA304
NO2-sensor CJMCU-4541
General Purpose Transistor PNP
Single Turn Potentiometer- 100k ohms
Resistor 330 ohm
Capacitor 100 µF
3 mm LED: Red
DHT22 Temperature Sensor
Resistor 560 ohm
Fine dust sensor SDS011
74HC4511 BCD to Seven-Segment Decoder / Driver / Latch
Software & Tools
Google Maps
Project description
Code
Mobile fine dust and NO2 meter
arduino
This is the code for the mobile fine dust meter with the optional display and NO2 meter, but this code also works for the basic version without display and the NO2 meter. Mandatory modules are the SDS011 fine dust sensor, the DHT22 sensor for measuring humidity and temperature and the GPS-module, because these modules transmit their measurements over a serial interface and for each of these sensors a wait-loop is traversed to capture new serial data. Also mandatory is the SDcard interface (SPI) which takes care of the logging of the gathered data on a SDcard. Further, this code features the following functions: 1. Creation of a new logfile each time the meter is powered on. 2. Calculation of a hash code when writing a new data record to the logfile. 3. Creation of a digital signature using the hash code at power shutdown or too low supply voltage. 4. Monitoring of the supply voltage and giving a low battery warning on the display 5. Software controlled power down to finish ongoing write cycle to the SDcard and writing the signature. 6. Round-robin rotation through 4 display modes by toggling the DPDT power switch. 7. Showing 6 different error codes on the display: 001 SDcard initialization failed 002 No FAT partition found 003 Initialization file access failed 004 Low battery voltage 005 GPS signal unreliable 006 Cannot open the logfile
1// include the software serial interface library 2#include <SoftwareSerial.h> 3// include the SDcard library 4#include <SPI.h> 5#include <SD.h> 6// include the Digital Humidity and Temperature (DHT) library 7#include <DHT.h> 8 9// set up variables using the SDcard utility library functions 10Sd2Card card; 11SdVolume volume; 12 13// set up variables using the softserial utility library functions 14SoftwareSerial SDS_Serial(8, 9); // RX, TX (TX unused and assigned as RX-pin for DHT-sensor) 15SoftwareSerial GPS_Serial(5, 6); // RX, TX (TX unused and assigned as output pin for warning signal) 16 17// unique identification code of the finedustmeter (no leading zeroes allowed!) 18const unsigned int id_lo = 802; // serial number (unique in each country) 19const unsigned int id_hi = 32; // country code 20 21const byte Select0 = 2; // Display select pin for bit0 22const byte Select1 = 3; // Display select pin for bit1 23const byte Select2 = 4; // Display select pin for bit2 24const byte Warning = 6; // Output pin to initiate warning signal 25const byte Write2LogLED = 7; // Notification that data is written to logfile 26const byte DHT_pin = 9; // Input pin to receive serial DHT-data 27const byte chipSelect = 10; // Select pin to enable access to the SDcard 28const byte Data0 = 14; // Display data pin for bit0 29const byte Data1 = 15; // Display data pin for bit1 30const byte Data2 = 16; // Display data pin for bit2 31const byte Data3 = 17; // Display data pin for bit3 32const byte Latch_data = 18; // Clock line to latch data into selected register 33const byte Power_hold = 19; // Output pin to switch off meter outside SDcard write cycle 34const int NO2_pin = A6; // Analog PIN 6 to read the NO2-sensor 35const int Switch_status = A7;// Analog PIN 7 to sense status of on-off switch and supply voltage 36const byte Vmin = 43; // Minimum supply voltage for writing data to SDcard 37static char line[40]; // Linebuffer for serial monitor 38char datafile[] = "log_000.txt"; // Initial name of datalogfile 39unsigned int filecount = 0; 40unsigned int lowpowercount = 0; 41unsigned int Pm25 = 0; 42unsigned int Pm10 = 0; 43uint8_t byteGPS; 44uint8_t display_mode = 0; // 0->display PM-values, 1->display NO2 and voltage, 2->display temperature and humidity, 3->display time 45uint32_t currentTime = 0; 46uint32_t hash = 0; 47char cmd[7] = "$GPRMC"; // Recommended minimum specific GPS/Transit data sentence 48int counter1 = 0; // counts how many bytes were received (max 200) for each sentence 49int counter2 = 0; // counts how many commas were seen in each sentence 50int offsets[13]; // holds offset to 12 different data items and checksum in GPRMC sentence 51char buf[200] = " "; // data buffer in which each received sentence is stored 52float hum; // stores humidity value 53float temp; // stores temperature value 54boolean proceed; 55boolean switch_off; 56boolean logging_flag; 57boolean hash_written; 58boolean data_present; 59DHT dht(DHT_pin, DHT22); // Initialize DHT-sensor 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 // from www.inovafitness.com 71 // packet format: AA C0 PM25_Low PM25_High PM10_Low PM10_High 0 0 CRC AB 72 mData = SDS_Serial.read(); 73 delay(2); //wait until packet is received 74 if(mData == 0xAA) { //first headerbyte ok 75 mPkt[0] = mData; 76 mData = SDS_Serial.read(); 77 if(mData == 0xC0) { //second headerbyte ok 78 mPkt[1] = mData; 79 mCheck = 0; 80 for(i=0;i<6;i++) { //data reception and crc calculation 81 mPkt[i+2] = SDS_Serial.read(); 82 delay(2); 83 mCheck += mPkt[i+2]; 84 } 85 mPkt[8] = SDS_Serial.read(); //get crc 86 delay(1); 87 mPkt[9] = SDS_Serial.read(); 88 if(mCheck == mPkt[8]) { //crc ok? 89 Pm25 = (uint16_t)mPkt[2] | (uint16_t)(mPkt[3]<<8); 90 Pm10 = (uint16_t)mPkt[4] | (uint16_t)(mPkt[5]<<8); 91 //one good packet received 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 // Check if we got a <LF>, which indicates the end of line 176 if (byteGPS == 10) { 177 // Check that we got 12 pieces, and that the first piece is 6 characters 178 if (counter2 != 12 || (get_size(0) != 6)) { 179 return false; 180 } 181 // Check that we received $GPRMC 182 for (int j=0; j<6; j++) { 183 if (buf[j] != cmd[j]) { 184 return false; 185 } 186 } 187 // Compute and validate checksum 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; // one good sentence received 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 // when in off state more than 1 second, disable logging, write hash to SDcard and shutdown 247 // when toggled to on state within 1 second, switch to next display mode 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 }; // nbr of days to be added from prev month at beginning of each month 278 uint8_t hours[] = { 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, 1 }; // nbr of hours to be added to GMT for daylight saving time in GMT+1 timezone 279 datebuf[2] = '\\0'; 280 for (i=0; i<2; i++) datebuf[i] = buf[date_offset+i+2]; 281 month = atoi(datebuf) - 1; // decrement month value from GPRMC sentence, since days and hours arrays range from 0 to 11 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 // calculate number of days at 1/3 or 1/10 since monday, 1 january 2018 288 uint16_t nbr_days = (year - 18)*365; 289 for (i=0; i<=month; i++) nbr_days += days[i]; 290 // correction for leap years 291 if (year > 20) nbr_days += (year - 21)/4 + 1; 292 if (year % 4 == 0) nbr_days++; 293 // find date of last sunday in march or october 294 uint8_t last_sunday = 31; 295 while ((nbr_days + last_sunday) % 7 > 0) last_sunday--; 296 // determine changeover from wintertime to summertime and vice versa 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); // Aref must be connected to 3.3V pin 319 // start serial connection to SDS-module 320 SDS_Serial.begin(9600); 321 // start serial connection to GPS-module 322 GPS_Serial.begin(9600); 323 Serial.begin(9600); 324 dht.begin(); 325 delay(500); // wait for display to boot up 326 // put unique identification code on display 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 // print the type of the SDcard 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); // allow some time (2 seconds) to read init result 356 // try to open the 'volume'/'partition' - it should be FAT16 or FAT32 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 // print the type and size of the first FAT-type volume 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); // allow some time (2 seconds) to read opening result 368 volumesize = volume.blocksPerCluster(); // clusters are collections of blocks 369 volumesize *= volume.clusterCount(); // we'll have a lot of clusters 370 volumesize *= 512; // SDcard blocks are always 512 bytes 371 strcpy_P(line,PSTR("Volume size (kbytes): ")); 372 Serial.print(line); 373 volumesize /= 1024; 374 Serial.println(volumesize); 375 delay(2000); // allow some time (2 seconds) to read volume size result 376 offsets[0] = 0; 377 proceed = false; 378 switch_off = false; 379 logging_flag = false; 380 hash_written = false; 381 data_present = false; 382 // initialise SD-card for file access 383 if (!SD.begin(chipSelect)) { 384 strcpy_P(line,PSTR("Initialization failed!")); 385 Serial.println(line); 386 error_idling(3); 387 } 388 // find the next log_(n).txt file which doesn't exist yet 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 // disable power off switching to prevent damage to SDcard during write cycles 398 digitalWrite(Power_hold, HIGH); 399 File dataFile = SD.open(datafile, FILE_WRITE); 400 // if the file is available, write the identification code as first line 401 if (dataFile) { 402 digitalWrite(Write2LogLED, HIGH); 403 sprintf(buf, "ID: %03d%03d", id_hi, id_lo); 404 dataFile.println(buf); // save identification code 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 // if the file isn't open, pop up an error 412 else { 413 strcpy_P(line,PSTR("Cannot open the logfile!")); 414 error_idling(6); 415 } 416 // display initial PM-values 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(); // wait for PM-data to arrive 425 } 426 // retrieve PM-data from the SDS011-module 427 ProcessSerialSDSData(); 428 if (display_mode == 0) { 429 // write the PM-data to the optional 7-segment display 430 write_data(1, int(Pm25/10+0.5)); 431 write_data(4, int(Pm10/10+0.5)); 432 } 433 // initiate warning signal when PM10-value is too high 434 if (Pm10 > 2000) { digitalWrite(Warning, HIGH); } else { digitalWrite(Warning, LOW); } 435 // make a string for assembling the data to the logfile 436 String dataString1 = "PM2.5: " + String(Pm25/10) + ", PM10: " + String(Pm10/10); 437 Serial.println(dataString1); 438 // retrieve temperature and humidity from DHT-sensor 439 temp = dht.readTemperature(); 440 hum = dht.readHumidity(); 441 if (display_mode == 2) { 442 // write temperature and humidity values to the optional 7-segment display 443 write_data(1, int(temp+0.5)); 444 write_data(4, int(hum+0.5)); 445 } 446 // make a string for assembling the data to the logfile 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(); // wait for GPS-data to arrive 452 } 453 // retrieve GPS-data 454 while (!proceed) { 455 if (GPS_Serial.available() > 0) { 456 byteGPS=GPS_Serial.read(); // Read a byte of the serial port of GPS-module 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 // write the time (hour:min) to the optional 7-segment display 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); // result is Vin in 0.1V steps (voltage divider on board: 5V->2.5V and 10bit ADC for Vref=3.3V -> 1023, hence 2.5V -> 775 and 775/15.5 = 50) 487 Serial.println("Vin: " + String(Vin)); 488 v = analogRead(NO2_pin); // take reading of ADC value from NOX-pin of NO2-sensor 489 float Vout = v/207.5; // convert ADC value to voltage (voltage divider on board: 5V->3.2V and 10bit ADC for Vref=3.3V -> 1023, hence 3.2V -> 992 and 992/198.4 = 5, 490 Serial.println("Vout: " + String(Vout)); // but because of resistor tolerance of 10% the value 207,5 is taken experimentally by measuring real output voltage on NOX-pin) 491 float RlRs = Vout/(Vin/10-Vout); // find load resistance over sensor resistance proportion from Vout, using Vin as supply voltage 492 float ppmNO2 = pow(10, 0.9682*log(RlRs)/log(10)-0.8108); // convert RsR0 to ppm concentration NO2 (refer to http://myscope.net/auswertung-der-airpi-gas-sensoren/) 493 Serial.println("ppm NO2: " + String(ppmNO2)); 494 float mgNO2 = (560.5/(273.15+temp))*ppmNO2; // convert ppm concentration to mg NO2/m taking air temperature into account and assuming 1013 mbar atmospheric pressure 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 // stop writing to SD card when Vin is below Vmin Volt or GPS-data not available 501 if (logging_flag && !switch_off && (Vin < Vmin || buf[offsets[2]] == 'V')) { 502 // display error number 4 or 5 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 // start writing to SD card when Vin is above Vmin Volt and GPS-data is available 524 if (!logging_flag && Vin > Vmin && buf[offsets[2]] == 'A') { 525 logging_flag = true; 526 delay(1000); // allow some time (1 second) 527 } 528 Check_power_switch_status(); 529 if (logging_flag && !hash_written) { 530 File dataFile = SD.open(datafile, FILE_WRITE); 531 // if the file is available, write to it 532 if (dataFile) { 533 digitalWrite(Write2LogLED, HIGH); 534 dataFile.println(dataString1); // save PM-data & NO2-data 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); // save DHT-data 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]); // save GPS-data 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 // if the file isn't open, pop up an error 553 else { 554 strcpy_P(line,PSTR("Cannot open the logfile!")); 555 Serial.println(line); 556 // display error number 6 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 // reset the GPS-data buffer 566 for (int i=0; i<200; i++) { 567 buf[i] = ' '; 568 } 569 proceed = false; 570} 571 572
Downloadable files
Schematics of the main interconnection board
This board interconnects all components: Arduino Nano, OpenSmart GPS-module and SDcard reader are plugged directly onto the board. The sensors SDS011, DHT22 and GJMCU-4541 are connected via cables as well as the optional display board and the earphone connector for the external warning device. The board houses also the soft power off electronics, a timer to provide a 30 seconds preheating of the NO2-sensor and a LED indicating write cycles to the SDcard. Battery power and DPDT-switch are also connected to the main board.
Schematics of the main interconnection board
Schematics of the main interconnection board
This board interconnects all components: Arduino Nano, OpenSmart GPS-module and SDcard reader are plugged directly onto the board. The sensors SDS011, DHT22 and GJMCU-4541 are connected via cables as well as the optional display board and the earphone connector for the external warning device. The board houses also the soft power off electronics, a timer to provide a 30 seconds preheating of the NO2-sensor and a LED indicating write cycles to the SDcard. Battery power and DPDT-switch are also connected to the main board.
Schematics of the main interconnection board
Display Board
This is the optional display board, which comprises 2x3 7-segment digits to show the measurement results of the 5 sensor values in real time.
Display Board
Display Board
This is the optional display board, which comprises 2x3 7-segment digits to show the measurement results of the 5 sensor values in real time.
Display Board
Documentation
Mechanical drawing of main interconnection PCB
A printed circuit board has been developed on which all components can be soldered or connected by means of flat cables. The Arduino Nano microcontroller, SDcard, GPS-module and electronics to ensure soft power down are soldered onto this board. DHT-, NO2- and PM-sensors are connected to this board via flat cables.
Mechanical drawing of main interconnection PCB
Transparant housing for mobile finedust meter
This is the housing in which all components will fit perfectly, including the display board and battery.
Transparant housing for mobile finedust meter

Mechanical drawing of main interconnection PCB
A printed circuit board has been developed on which all components can be soldered or connected by means of flat cables. The Arduino Nano microcontroller, SDcard, GPS-module and electronics to ensure soft power down are soldered onto this board. DHT-, NO2- and PM-sensors are connected to this board via flat cables.
Mechanical drawing of main interconnection PCB
Transparant housing for mobile finedust meter
This is the housing in which all components will fit perfectly, including the display board and battery.
Transparant housing for mobile finedust meter

Comments
Only logged in users can leave comments