Home

Photon Based Weathercloud Sensor

Designed to read from weather sensors connected to the Photon (in this example they are DHT-style) and send the resulting data on to Weathercloud.net as data for their system to record and be used by the user (or others). The data is fairly simple (all integer based) but Weathercloud accepts a vast variety of data types (all are listed below). The most difficult part is that the user will need to customize their sensor setup to match what they have available and populate the appropriate array fields to send the data to Weathercloud (as well as obtain their own KEY and ID data to use the service). This also makes a very simplistic website so that someone can view sensor data in realtime on their Photon.

No .zip file is provided at this time. Please note that this project is still in progress.


Photon Weathercloud Code - Last Revised 12/31/2016

/*
Weathercloud-post
Created by: David Snell
Last Updated: 12/31/2016
Description: Designed to read from weather sensors connected to the Photon (in this example they are DHT-style) and send
the resulting data on to Weathercloud.net as data for their system to record and be used by the user (or others). The
data is fairly simple (all integer based) but Weathercloud accepts a vast variety of data types (all are listed below).
The most difficult part is that the user will need to customize their sensor setup to match what they have available
and populate the appropriate array fields to send the data to Weathercloud (as well as obtain their own KEY and ID data
to use the service).
This also makes a very simplistic website so that someone can view sensor data in realtime on their Photon.

The DHT-type sensors have been used in the current design. The code expects the following pinout:
D6 = Data in from the DHT sensor.
VIN = Power to the DHT sensor.
GND = Connected to the DHT sensor.
A 10K Ohm resistor connected between D6 and VIN.
Power supplied to the Photon using the microUSB port.

This code is strictly as-is. Anyone provided a copy is free to use it as they desire. The existing DHT sensors rely on
the PietteTech_DHT library which has it's own guidelines for use.
*/

// This #include statement was automatically added by the Particle IDE.
#include "PietteTech_DHT/PietteTech_DHT.h"

// Enable the Backup RAM for retaining values otherwise.
//STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));

// Begin General
int Photon = 1; // Which Photon is being targeted.
bool Networked = false;
TCPClient webClient;
TCPClient WeatherClient;
TCPServer webServer = TCPServer( 80 );
unsigned int SensorDelay = 5000; // Set the delay between temperature readings. Default is 5 seconds (5000 milliseconds)
unsigned long SensorCheck = 5000; // Set the initial time before a sensor reading occurs
unsigned long NetworkDelay = 300000; // Set to check every 5 minutes
unsigned long NetworkCheck = 60000;
unsigned int NetworkFailed = 0;
int NetworkFailures = 0;
// End General

// Begin DHT Specific
//String DHTTYPE[ 5 ] = { "", "AM2301", "DHT11", "DHT11", "DHT22" };
#define DHTPIN 6
#define DHTTYPE AM2301 // Used on Photon 1
//#define DHTTYPE DHT11 // Used on Photons 2 & 3
//#define DHTTYPE DHT22 // Used on Photon 4
void dht_wrapper(); // must be declared before the lib initialization
PietteTech_DHT DHT( DHTPIN, DHTTYPE, dht_wrapper );
//int n;      // counter
// End DHT Specific

// Begin Weathercloud Variables
String WeatherID[ 5 ] = { "", "161033db4fe9edac", "56c65f73c824363e", "1b7e34364fa68ae7", "" }; // ID used for the specific instance reporting to Weathercloud.
String WeatherKey[ 5 ] = { "", "bf78c26f408d0f651d03235e5f7d3cf7", "f3153986759e121994be6d354738c5ce", "11918fefe05965e03ca234500155fd31", "" }; // Key used for the specific instance reporting to Weathercloud.
String WeatherServer = "api.weathercloud.net"; // Server target for the Weathercloud API.
double APIVersion = 0.6; // Version of Weathercloud API this is designed for.
double WeatherVersion = 1.4; // Version required by Weathercloud API.
int WeatherType = 921; // Type required by Weathercloud API.
unsigned long PostTime = 600000; // Initial delay of 5 minutes after startup.
unsigned long PostDelay = 600000; // Delay between data postings of 10 minutes.
const int MaxSensors = 6; // The current max number of sensor types listed in the SensorNames array..
int LocalTemp; // The current Fahrenheit temperature reading from the sensor.
int SensorReadings[ 6 ]; // Array to hold all the sensor readings that you might report back. Should match the SensorNames array.
String SensorNames[ 6 ] = { "tempin", "temp", "dewin", "dew", "humin", "hum" }; // Fill this array with the keywords below. XX in the names denotes additional values Weathercloud allows, from 02 to 20 to identify additional sensors.
// Additional names to add as Weathercloud builds support would be: 
// "soilmoistXX", "leafwetXX", "bartrend", "time", "forecast", "forecasticon", "battery", "batteryXX"
/* Labeled as SensorReadings Array # = SensorNames name = Description
0 = tempin = Temperature indoors (tempin) in Celsius * 10 (30.0 is shown as 300)
1 = temp = Temperature in Celsius
2 = tempXX = Temperature in Celsius
3 = tempagroXX = Temperature in Celsius
4 = chill = Temperature in Celsius
5 = dewin = Temperature in Celsius
6 = dew = Temperature in Celsius
7 = heatin = Temperature in Celsius
8 = heat = Temperature in Celsius
9 = thw = Temperature in Celsius
10 = humin = Humidity indoors (humin) in %.
11 = hum = Humidity (hum) in %.
12 = humXX = Humidity (humXX) in %.
13 = wspd = Wind speed (wspd) in m/s.
14 = wspdXX = Wind speed (wspdXX) in m/s.
15 = wspdhi = Wind speed (wspdhi) in m/s.
16 = wspdhiXX = Wind speed (wspdhiXX) in m/s.
17 = wspdavg = Wind speed (wspdavg) in m/s.
18 = wspdavgXX = Wind speed (wspdavgXX) in m/s.
19 = wdir = Wind direction (wdir) in degrees.
20 = wdirXX = Wind direction (wdirXX) in degrees.
21 = wdiravg = Wind direction (wdiravg) in degrees.
22 = wdiravgXX = Wind direction (wdiravgXX) in degrees.
23 = bar = Atmospheric pressure (bar) in hectopascals.
24 = rain = Daily total rain (rain) in mm.
25 = rainrate = Rate of rain (rainrate) in mm per hour.
26 = et = Daily total ET (et) in mm.
27 = solarrad = Solar radiation (solarrad) in watts per square meter.
28 = uvi = UV index (uvi).
// Types below this point are not yet accepted but are in the API
29 = soilmoistXX = Soil moisture (soilmoist) in centibars (Cb).
30 = leafwetXX = Leaf wetness (leafwetXX).
31 = bartrend = (bartrend)
32 = time = Time in hh:mm [0, 2400].
33 = forecast = (forecast)
34 = forecasticon = (forecasticon)
35 = battery = Battery voltage (battery) in V.
36 = batteryXX = Battery state (batteryXX) 0 = low, 1 = OK.
37 = date = date in yyyymmdd format
*/
// End Weathercloud Variables

// Begin Time related
String StartTime; // String showing the time when the Photon was started
unsigned int CurrentTime[ 5 ]; // Array of the segments of time once separated.
String LastTime; // String showing the time when the last posting of data was made
unsigned long TimeServerCheck = 60000; // Set the base delay before checking the server for time
unsigned long TimeServerDelay = 3600000; // Delay between checking the server for the time. Default is 1 hour
unsigned long TimeSelfCheck = 60000; // Set the base timing value
unsigned long TimeSelfDelay = 60000; // How often it recalculates it's own time (once per minute)
// End Time related section

// This wrapper is required foor the PierreTech library.
void dht_wrapper() {
  DHT.isrCallback();
}
// End DHT wrapper

// Begin GetServerTime
void GetServerTime() {
  CurrentTime[ 0 ] = Time.month();
  CurrentTime[ 1 ] = Time.day();
  CurrentTime[ 2 ] = Time.year();
  CurrentTime[ 3 ] = Time.hour();
  CurrentTime[ 4 ] = Time.minute();
}
// End GetServerTime

// Begin GetSelfTime
void GetSelfTime() {
  CurrentTime[ 4 ] = CurrentTime[ 4 ] + 1;
  if( CurrentTime [ 4 ] > 59 ){
    CurrentTime[ 4 ] = 0;
    CurrentTime[ 3 ] = CurrentTime[ 3 ] + 1;
    if( CurrentTime [ 3 ] > 23 ){
      CurrentTime[ 3 ] = 0;
      CurrentTime[ 2 ] = CurrentTime[ 2 ] + 1;
    }
  }
}
// End GetSelfTime

// Begin FormatDateTime
String FormatDateTime( int Month, int Day, int Year, int Hour, int Minute ){
  String tempDateTime = String( Month );
  tempDateTime.concat( "/" );
  tempDateTime.concat( String( Day ) );
  tempDateTime.concat( "/" );
  tempDateTime.concat( String( Year ) );
  tempDateTime.concat( " @ " );
  tempDateTime.concat( String( Hour ) );
  tempDateTime.concat( ":" );
  if( Minute < 10 ){
    tempDateTime.concat( "0" );
  }
  tempDateTime.concat( String( Minute ) );
  tempDateTime.concat( " UTC" );
  return tempDateTime;
}
// End FormatDateTime

// Begin setup
void setup() {
  // Begin the web server
  webServer.begin();
  // Sets PINs as needed
  pinMode( DHTPIN, INPUT );

  // Set base value of -1000 for all SensorReadings (-1000 counts as no value set)
  for( int Index = 0; Index < MaxSensors; Index++ ){
    SensorReadings[ Index ] = -1000;
  }
  // Delay to let the web server start up
  delay( 2000 );
  if( WiFi.ready() ){
    Networked = true;
  }
  // Record the startup time of the Photon
  StartTime = Time.timeStr();
}
// End setup

// Begin loop
void loop() {
  // Performs a check of the sensors on a regular basis.
  if( millis() > SensorCheck ){
    if( Photon < 5 ){
      CheckSensors();
    }
    SensorCheck = millis() + SensorDelay;
  }
  // Perform check on wifi
  if( millis() > NetworkCheck ){
    Networked = NetCheck();
    NetworkCheck = millis() + NetworkDelay;
  }
  // Performs a time calculation itself based on millis()
  if( millis() > TimeSelfCheck ){
    GetSelfTime();
    TimeSelfCheck = millis() + TimeSelfDelay;
  }
  // Performs a time check with the server
  if( millis() > TimeServerCheck && Networked ){
    GetServerTime();
    TimeServerCheck = millis() + TimeServerDelay;
  }
  // Checks if any clients are connected for the webserver and if so gives them the webpage.
  if ( webClient.connected() && Networked ){
    while( webClient.available() ){
      serveWebpage();
    }
  } else {
    webClient = webServer.available();
  }
  // Checks if the sensor should be sent to Weathercloud or not.
  if( millis() > PostTime && Networked ){
    if( Photon < 4 ){
      PostToWeathercloud();
    }
  }
  UpdateStatus();
}
// End loop

// Begin NetCheck
bool NetCheck(){
  bool TempBool;
  if( !WiFi.ready() ){
    NetworkFailed ++;
    TempBool = false;
    WiFi.connect();
    if( NetworkFailed > 5 ){
      WiFi.off();
      WiFi.on();
      NetworkFailed = 0;
    }
  } else {
    TempBool = true;
  }
  return TempBool;
}
// End NetCheck

// Begin CheckSensors
void CheckSensors(){
  // Checks the various sensors used. In this case it is a DHT-style sensor.
  int result = DHT.acquireAndWait();
  switch ( result ) {
    case DHTLIB_OK:
      break;
    case DHTLIB_ERROR_CHECKSUM:
      break;
    case DHTLIB_ERROR_ISR_TIMEOUT:
      break;
    case DHTLIB_ERROR_RESPONSE_TIMEOUT:
      break;
    case DHTLIB_ERROR_DATA_TIMEOUT:
      break;
    case DHTLIB_ERROR_ACQUIRING:
      break;
    case DHTLIB_ERROR_DELTA:
      break;
    case DHTLIB_ERROR_NOTSTARTED:
      break;
    default:
      break;
  }
  float h = DHT.getHumidity();
  float d = DHT.getDewPoint();
  float t = DHT.getCelsius();
  LocalTemp = int( ( ( ( t * 9 ) / 5 ) + 32 ) );
  SensorReadings[ 0 ] = int( ( t * 10 ) );
  SensorReadings[ 2 ] = int( ( d * 10 ) );
  SensorReadings[ 4 ] = int( h );
}

// Begin serveWebpage
void serveWebpage() {
  // Simple web server response that shows which Photon is being used as well as the sensor readings taken.
  webClient.println( "HTTP/1.1 200 OK" );
  webClient.println( "Content-Type: text/html" );
  webClient.println();
  webClient.println( "<html>" );
  webClient.println( "<head>" );
  webClient.print( "<meta http-equiv='refresh' content='5'>" );
  webClient.print( "<title>Photon " );
  webClient.print( Photon );
  webClient.println( "</title>" );
  webClient.println( "</head>" );
  webClient.println( "<body>" );
  webClient.print( "<b>Photon " );
  webClient.print( Photon );
  if( Photon > 3 ){
    webClient.print( " - Not Reporting" );
  }
  webClient.println( "</b></br>" );
  webClient.print( "Started on " );
  webClient.print( StartTime );
  webClient.print( "</br>" );
  webClient.print( "Current time is " );
  webClient.print( FormatDateTime( CurrentTime[ 0 ], CurrentTime[ 1 ], CurrentTime[ 2 ], CurrentTime[ 3 ], CurrentTime[ 4 ] ) );
  webClient.print( "</br>" );
  webClient.println( "</br>" );
  webClient.println( "<u>Posted Data</u></br>" );
  webClient.print( "Last post was " );
  if( LastTime != NULL ){
    webClient.print( LastTime );
    webClient.print( "</br>" );
  } else {
    webClient.println( "not yet performed</br>" );
  }
  webClient.println( "Next posting will be " );
  if( Photon < 4 ){
    int NextPostMinute = ( CurrentTime[ 4 ] + ( ( PostTime - millis() ) / 60000 ) );
    int NextPostHour = CurrentTime[ 3 ];
    if( NextPostMinute > 60 ) {
      NextPostHour = ( NextPostHour + 1 );
      NextPostMinute = ( NextPostMinute - 60 );
    }
    if( NextPostHour > 24 ) {
      NextPostHour = 0;
    }
    webClient.print( NextPostHour );
    webClient.print( ":" );
    if ( NextPostMinute < 10 ) {
      webClient.print( "0" );
    }
    webClient.print( NextPostMinute );
    webClient.println( " UTC</br>" );
  } else {
    webClient.println( "never</br>" );
  }
  webClient.println( "</br>" );
  webClient.println( "<u>Current Readings</u></br>" );
  webClient.print( "Local temperature in F = " );
  webClient.print( LocalTemp );
  webClient.println( "</br>" );
  for( int x = 0; x < MaxSensors; x++ ){
    if( SensorReadings[ x ] != -1000 ) {
      webClient.print( SensorNames[ x ] );
      webClient.print( " = " );
      webClient.print( SensorReadings[ x ] );
      webClient.println( "</br>" );
    }
  }
  webClient.println( "</body>" );
  webClient.println( "</html>" );
  webClient.flush();
  webClient.stop();
  delay( 100 );
}
// End serveWebpage

void PostToWeathercloud( void ) {
  // Posts the data to the Weathercloud server. Only posts sensor readings that were changed from the -1000 value.
  WeatherClient.connect( WeatherServer, 80 );
  if( WeatherClient.connected() ) {
    WeatherClient.print( "GET /v01/set/wid/" );
    WeatherClient.print( WeatherID[ Photon ] );
    WeatherClient.print( "/key/" );
    WeatherClient.print( WeatherKey[ Photon ] );
    for( int x = 0; x < MaxSensors; x++ ) {
      if( SensorReadings[ x ] != -1000 ) {
        WeatherClient.print( "/" );
        WeatherClient.print( SensorNames[ x ] );
        WeatherClient.print( "/" );
        WeatherClient.print( SensorReadings[ x ] );
      }
    }
    WeatherClient.print( "/ver/" );
    WeatherClient.print( WeatherVersion );
    WeatherClient.print( "/type/" );
    WeatherClient.print( WeatherType );
    WeatherClient.println( " HTTP/1.1" );
    WeatherClient.print( "Host: " );
    WeatherClient.println( WeatherServer );
    WeatherClient.println( "User-agent: Photon" );
    WeatherClient.println( "Connection: close" );
    WeatherClient.println();
  }
  PostTime = millis() + PostDelay;
  LastTime = Time.timeStr();
  WeatherClient.flush();
  WeatherClient.stop();
}
// End PostToWeathercloud

// Begin StatusLED
int Status = 5;
bool StatusUp = true;
unsigned int StatusTime = 0;
unsigned int StatusDelay = 500;
void UpdateStatus(){
  if( millis() > StatusTime ){
    if( StatusUp ){
      Status ++;
      if( Status > 5 ){
        Status = Status - 1;
        StatusUp = false;
      }
    } else {
      Status = Status - 1;
      if( Status < 2 ){
        StatusUp = true;
      }
    }
    if( Networked ){
      RGB.control( true );
      RGB.color( 0, Status, 0 );
    } else {
      RGB.control( true );
      RGB.color( Status, 0, 0 );
    }
    StatusTime = millis() + StatusDelay;
  }
}
// End StatusLED