Skip to content

Instantly share code, notes, and snippets.

@azdle
Created September 3, 2014 21:19
Show Gist options
  • Save azdle/dc984bf811b8e429ea42 to your computer and use it in GitHub Desktop.
Save azdle/dc984bf811b8e429ea42 to your computer and use it in GitHub Desktop.
Exosite Powered Sparkfun Arduino Weather Station
/*
** Exosite Modifcations By Patrick Barrett <[email protected]>
** Modified 03-09-2014
**
** Note: This runs on the Arduino Yun, but requires modifications to the libraries,
** provided by Sparkfun, to use software I2C (https://github.com/greiman/DigitalIO)
*/
/*
Based on the work by
Weather Shield Example
By: Nathan Seidle
SparkFun Electronics
Date: November 16th, 2013
License: This code is public domain but you buy me a beer if you use this and we meet someday (Beerware license).
Much of this is based on Mike Grusin's USB Weather Board code: https://www.sparkfun.com/products/10586
This example code assumes the GPS module is not used.
*/
//////////////////////////////////////////////////////
#include <DigitalIO.h>
#include "MPL3115A2Soft.h"
#include "HTU21DSoft.h"
#include <EEPROM.h>
#include <SPI.h>
#include <Bridge.h>
#include <YunClient.h>
#include <Process.h>
#include <Exosite.h>
/*==============================================================================
* Configuration Variables
*
* Change these variables to your own settings.
*=============================================================================*/
// Use these variables to customize what datasources are read and written to.
String returnString;
// Number of Errors before we try a reprovision.
const unsigned char reprovisionAfter = 3;
/*==============================================================================
* End of Configuration Variables
*=============================================================================*/
class YunClient client;
Exosite exosite(&client);
unsigned char errorCount = reprovisionAfter; // Force Provision On First Loop
char macString[18]; // Used to store a formatted version of the MAC Address
#define YUN 1
//Create an instance of the object
MPL3115A2Soft myPressure;
HTU21DSoft myHum;
//Hardware pin definitions
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// digital I/O pins
const byte WSPEED = 3;
const byte RAIN = 2;
const byte STAT1 = 7;
const byte STAT2 = 8;
// analog I/O pins
const byte REFERENCE_3V3 = A3;
const byte LIGHT = A1;
const byte BATT = A2;
const byte WDIR = A0;
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//Global Variables
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
long lastSecond; //The millis counter to see when a second rolls by
byte seconds; //When it hits 60, increase the current minute
byte seconds_2m; //Keeps track of the "wind speed/dir avg" over last 2 minutes array of data
byte minutes; //Keeps track of where we are in various arrays of data
byte minutes_10m; //Keeps track of where we are in wind gust/dir over last 10 minutes array of data
long lastWindCheck = 0;
volatile long lastWindIRQ = 0;
volatile byte windClicks = 0;
long lastWeatherCheck = 0;
//These are all the weather values that wunderground expects:
int winddir = 0; // [0-360 instantaneous wind direction]
float windspeedkph = 0; // [mph instantaneous wind speed]
float humidity = 0; // [%]
float tempc = 0; // [temperature F]
volatile float dailyrainmm = 0; // [rain inches so far today in local time]
//float baromin = 30.03;// [barom in] - It's hard to calculate baromin locally, do this in the agent
float pressure = 0;
//float dewptf; // [dewpoint F] - It's hard to calculate dewpoint locally, do this in the agent
float batt_lvl = 11.8; //[analog value from 0 to 1023]
float light_lvl = 455; //[analog value from 0 to 1023]
// volatiles are subject to modification by IRQs
volatile unsigned long raintime, rainlast, raininterval, rain;
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//Interrupt routines (these are called by the hardware interrupts, not by the main code)
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void rainIRQ()
// Count rain gauge bucket tips as they occur
// Activated by the magnet and reed switch in the rain gauge, attached to input D2
{
raintime = millis(); // grab current time
raininterval = raintime - rainlast; // calculate interval between this and last event
if (raininterval > 10) // ignore switch-bounce glitches less than 10mS after initial edge
{
dailyrainmm += 0.2794; //Each dump is 0.2794mm of water
rainlast = raintime; // set up for next event
Serial.print("It's Raining");
}
}
void wspeedIRQ()
// Activated by the magnet in the anemometer (2 ticks per rotation), attached to input D3
{
static int wndcnt = 0;
if (millis() - lastWindIRQ > 10) // Ignore switch-bounce glitches less than 10ms (142MPH max reading) after the reed switch closes
{
lastWindIRQ = millis(); //Grab the current time
windClicks++; //There is 2.4kph for each click per second.
}
}
void setup()
{
Serial.begin(9600);
Serial.println("Weather Shield Example");
pinMode(STAT1, OUTPUT); //Status LED Blue
pinMode(STAT2, OUTPUT); //Status LED Green
pinMode(WSPEED, INPUT_PULLUP); // input from wind meters windspeed sensor
pinMode(RAIN, INPUT_PULLUP); // input from wind meters rain gauge sensor
pinMode(REFERENCE_3V3, INPUT);
pinMode(LIGHT, INPUT);
myPressure.setModeBarometer(); // Measure pressure in Pascals from 20 to 110 kPa
myPressure.setOversampleRate(7); // Set Oversample to the recommended 128
myPressure.enableEventFlags(); // Enable all three pressure and temp event flags
seconds = 0;
lastSecond = millis();
lastWeatherCheck = millis();
// attach external interrupt pins to IRQ functions
attachInterrupt(1, rainIRQ, FALLING);
attachInterrupt(0, wspeedIRQ, FALLING);//May Need to swap IRQ for uno
// turn on interrupts
interrupts();
Bridge.begin();
getYunMAC(macString, 18);
// Print Some Useful Info
Serial.print(F("MAC Address: "));
Serial.println(macString);
}
void loop()
{
// Update Global Variables
calcWeather();
// Check if we should reprovision.
if(errorCount >= reprovisionAfter){
if(exosite.provision("exosite", "ard-generic", macString)){
errorCount = 0;
}
}
//Write to "uptime" and read from "uptime" and "command" datasources.
if ( exosite.writeRead(buildWriteString(), "", returnString)){
Serial.println("OK");
errorCount = 0;
}else{
Serial.println("Error");
errorCount++;
}
delay(1000);
}
//Calculates each of the variables that wunderground is expecting
int calcWeather()
{
//Calc humidity
humidity = myHum.readHumidity();
//Calc tempf from pressure sensor
tempc = myPressure.readTemp();
//Calc pressure
pressure = myPressure.readPressure();
//Calc light level
light_lvl = get_light_level();
//Calc battery level
batt_lvl = get_battery_level();
//Calc winddir
winddir = get_wind_direction();
//Calc windspeed
windspeedkph = get_wind_speed();
return 0;
}
//Returns the voltage of the light sensor based on the 3.3V rail
//This allows us to ignore what VCC might be (an Arduino plugged into USB has VCC of 4.5 to 5.2V)
float get_light_level()
{
float operatingVoltage = analogRead(REFERENCE_3V3);
float lightSensor = analogRead(LIGHT);
operatingVoltage = 3.3 / operatingVoltage; //The reference voltage is 3.3V
lightSensor = operatingVoltage * lightSensor;
return(lightSensor);
}
//Returns the voltage of the raw pin based on the 3.3V rail
//This allows us to ignore what VCC might be (an Arduino plugged into USB has VCC of 4.5 to 5.2V)
//Battery level is connected to the RAW pin on Arduino and is fed through two 5% resistors:
//3.9K on the high side (R1), and 1K on the low side (R2)
float get_battery_level()
{
float operatingVoltage = analogRead(REFERENCE_3V3);
float rawVoltage = analogRead(BATT);
operatingVoltage = 3.30 / operatingVoltage; //The reference voltage is 3.3V
rawVoltage = operatingVoltage * rawVoltage; //Convert the 0 to 1023 int to actual voltage on BATT pin
rawVoltage *= 4.90; //(3.9k+1k)/1k - multiple BATT voltage by the voltage divider to get actual system voltage
return(rawVoltage);
}
//Returns the instataneous wind speed
float get_wind_speed()
{
float deltaTime = millis() - lastWindCheck; //750ms
deltaTime /= 1000.0; //Covert to seconds
float windSpeed = (float)windClicks / deltaTime; //3 / 0.750s = 4
windClicks = 0; //Reset and start watching for new wind
lastWindCheck = millis();
windSpeed *= 2.4; //4 * 1.492 = 5.968MPH
return(windSpeed);
}
//Read the wind direction sensor, return heading in degrees
int get_wind_direction()
{
unsigned int adc;
adc = analogRead(WDIR); // get the current reading from the sensor
// The following table is ADC readings for the wind direction sensor output, sorted from low to high.
// Each threshold is the midpoint between adjacent headings. The output is degrees for that ADC reading.
// Note that these are not in compass degree order! See Weather Meters datasheet for more information.
if (adc < 380) return (113);
if (adc < 393) return (68);
if (adc < 414) return (90);
if (adc < 456) return (158);
if (adc < 508) return (135);
if (adc < 551) return (203);
if (adc < 615) return (180);
if (adc < 680) return (23);
if (adc < 746) return (45);
if (adc < 801) return (248);
if (adc < 833) return (225);
if (adc < 878) return (338);
if (adc < 913) return (0);
if (adc < 940) return (293);
if (adc < 967) return (315);
if (adc < 990) return (270);
return (-1); // error, disconnected?
}
void getYunMAC(char* MACptr, byte bufSize) {
Process p; // Create a process and call it "p"
p.runShellCommand("/sbin/ifconfig -a | /bin/grep HWaddr |/bin/grep wlan0 | /usr/bin/awk '{print $5}'");
for(int i = 0; i < (bufSize-1) && p.available() > 0; i++) {
MACptr[i] = p.read();
MACptr[i+1] = 0;
}
}
String buildWriteString()
{
return "wd=" + String(winddir) +
"&ws=" + String(windspeedkph) +
"&h=" + String(humidity) +
"&t=" + String(tempc) +
"&r=" + String(dailyrainmm) +
"&p=" + String(pressure) +
"&b=" + String(batt_lvl) +
"&l=" + String(light_lvl) +
"&u=" + String(millis());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment