benfasoli
3/30/2016 - 10:11 PM

Arduino based handheld spatial meteorological and CO2 analysis platform.

Arduino based handheld spatial meteorological and CO2 analysis platform.

# Ben Fasoli
rm(list=ls())
setwd('~/links/projects/archive/urop_airmet')
library(dplyr); library(uataq); library(stringr)

# Raw parsing ------------------------------------------------------------------
raw  <- readr::read_lines('data/test_2/RAW.DAT') %>%
  grep(pattern='UATAQ|GPGGA', value=T) %>%
  str_split_fixed(',', 2) %>%
  (function(x) { 
    df <- data_frame(idx=suppressWarnings(as.numeric(x[ ,1])), 
                     str=x[ ,2]) %>%
      mutate(str = gsub('.*GPGGA', '$GPGGA', str),
             str = gsub('.*UATAQ', '$UATAQ', str))
    split <- str_split_fixed(df$str, ',', 2)
    df %>% transmute(idx, nmea=split[ ,1], str=split[ ,2])
  }) %>%
  na.omit()

# Arduino millis timing index resets periodically. Produce a continuous pseudo-
# timestamp for matching the measurements with gps data.
numel <- nrow(raw)
while (T) {
  didx <- c(NA, raw$idx[2:numel] - raw$idx[1:(numel-1)])
  neg <- didx < 1
  if (!any(na.omit(neg))) break
  
  first <- head(which(neg), 1)
  raw$idx[first:numel] <- raw$idx[first:numel] + raw$idx[first-1]
}

# Function to break csv strings into separate columns
break_me <- function(x) {
  x <- x %>%
    mutate(ndelim = str_count(str, ',')) %>%
    filter(ndelim == median(ndelim))
  brk <- breakstr(x$str) %>%
    lapply(function(x) suppressWarnings(as.numeric(x))) %>%
    as_data_frame()
  bind_cols(x, brk)
}

uataq <- filter(raw, nmea=='$UATAQ') %>% 
  break_me() %>%
  rename(t_c=V1, rh_pct=V2, p_pa=V3, co2_v=V4, co2_ppm=V5, pm_1023=V6, pm_ugm3=V7) %>%
  select(-nmea, -ndelim, -str)

gpgga <- filter(raw, nmea=='$GPGGA') %>% 
  break_me() %>%
  rename(gps_time=V1, lat=V2, ns=V3, lon=V4, ew=V5, fix=V6, nsat=V7) %>%
  select(idx, gps_time, lat, lon) %>%
  mutate(lat = floor(lat/100)+(lat-floor(lat/100)*100)/60,
         lon = -(floor(lon/100)+(lon-floor(lon/100)*100)/60))

df <- bind_rows(uataq, gpgga) %>%
  arrange(idx)


# Data interpolation -----------------------------------------------------------
# options:
# t_c, rh_pct, p_pa, co2_v, co2_ppm, pm_1023, pm_ugm3
show <- 'p_pa'

df <- df[c('idx', show, 'lat', 'lon')]

interp <- as_data_frame(lapply(df, na_interp, x=df$idx)) %>%
  na.omit()

if (nrow(interp) < 100)
  stop(paste(
    'Something doesn\'t seem right when trying to geolocate. The interpolated',
    'dataset is tiny. Maybe there was a problem with the antenna?'
  ))

# Grid averaging ---------------------------------------------------------------
# interp <- interp[interp$lon < -111.818 & interp$lon > -112, ]
grid_size <- 0.0002
domain <- list()
domain$lat   <- seq(40.47511, 40.80111, by=grid_size)
domain$lon   <- seq(-112.1142, -111.7782, by=grid_size)

interp$grlat <- find_neighbor(interp$lat, domain$lat)
interp$grlon <- find_neighbor(interp$lon, domain$lon)

obsav <- aggregate(interp[[show]], by=list(interp$grlat, interp$grlon),
                   mean, na.rm=T)
grd <- data_frame(tracer = obsav$x,
                  lat   = domain$lat[obsav$Group.1],
                  lon   = domain$lon[obsav$Group.2])


# Create map -------------------------------------------------------------------
library(leaflet)
cpal <- colorNumeric(c('blue', 'cyan', 'green', 'yellow', 'orange', 'red'),
                     seq(min(grd$tracer, na.rm=T),
                         max(grd$tracer, na.rm=T),
                         length.out=64))
leaflet() %>%
  fitBounds(min(grd$lon, na.rm=T), min(grd$lat, na.rm=T),
            max(grd$lon, na.rm=T), max(grd$lat, na.rm=T)) %>%
  addTiles(urlTemplate='http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}@2x.png',
           attribution='UATAQ, Ben Fasoli. Basemap from CartoDB') %>%
  addCircleMarkers(lng=grd$lon, lat=grd$lat, radius=5, weight=2,
                   fillColor=cpal(grd$tracer), color=cpal(grd$tracer),
                   opacity=0.3, fillOpacity=0.3) %>%
  addLegend(pal=cpal, values=grd$tracer, position='bottomleft')
// Air sensor 1
// Ben Fasoli
//
// Arduino based platform to measure temperature, relative humidity,
// pressure, CO2, and PM.

// Libraries
#include <dht.h>
#include <Adafruit_BMP085.h>
#include <Adafruit_GPS.h>
#include <SPI.h>
#include <SD.h>
#include <SoftwareSerial.h>

// Declarations
int interval = 1000; // sampling interval, ms
uint32_t timer = 0;

// Initializations
// DHT22
dht DHT;
const int dhtPin = 2; // digital pin connected to DHT22 data
// BMP180
Adafruit_BMP085 bmp;
// GPS
SoftwareSerial gpsSerial(5, 6); // TX, RX pins
Adafruit_GPS GPS(&gpsSerial);
// Sharp
const int sharpPin = 0; // analog pin
const int sharpLed = 8;
// COZIR
const int cozirPin = 2; // analog pin


// Setup
void setup() {
  Serial.begin(9600);
  Serial.println("Air monitoring package: 1");
  Serial.println("Ben Fasoli");
  Serial.println("time_utc, lat_dd, lon_dd, alt_m, nsat, dht_temp_c, dht_rh_pct," \
                 "bmp_pres_pa, bmp_temp_c, cozir_raw, cozir_co2_ppm, pm_raw, pm_ugm3");
  Serial.println();

  SD.begin(10);

  bmp.begin();

  GPS.begin(9600);
  GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);

  pinMode(sharpLed, OUTPUT);
}

// Data collection loop
void loop() {
  File file = SD.open("raw.dat", FILE_WRITE);
  // GPS
  char c = GPS.read();
  if (GPS.newNMEAreceived()) {
    char* nmea = GPS.lastNMEA();
    int i;
    for (i = 0; i < strlen(nmea); i++) {
      if (nmea[i] == '\n') nmea++;
    }
    file.print(millis()); file.print(",");
    file.print(nmea);
    Serial.print(nmea);
  }


  // UATAQ NMEA sentence
  if (timer > millis()) timer = millis();
  if (millis() - timer > interval) {
    Serial.println(millis());
    timer = millis();
    file.print(millis()); file.print(",");
    file.print("$UATAQ"); file.print(",");

    // DHT22
    int dht_chk  = DHT.read22(dhtPin);
    if (dht_chk == 0) {
      file.print(DHT.temperature);
      file.print(",");
      file.print(DHT.humidity);
      file.print(",");
    } else {
      file.print(-9999.0);
      file.print(",");
      file.print(-9999.0);
      file.print(",");
    }

    // BMP180
    float bmp_pres_pa = bmp.readPressure();
    //float bmp_temp_c  = bmp.readTemperature();
    if (bmp_pres_pa < 30000.0 | bmp_pres_pa > 110000.0) {
      bmp_pres_pa = -9999.0;
      //bmp_temp_c  = -9999.0;
    }
    file.print(bmp_pres_pa);
    file.print(",");
    //file.print(bmp_temp_c);
    //file.print(",");

    // COZIR
    float cozir_raw = analogRead(cozirPin) / 1023.0 * 5.0;
    float cozir_co2_ppm = cozir_raw / 3.3 * 2000.0 ;
    file.print(cozir_raw);
    file.print(",");
    file.print(cozir_co2_ppm);
    file.print(",");
    
    // Sharp
    digitalWrite(sharpLed, LOW);
    delayMicroseconds(280);
    float sharp_raw = analogRead(sharpPin);
    delayMicroseconds(40);
    digitalWrite(sharpLed, HIGH);
    file.print(sharp_raw);
    file.print(",");
    file.print((0.17 * sharp_raw * 5.0 / 1023.0) * 1000.0);
    
    // End NMEA sentence
    file.println();
  }

  file.close();
}