Skip to content

Instantly share code, notes, and snippets.

@benfasoli
Last active May 2, 2016 17:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save benfasoli/1db7f7fe731685c489c67c9a79b1527d to your computer and use it in GitHub Desktop.
Save benfasoli/1db7f7fe731685c489c67c9a79b1527d to your computer and use it in GitHub Desktop.
Arduino based handheld spatial meteorological and CO2 analysis platform.
// 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();
}
# 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')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment