Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
SoilMonitor.ino for Particle Photon
// This #include statement was automatically added by the Particle IDE.
#include "AzureMobileService/AzureMobileService.h"
// This #include statement was automatically added by the Particle IDE.
#include "SparkFun_Photon_Weather_Shield_Library/SparkFun_Photon_Weather_Shield_Library.h"
//Azure Mobile Service Configuration
#define MYSERVICE "soilmonitor"
AzureMobileService ams;
const char* table = "Data";
//SoilMonitor Zone Configuration and ADC_MAX_RES
const char *LOCATION_NAME = "Paul's Backyard";
const int NUMBER_OF_ZONES = 6;
//Zone Names may not containt whitespace
const char *ZONE_NAMES[NUMBER_OF_ZONES] = {"Orange", "Lemon", "Yard", "Bottlebrush", "Fig", "Peach"};
const float ADC_MAX_RES = 4096; //Particle Core | Particle Photon use 12-bit Resolution ADCs (2^12 = 4096)
float currentSaturation = -1;
float saturationData[NUMBER_OF_ZONES];
float reading = 0;
//WeatherShield Configuration
float humidity = 0;
float tempf = 0;
float pascals = 0;
float baroTemp = 0;
//Create Instance of HTU21D or SI7021 temp and humidity sensor and MPL3115A2 barrometric sensor
Weather sensor;
//Put Device to Sleep when not reporting data
int ReportAndSleepIntervalInSeconds = 1800; //Default 1800 seconds equates to 2 reads per hour
//The maximum message size to send to Azure Mobile Services
//Careful with the payload size, if you raise this too high, you may experience a 'red light' fault on the Particle device
const int MaxAzurePayloadSize = 500;
void setup() {
//Using D7 to drive current, we can rest sensors during sleep, this is a hack but gets the job done ;)
pinMode(D7, OUTPUT);
pinMode(A0, INPUT);
pinMode(A1, INPUT);
pinMode(A2, INPUT);
pinMode(A3, INPUT);
pinMode(A4, INPUT);
pinMode(A5, INPUT);
ams.configure(MYSERVICE, MYKEY);
//Initialize the I2C sensors and ping them
/*You can only receive acurate barrometric readings or acurate altitiude
readings at a given time, not both at the same time. The following two lines
tell the sensor what mode to use. You could easily write a function that
takes a reading in one made and then switches to the other mode to grab that
reading, resulting in data that contains both acurate altitude and barrometric
readings. For this example, we will only be using the barometer mode. Be sure
to only uncomment one line at a time. */
sensor.setModeBarometer();//Set to Barometer Mode
//baro.setModeAltimeter();//Set to altimeter Mode
//These are additional MPL3115A2 functions the MUST be called for the sensor to work.
sensor.setOversampleRate(7); // Set Oversample rate
//Call with a rate from 0 to 7. See page 33 for table of ratios.
//Sets the over sample rate. Datasheet calls for 128 but you can set it
//from 1 to 128 samples. The higher the oversample rate the greater
//the time between data samples.
sensor.enableEventFlags(); //Necessary register calls to enble temp, baro ansd alt
void loop() {
//Turn Saturation Sensors On
digitalWrite(D7, HIGH);
//Turn Saturation Sensors Off
digitalWrite(D7, LOW);
Serial.println(" Begin Sleep ");
System.sleep(SLEEP_MODE_DEEP, ReportAndSleepIntervalInSeconds);
void ReadSaturationSensors() {
Serial.println(" Begin Saturation Data");
for(int i = 0; i < NUMBER_OF_ZONES; i++)
reading = analogRead(i);
currentSaturation = ((reading/ADC_MAX_RES - 1) * -1);
saturationData[i] = currentSaturation;
Serial.print(": ");
void ReadWeatherSensors() {
// Measure Relative Humidity from the HTU21D or Si7021
humidity = sensor.getRH();
// Measure Temperature from the HTU21D or Si7021
tempf = sensor.getTempF();
// Temperature is measured every time RH is requested.
// It is faster, therefore, to read it from previous RH
// measurement with getTemp() instead with readTemp()
//Measure the Barometer temperature in F from the MPL3115A2
baroTemp = sensor.readBaroTempF();
//Measure Pressure from the MPL3115A2
pascals = ((sensor.readPressure()/100) * 0.0295300);
//If in altitude mode, you can get a reading in feet with this line:
//float altf = sensor.readAltitudeFt();
Serial.println(" Begin Weather Data");
Serial.println("F ");
Serial.println("% ");
Serial.println("F ");
//The MPL3115A2 outputs the pressure in Pascals. However, most weather stations
//report pressure in hectopascals or millibars. Divide by 100 to get a reading
//more closely resembling what online weather reports may say in hPa or mb.
//Another common unit for pressure is Inches of Mercury (in.Hg). To convert
//from mb to in.Hg, use the following formula. P(inHg) = 0.0295300 * P(mb)
//More info on conversion can be found here:
void PublishData() {
Serial.println(" Begin Publish To Azure");
char *payload = (char *) malloc(MaxAzurePayloadSize);
snprintf(payload, MaxAzurePayloadSize, "{ \"Location\":\"%s\",", LOCATION_NAME);
for(int i = 0; i < NUMBER_OF_ZONES; i++)
//Old method for sending each Zone as a row, retained as example
//snprintf(payload, MaxAzurePayloadSize, "{ \"Location\":\"%s\", \"Zone\":\"%s\", \"Saturation\":%f,\"Humidity\":%f,\"TempF\":%f,\"BaroTemp\":%f,\"Pascals\":%f }", LOCATION_NAME, ZONE_NAMES[i], saturationData[i], humidity, tempf, baroTemp, pascals);
char currentZoneData[50];
snprintf(currentZoneData, 50, " \"%s\":%f,", ZONE_NAMES[i], saturationData[i]);
strcat(payload, currentZoneData);
char weatherData[100];
snprintf(weatherData, 100, "\"Humidity\":%f,\"TempF\":%f,\"BaroTemp\":%f,\"Pascals\":%f }", humidity, tempf, baroTemp, pascals);
int response = ams.create(table, payload);
if(response != 201)
Serial.print("Expected Response Code 201, Received Code: ");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.