Skip to content

Instantly share code, notes, and snippets.

@tuxite
Last active August 29, 2015 14:05
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 tuxite/b1ee48a7c7935c0f8e4c to your computer and use it in GitHub Desktop.
Save tuxite/b1ee48a7c7935c0f8e4c to your computer and use it in GitHub Desktop.
Arduino sketch for Sparfun Weather Shield, sending weather data through UDP broascast Json
/*
Weather Shield using Ethernet
by Matthieu Morin
created on 2014-08-10
Licence: GPL V3
Based on:
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 code reads all the various sensors (wind speed, direction, rain gauge, humidty, pressure, light, batt_lvl)
and reports it over the serial comm port. This can be easily routed to an datalogger (such as OpenLog) or
a wireless transmitter (such as Electric Imp).
Measurements are reported once a second but windspeed and rain gauge are tied to interrupts that are
calcualted at each report.
This example code assumes the GPS module is not used.
*/
#include <Wire.h> //I2C needed for sensors
#include "MPL3115A2.h" //Pressure sensor
#include "HTU21D.h" //Humidity sensor
#include <SPI.h>
#include <Ethernet.h>
// [CONFIG] Sensor instances ---------------------------------------------------
MPL3115A2 myPressure;
HTU21D myHumidity;
// -----------------------------------------------------------------------------
// [CONFIG] 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;
// -----------------------------------------------------------------------------
// [CONFIG] Ethernet -----------------------------------------------------------
byte mac[] = { 0x90, 0xA2, 0xDA, 0x0D, 0x21, 0xD9 }; // MAC Address of the Ethernet Shield
int port = 8000; // Port of the remote server
// Instances of the Ethernet library
EthernetClient client;
char server[] = "iridium";
// -----------------------------------------------------------------------------
//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 minutes; //Keeps track of where we are in various arrays of data
long lastWindCheck = 0;
volatile long lastWindIRQ = 0;
volatile byte windClicks = 0;
volatile float rainHour[60]; //60 floating numbers to keep track of 60 minutes of rain
int winddir = 0; // [0-360 instantaneous wind direction]
float windspeedkmh = 0; // [kmh instantaneous wind speed]
float humidity = 0; // [%]
float tempc = 0; // [temperature C]
float rainin = 0; // [rain mm over the past hour)] -- the accumulated rainfall in the past 60 min
volatile float dailyrainin = 0; // [rain mm so far today in local time]
float pressure = 0;
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
{
dailyrainin += 0.2794; // Each dump is 0.2794mm of water
rainHour[minutes] += 0.2794; //Increase this minute's amount of rain
rainlast = raintime; // set up for next event
}
}
void wspeedIRQ()
// Activated by the magnet in the anemometer (2 ticks per rotation), attached to input D3
{
if (millis() - lastWindIRQ > 10) // Ignore switch-bounce glitches less than 10ms (240kmh max reading) after the reed switch closes
{
lastWindIRQ = millis(); //Grab the current time
windClicks++; //There is 2.4km/h for each click per second.
}
}
// Setup -----------------------------------------------------------------------
void setup()
{
// Open serial communications and wait for port to open:
Serial.begin(9600);
Serial.println("DSM Weather Station Prototype");
// Start the Ethernet connection using DHCP:
if (Ethernet.begin(mac) == 0) {
Serial.println("Failed to configure Ethernet using DHCP");
}
// print your local IP address:
Serial.print("My IP address: ");
for (byte thisByte = 0; thisByte < 4; thisByte++) {
// print the value of each byte of the IP address:
Serial.print(Ethernet.localIP()[thisByte], DEC);
Serial.print('.');
}
Serial.println();
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);
//Configure the pressure sensor
myPressure.begin(); // Get sensor online
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
//Configure the humidity sensor
myHumidity.begin();
seconds = 0;
lastSecond = millis();
// attach external interrupt pins to IRQ functions
attachInterrupt(0, rainIRQ, FALLING);
attachInterrupt(1, wspeedIRQ, FALLING);
// turn on interrupts
interrupts();
Serial.println("Weather Shield online!");
}
void loop()
{
//--------------------------------------------------------------------------
// Start of the Weather sensors Loop
//--------------------------------------------------------------------------
// Keep track of which minute it is
if(millis() - lastSecond >= 1000){
digitalWrite(STAT2, HIGH); //Blink stat LED
lastSecond += 1000;
// Calc the wind speed and direction every second for 120 second to get 2 minute average
float currentSpeed = get_wind_speed();
windspeedkmh = currentSpeed;
int currentDirection = get_wind_direction();
if(++seconds > 59)
{
seconds = 0;
if(++minutes > 59) minutes = 0;
rainHour[minutes] = 0; // Zero out this minute's rainfall amount
}
calcWeather();
// Read the server response if any for debugging purpose
if (client.available()) {
char c;
while (c != 0x0A){ // Read only the first line of the server response.
c = client.read();
Serial.print(c);
}
client.flush(); // Flush the client response for next iteration of the loop.
}
// Connect to the server
connection();
// Send the data then close the connection if connected.
if (client.connected()){
printWeather();
client.stop();
}
digitalWrite(STAT2, LOW); //Turn off stat LED
}
delay(100);
}
//Calculates each of the variables that wunderground is expecting
void calcWeather()
{
//Calc winddir
winddir = get_wind_direction();
//Calc humidity
humidity = myHumidity.readHumidity();
//Calc tempc from pressure sensor
tempc = myPressure.readTemp();
//Total rainfall for the day is calculated within the interrupt
//Calculate amount of rainfall for the last 60 minutes
rainin = 0;
for(int i = 0 ; i < 60 ; i++)
rainin += rainHour[i];
//Calc pressure
pressure = myPressure.readPressure()/100.0;
//Calc light level
light_lvl = get_light_level();
//Calc battery level
batt_lvl = get_battery_level();
}
//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 instantaneous wind speed, in km/h
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 * 2.4 = 9.6kmh
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?
}
// Adds all data to a Json object (will be printed in UDP).
void printWeather()
{
String request = "GET /feed?platform=arduino";
char buf[6]; // To store the value of floats during conversion to String.
// We print only instant value, the computation will be done server side.
request += urlAdd("wind_speed");
dtostrf(windspeedkmh, 4, 2, buf);
request += buf;
request += urlAdd("wind_dir");
request += winddir;
request += urlAdd("humidity");
dtostrf(humidity, 4, 2, buf);
request += buf;
request += urlAdd("temperature");
dtostrf(tempc, 4, 2, buf);
request += buf;
request += urlAdd("pressure");
dtostrf(pressure, 4, 2, buf);
request += buf;
// The rain is computed for one hour
request += urlAdd("rain");
dtostrf(rainin, 4, 2, buf);
request += buf;
request += urlAdd("battery_level");
dtostrf(batt_lvl, 4, 2, buf);
request += buf;
request += urlAdd("light_level");
dtostrf(light_lvl, 4, 2, buf);
request += buf;
request += " HTTP/1.1\nHost: ";
request += server;
client.println(request);
}
String urlAdd(const char* name){
String url = "&";
url += name;
url += "=";
return url;
}
void connection(){
if (client.connect(server, port)) {
Serial.print("Connected to ");
Serial.println(server);
}
else {
// If you didn't get a connection to the server:
Serial.println("Connection failed");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment