Yes, code! I just wanted to get this rolling last night…
//
// Rain Drop Sensor + Capacitive Soil Moisture Sensor v1.2 + SHT31 Temperature & Humidity Sensor
// Purpose: Rain Drop Sensor - Analog sensor can detect how much rain is falling, digital sensor can be calibrated with pot to trigger event at certain wetness level, also triggers led on device.
// Capacitive Soil Moisture Sensor v1.2 - Measures soil moisture
// https://build.particle.io/build/5d2c7c483275db000597f6ab# SHT31 Temperature & Humidity Sensor - I2C sensor that measures both temperature in Celcius and humidity
// Author: David Cool
// Version: 1.0
//
STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));
#include <adafruit-sht31.h>
#include <SparkFunMAX17043.h>
//#include <google-maps-device-locator.h>
// Setup SHT31 object
Adafruit_SHT31 sht31 = Adafruit_SHT31();
// variables for MAX17043 fuel guage
double voltage = 0; // Variable to keep track of LiPo voltage
double soc = 0; // Variable to keep track of LiPo state-of-charge (SOC)
int alert = 0; // Variable to keep track of whether alert has been triggered
// Setup Google Maps Device Locator Object
//GoogleMapsDeviceLocator locator;
//#define RESOLUTION 1024 //Use for 10 bit resolution ADC (Arduino)
#define RESOLUTION 4096 //Use for 12 bit resolution ADC (Particle Photon)
// define rain drop sensor variables
const int RAIN_DROP_ANALOG_PIN = A0;
const int RAIN_DROP_DIGITAL_PIN = D2;
int analogdropLevel;
int digitaldropLevel;
// define soil moisture sensor pin
const int SOIL_SENSOR_PIN = A1;
int soilMoisture;
// define SHT31 temperature & humidity variables
float tc;
float tf;
float h;
// define dew point and humidex variables
double dpc;
double dpf;
double hdc;
double hdf;
// define variables for Google geolocation data
float glat;
float glon;
float gaccuracy;
// used to store device name
char dev_name[32] = "";
bool publishName = false;
unsigned long firstAvailable = 0;
int counter;
retained int retainedCounter = 0;
void handler(const char *topic, const char *data) {
strncpy(dev_name, data, sizeof(dev_name)-1);
Serial.printlnf("received %s: %s", topic, dev_name);
publishName = true;
}
// dewPoint function NOAA
// reference (1) : https://wahiduddin.net/calc/density_algorithms.htm
// reference (2) : https://www.colorado.edu/geography/weather_station/Geog_site/about.htm
//
double dewPoint(double celsius, double humidity)
{
// (1) Saturation Vapor Pressure = ESGG(T)
double RATIO = 373.15 / (273.15 + celsius);
double RHS = -7.90298 * (RATIO - 1);
RHS += 5.02808 * log10(RATIO);
RHS += -1.3816e-7 * (pow(10, (11.344 * (1 - 1/RATIO ))) - 1) ;
RHS += 8.1328e-3 * (pow(10, (-3.49149 * (RATIO - 1))) - 1) ;
RHS += log10(1013.246);
// factor -3 is to adjust units - Vapor Pressure SVP * humidity
double VP = pow(10, RHS - 3) * humidity;
// (2) DEWPOINT = F(Vapor Pressure)
double T = log(VP/0.61078); // temp var
return (241.88 * T) / (17.558 - T);
}
/*
// humidex c calculation using dew point
double humidexc(double tc, double dp)
{
double e = 5417.7530*((1/273.16)-(1/(273.16 + dp)));
double h = tc + 0.5555 * ( 6.11 * exp (e) - 10);
return h;
}
*/
// humidex c calculation w/ relative humidity
double humidexc(double tc,double h) {
double e;
e = (6.112 * pow(10,(7.5 * tc/(237.7 + tc))) * h/100); //vapor pressure
double humidexc = tc + 0.55555555 * (e - 10.0); //humidex
return humidexc;
}
// source: https://www.weather.gov/epz/wxcalc_heatindex
// source: https://www.weather.gov/media/epz/wxcalc/heatIndex.pdf
double humidexf(double tf, double h) {
double humidexf = -42.379 + (2.04901523 * tf) + (10.14333127 * h) - (0.22475541 * tf * h) - (6.83783 * pow(10, -3) * pow(tf, 2)) - (5.481717 * pow(10, -2) * pow(h, 2)) + (1.22874 * pow(10, -3) * pow(tf, 2) * h) + (8.5282 * pow(10, -4) * tf * pow(h, 2)) - (1.99 * pow(10, -6) * pow(tf, 2) * pow(h, 2));
return humidexf;
}
/*
void locationCallback(float lat, float lon, float accuracy) {
// Handle the returned location data for the device. This method is passed three arguments:
// - Latitude
// - Longitude
// - Accuracy of estimated location (in meters)
//String Coordinates;
//Coordinates = String::format("{\"lat\":%f,\"lon\":%f,\"accuracy\":%d}", lat, lon, accuracy);
//Serial.print("Sending Coordinates : ");
//Serial.println(Coordinates);
glat = lat;
glon = lon;
gaccuracy = accuracy;
}
*/
void setup()
{
// start serial monitor
Serial.begin(9600);
// have the google library tell your code what location was found
//locator.withSubscribe(locationCallback).withLocatePeriodic(600);
Serial.println("SHT31 Temperature & Humidity Sensor");
if (! sht31.begin(0x44)) { // Set to 0x45 for alternate i2c addr
Serial.println("Couldn't find SHT31");
while (1) delay(1);
}
// setup A0 and D0 as input pins
pinMode(RAIN_DROP_ANALOG_PIN,INPUT);
pinMode(RAIN_DROP_DIGITAL_PIN,INPUT);
pinMode(SOIL_SENSOR_PIN,INPUT);
// Set up Spark variables (voltage, soc, and alert):
//Particle.variable("voltage", voltage);
//Particle.variable("soc", soc);
//Particle.variable("alert", alert);
// To read the values from a browser, go to:
// http://api.particle.io/v1/devices/{DEVICE_ID}/{VARIABLE}?access_token={ACCESS_TOKEN}
// Set up the MAX17043 LiPo fuel gauge:
lipo.begin(); // Initialize the MAX17043 LiPo fuel gauge
// Quick start restarts the MAX17043 in hopes of getting a more accurate
// guess for the SOC.
lipo.quickStart();
// We can set an interrupt to alert when the battery SoC gets too low.
// We can alert at anywhere between 1% - 32%:
lipo.setThreshold(10); // Set alert threshold to 10%.
// get device name from cloud
Particle.subscribe("particle/device/name", handler);
Particle.publish("particle/device/name"); // <-- ask the cloud for the name to be sent to you
}
void loop()
{
// Publish location to Google
//locator.loop();
bool wifiReady = WiFi.ready();
bool cloudReady = Particle.connected();
Serial.printlnf("wifi=%s cloud=%s counter=%d retainedCounter=%d", (wifiReady ? "on" : "off"), (cloudReady ? "on" : "off"),
counter++, retainedCounter++);
if (wifiReady && cloudReady) {
if (firstAvailable == 0) {
firstAvailable = millis();
}
if (millis() - firstAvailable > 60000) {
// After we've been up for 60 seconds, go to sleep. The delay is so the serial output gets written out before
// sleeping.
Serial.println("calling System.sleep(SLEEP_MODE_DEEP, 600)");
delay(2);
// SLEEP_MODE_DEEP timinig is in SECONDS, NOT microseconds!!!
System.sleep(SLEEP_MODE_DEEP, 120);
// The rest of the code here is not reached. SLEEP_MODE_DEEP causes the code execution to stop,
// and when wake up occurs, it's like a reset where you start again with setup(), all variables are
// cleared, etc.
Serial.println("returned from sleep, should not occur");
}
if (publishName) {
// Publish an event every ? minutes. The event is JSON formatted
// rain drop sensor
analogdropLevel = analogRead(RAIN_DROP_ANALOG_PIN);
digitaldropLevel = digitalRead(RAIN_DROP_DIGITAL_PIN);
// soil moisture sensor
soilMoisture = analogRead(SOIL_SENSOR_PIN);
// SHT31 temp & humidity sensor
tc = sht31.readTemperature();
tf = (tc * 9.0/5.0) + 32.0;
h = sht31.readHumidity();
// calculate dew points in C & F
dpc = dewPoint(tc,h);
dpf = ( dewPoint(tc,h) * 9.0/5.0 ) + 32.0;
// calculate humidex in C & F
hdc = humidexc(tc,h);
hdf = humidexf(tf,h);
// wake up the lipo fuel guage
//lipo.wake();
// lipo.getVoltage() returns a voltage value (e.g. 3.93)
voltage = lipo.getVoltage();
// lipo.getSOC() returns the estimated state of charge (e.g. 79%)
soc = lipo.getSOC();
// lipo.getAlert() returns a 0 or 1 (0=alert not triggered)
alert = lipo.getAlert();
// put lipo fuel guage to sleep
//lipo.sleep();
// Those variables will update to the Spark Cloud, but we'll also print them
// locally over serial for debugging:
/*
Serial.print("Voltage: ");
Serial.print(voltage); // Print the battery voltage
Serial.println(" V");
Serial.print("Alert: ");
Serial.println(alert);
Serial.print("Percentage: ");
Serial.print(soc); // Print the battery state of charge
Serial.println(" %");
Serial.println();
*/
char sensor_data[512];
snprintf(sensor_data, sizeof(sensor_data), "{\"analog_rain_drop_value\":\"%d\",\"digital_rain_drop_value\":\"%d\",\"soil_moisture\":\"%d\",\"C\":\"%.2f\",\"F\":\"%.2f\",\"humidity\":\"%.2f\",\"dew_point_celsius\":\"%.2lf\",\"dew_point_fahrenheit\":\"%.2lf\",\"humidex_celsius\":\"%.2lf\",\"humidex_fahrenheit\":\"%.2lf\",\"device_name\":\"%s\",\"lat\":\"%.15f\",\"lon\":\"%.15f\",\"accuracy\":\"%.2f\",\"voltage\":\"%.2lf\",\"soc\":\"%.2lf\",\"alert\":\"%d\"}", analogdropLevel, digitaldropLevel, soilMoisture, tc, tf, h, dpc, dpf, hdc, hdf, dev_name, glat, glon, gaccuracy, voltage, soc, alert);
Particle.publish("sensor_data", sensor_data, PRIVATE);
delay(1000); // to ensure adhering to rate limit
publishName = false;
Particle.publish("cool_sensors", sensor_data, PRIVATE);
delay(1000); // to ensure adhering to rate limit
publishName = false;
}
}
else {
firstAvailable = 0;
}
delay(1000);
}
This is a picture of how things are wired up: