Last active
May 2, 2016 17:45
-
-
Save benfasoli/1db7f7fe731685c489c67c9a79b1527d to your computer and use it in GitHub Desktop.
Arduino based handheld spatial meteorological and CO2 analysis platform.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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