Hackster is hosting Hackster Holidays, Ep. 6: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Monday!Stream Hackster Holidays, Ep. 6 on Monday!
Ninja
Created February 26, 2017

Air4Kids

Air4KIds device is add-on for child cribs and strollers. It monitors and purifies the air and informs parents about sources of pollution

278
Air4Kids

Things used in this project

Hardware components

Arduino 101
Arduino 101
×1
Grove starter kit plus for Intel Edison
Seeed Studio Grove starter kit plus for Intel Edison
×1
Fan
×1
Gas sensor
×2
General Purpose Transistor NPN
General Purpose Transistor NPN
×1
9V battery (generic)
9V battery (generic)
×1
Linear Regulator (7805)
Linear Regulator (7805)
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
3D Printer (generic)
3D Printer (generic)

Story

Read more

Custom parts and enclosures

CAD Assembly

Device enclosure

Schematics

Air4Kids Circuit

Code

Air4Kids Code

C/C++
Multisensor monitoring and logging code. It also communicates data over GPRS.
This version does not contain fan motor control.
#include <Wire.h> // Wire library that helps the Arduino with i2c 
#include <Streaming.h> // Streaming C++-style Output with Operator << from http://arduiniana.org/libraries/streaming/

//------------------------------------------------------------------------------
// CONFIGURATION
//------------------------------------------------------------------------------

// define FAST sensor sampling interval
unsigned long INTERVAL_FAST = 5000; // sample duration (ms)30s, david holstius used 60000
unsigned long last_time_fast_ms;     // time of last check/output

String s3 = "C12=";
String s = "";
String si2="";
String sd="SF";
//int x=0;

char inChar;
int i1 = 0;
int i2 = 0;
int OK = 0;
int SIGNAL = 0;

//int reset;
//int w;
int sdc=0;
//int r=0;


//------------------------------------------------------------------------------
// COMPONENT SETUP
//------------------------------------------------------------------------------

//-------------------------------------------------------------------------- MUX

int MUX_BASE = 0; // analog pin to interact with mux
int MUX_CONTROL[] = {
  2, 3, 4, 5}; // digital pins for mux pin selection
int mux(int channel) {
  // select channel on multiplexer
  for (int i = 0; i < 4; i++) { 
    digitalWrite(MUX_CONTROL[i], bitRead(channel, i)); 
  }
  // return base pin for analogRead/Write to use
  return(MUX_BASE);
}

void setup_mux() {
  // set digital pins controlling multiplexer to OUTPUT
  for (int i = 0; i < 4; i++) { 
    pinMode(MUX_CONTROL[i], OUTPUT); 
  }
}

//------------------------------------------------------------ SD card (logging)

#include "SD.h" //SD library to talk to the card
#define SD_PIN 10 // for the data logging shield, we use digital pin 10 for the SD cs line
#define SD_INTERVAL_SYNC 1000 // mills between calls to flush() - to write data to the card
uint32_t sd_last_time = 0; // time of last sync()
File logfile; // the logging file

// setup
void setup_sd() {
  //digitalWrite(SD_PIN, HIGH);
  pinMode(SD_PIN, OUTPUT); // default chip select pin is set to output

  if (!SD.begin(SD_PIN)) { // see if the card is present and can be initialized
    sdc=1;
    return; // don't do anything else
  }
  // create a new file, LOGGERnnnn.csv (nnnn is a number), make a new file every time the Arduino starts up 
  char filename[] = "0000.csv"; 
  for (uint16_t i = 0; i < 10000; i++) {

    for (int j = 0; j < 4; j++) {
      filename[4-j-1] = ((int)(i / pow(10, j)) % 10) + '0';
    }

    if(!SD.exists(filename)) { // only open a new file if it doesn't exist
      logfile = SD.open(filename, FILE_WRITE); // Unix style command flags - logfile.open() procedure FILE_WRITE - create the file and write data to it
      break;  // leave the loop!
    }
  }
  if (!logfile) {
    //logfile<<"could not create file"<<endl;
    sdc=1;
  }
  sd_last_time = millis();
}

// --------------------------------------------------- GPRS setup

void setupIP()
{
  SIGNAL=0;
  i1=0;
  while (SIGNAL==0) {
    delay(10000);
    logger();
    for (int i=0; i<5; i++)
    {
      Serial.println("AT+CGATT?"); //this command is used to attach/detach from GPRS service. the ? should say if you are physically connected i think
      delay(2000);
      ShowSerialData();
    }
    logger();
  
    if (i1==0) {
      Serial.println("AT+CPOWD=1"); //power off the module
      delay(5000);
      SIM900power();
    }
  }
  
  Serial.println("AT+CIPSHUT"); //reset the ip session if any
  delay(2000);
  
//  Serial.println("AT+CIPSTATUS"); //check if the ip stack is initialized
//  delay(2000);

  Serial.println("AT+CIPMUX=0"); //this is to set the connection mode to single i.e 0
  delay(2000);

  Serial.println("AT+CSTT=\"internet.com\",\"wapuser1\",\"wap\""); //start task and set APN, APN is the access point name which interfaces 3G with the internet
  delay(1000);
  
  Serial.println("AT+CIICR"); //bring up wireless connnection with GPRS
  delay(3000);
  
  Serial.println("AT+CIFSR"); //get local IP address
  delay(2000);
  
  Serial.println("AT+CIPSPRT=0"); //set prompt of > when sending data
  delay(3000);
  
  Serial.println("AT+CIPSTART=\"tcp\",\"m2.exosite.com\",\"80\""); //start up TCP connection
  delay(3000);
  
}


//------------------------------------------------------ Chronodot (timekeeping)

#include "Chronodot.h" // Chronodot RTC library
Chronodot RTC;

// setup
void setup_rtc() {
  // kick off the RTC initializing the Wire library - poking the RTC to see if its alive
  Wire.begin();  
  RTC.begin(); // Chronodot
}

void log_time2() {
  DateTime now = RTC.now();
  int year  = now.year();
  int month = now.month();
  int day   = now.day();
  int hours = now.hour();
  int mins  = now.minute();
  int secs  = now.second();
  logfile << "\"" << year;
  logfile << "-" << ((month<10)?"0":"") << month;
  logfile << "-" << ((day<10)?"0":"")   << day;
  logfile << " " << ((hours<10)?"0":"") << hours;
  logfile << ":" << ((mins<10)?"0":"")  << mins;
  logfile << ":" << ((secs<10)?"0":"")  << secs;
  logfile << "\",";
}

//---------------------------------------------------------- DHT22 (temperature)

#include "DHT.h"
#define DHT_PIN 7
#define DHT_TYPE DHT22   // DHT 22  (AM2302)
DHT dht(DHT_PIN, DHT_TYPE);

// setup
void setup_dht() {
  dht.begin();
}

// log
void log_dht() {
  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  int h = dht.readHumidity();
  int t = dht.readTemperature();
  // check if returns are valid, if they are NaN (not a number) then something went wrong!
  if (isnan(t) || isnan(h)) {
    //    log_error("DHT22", "failed to read");
  } 
  else {
    logfile<<h<<",";
    logfile<<t<<",";

  }
  s+=String(h)+",";
  s+=String(t)+",";
}

//----------------------------------------------------------- Sinyei (particles)

#define SNY_PIN 8
unsigned long sny_duration;
unsigned long sny_lowocc_us = 0; // sum of time spent LOW

unsigned long SNY_INTERVAL_ms = 30000; // sample duration (ms)30s, david holstius used 60000
unsigned long sny_last_time;           // time of last serial dump

// setup
void setup_sinyei() {
  pinMode(SNY_PIN, INPUT);
  sny_last_time = millis();
  sny_lowocc_us = 0;
}

// reading
void log_sinyei(unsigned long lowocc_us, unsigned long sampletime_ms) {
  float ratio; 
  ratio = lowocc_us / (sampletime_ms * 1000.0) * 100.0;
  logfile<<ratio<<",";

  si2=String(ratio);
}

//------------------------------------------------------------ Sharp (particles)

// connected to analog 1 and controlled by digital 9
#define SRP_PIN_DUST 1
#define SRP_PIN_LED 9 // led Power is any digital pin on the arduino connected to Pin 3 on the sensor (can be 2, 4,)

int srp_delay1_us = 280; // delays are in microseconds
int srp_delay2_us = 40;
int srp_delayoff_us = 9680;

// setup
void setup_sharp() {
  pinMode(SRP_PIN_LED, OUTPUT);
}

// log
void log_sharp() {
  digitalWrite(SRP_PIN_LED, LOW); // power on the LED
  delayMicroseconds(srp_delay1_us);
  int dustVal = analogRead(SRP_PIN_DUST); // read the dust value
  delayMicroseconds(srp_delay2_us);
  digitalWrite(SRP_PIN_LED, HIGH); // turn the LED off
  delayMicroseconds(srp_delayoff_us);
//
  logfile<<dustVal<<",";
  s+=String(dustVal);
}

// ------------------------------------------------------- voltage

long read_vcc() {
  // Read 1.1V reference against AVcc
  // set the reference to Vcc and the measurement to the internal 1.1V reference
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
  ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
  ADMUX = _BV(MUX5) | _BV(MUX0);
#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
  ADMUX = _BV(MUX3) | _BV(MUX2);
#else
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#endif  
  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Start conversion
  while (bit_is_set(ADCSRA,ADSC)); // measuring
  uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH  
  uint8_t high = ADCH; // unlocks both
  long result = (high<<8) | low;
  result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
  return result; // Vcc in millivolts
}

//------------------------------------------------------- Other GPRS functions



void SIM900power()
// software equivalent of pressing the GSM shield "power" button
{
  pinMode (6,OUTPUT);
  digitalWrite(6,LOW);
  delay(1000);
  digitalWrite(6, HIGH);
  delay(2000);
  digitalWrite(6, LOW);
  delay(3000);
}


void TCPDisconnect()
{
  Serial.println("AT+CIPCLOSE"); //close the TCP connection
  delay(5000);
  //ShowSerialData();
}

//------------------------------------------------------------------------------
// SETUP
//------------------------------------------------------------------------------

void(* resetFunc) (void) =0;

void setup(void) {

  SIM900power();
  // setup serial
  Serial.begin(19200);
  setupIP();
  //TCPConnect();

  setup_rtc(); // all logging depends on RTC, so start it up first
  setup_sd();
  setup_mux();
  setup_dht();
  setup_sinyei();
  setup_sharp();

  // set fast sensor clock
  last_time_fast_ms = millis(); // get the current time
  
  s = s3;

}

//------------------------------------------------------------------------------
// MAIN LOOP
//------------------------------------------------------------------------------


void loop() {

  logger();

  s+=","+si2;
  if (sdc==1) {
    s+=","+sd;
    sdc=0;
  }
    
  si2="";
  sendtoExosite();
  s=s3;
  // reset clock to current time
  last_time_fast_ms = millis(); 

  //--------------------------------------------------------------- sync SD card

  // write data to disk! Don't sync too often (2048 bytes of I/O SD card, a lot of power, takes time)
  if ((millis() - sd_last_time) > SD_INTERVAL_SYNC) { 
    logfile.flush();
    sd_last_time = millis();
  }

}

void logger() {

  sny_duration = pulseIn(SNY_PIN, LOW);
  sny_lowocc_us += sny_duration;

  //-------------------------------------- read/lMog Sinyei sensor (30s interval)

  if ((millis() - sny_last_time) > SNY_INTERVAL_ms) {

    // log Sinyei
    log_sinyei(sny_lowocc_us, millis() - sny_last_time);

    // reset
    sny_lowocc_us = 0;
    sny_last_time = millis();
  }

  logfile<<endl;

  log_time2();
  log_dht();
  log_sharp();

  int num_sensors = 16;
  int sensor_value;

  for (int channel = 0; channel < num_sensors; channel++) {
    sensor_value = analogRead(mux(channel)); 
    if (channel==0 || channel==1 || channel==2 || channel==4 || channel==5 || channel==6 || channel==7 || channel==9 || channel==10 || channel==11){
      logfile<<sensor_value<<",";
      s+=",";
      s+=String(sensor_value);
    }
  }
  delay(100);

}

void sendtoExosite()
{

  int num;
  String le;
  num=s.length();

  le=String(num);
  
  i1=0;
  i2=0;
  OK=0;
  
  Serial.println("AT+CIPSEND"); //begin sending data to remote server
  delay(1700); //was 2000
  
  Serial.flush();
  ShowSerialData(); // i definitely need to read this response since it will let me know whether the data was sent or not
  
  Serial.println("POST /onep:v1/stack/alias HTTP/1.1");
  delay(50);
  
  Serial.println("Host: m2.exosite.com");
  delay(50);
  
  Serial.println("X-Exosite-CIK: 1e6012249cd51557a55b6ec8305543a53ec93d83");
  delay(50);
  
  Serial.println("Accept: application/x-www-form-urlencoded; charset=utf-8");
  delay(50);
  
  Serial.println("Content-Length: "+le);
  delay(50);
  
  Serial.println("Content-Type: application/x-www-form-urlencoded");
  delay(50);
  
  Serial.println("");
  delay(50);
  
  Serial.println(s); //this is where the data I want to send goes
  delay(50);
  
  Serial.flush();
  delay(500); //was 600
  ShowSerialData();
  
  Serial.println((char)26);
  delay(2500); //was 2700
  
  /*
  This part of the code is to troubleshoot if the data is not sending
  */
  
  if (OK==0) //if the response did not read 'OK' then that means that the data did not send and is probably not connected so I have to reconnect 
  {
    Serial.println("NOT WORKING");
    TCPDisconnect();
    setupIP();
    //TCPConnect();
  }
}

void ShowSerialData() // this is where I view the response and index OK if I am using software serial or just index OK if i am using hardware serial
{
  while(Serial.available()!=0)
  {
    inChar = Serial.read();
    if (inChar=='1') {
      SIGNAL++;
    }
    if (inChar=='O') {
      i2=i1;
    }
    if (inChar=='K' && i1-i2==1) {
      OK++;
    }
    i1++;
  }
}

Credits

Ninja

Ninja

9 projects • 7 followers

Comments