Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
|
This project originated from a class IoT project assigned by our instructor, Brian Rashap. The first component for development assigned by Brian was the red Flower Pot, a Fusion 360 software product. The pot was 3-D printed using Cura software and printed on an Ultimaker III printer. With the pot created, Brian formally assigned this project on August 8.This project includes several IoT devices previously studied in our bootcamp, including:
* Particle Argon Microcontroller
* I2C OLED
* Capacitive Soil Moisture Probe
* BME280 sensor
* Relay switch (connected to 5V power supply)
* motorized pump
* Grove Seeed dust sensor
The microcontroller drives the BME and soil moisture probe. Brian's specifications for our C++ driver program featured:
* collecting data from the BME and capacitive sensor, including moisture, humidity and temperature
* displaying data on programmed intervals to the OLED
* setting up an Adafruit account for creating separate feeds to publish and display these data on regular intervals
* providing a programmed moisture threshold to automatically water the plant to maintain proper soil moisture
* providing a dashboard button to manually water the plant as needed (using 1/2 sec intervals).
/*
* Project L14_04_PlantWater
* Description:
* This program uses the Capacitive Soil Moisture probe to take moisture readings from the
* BME280 sensor humidity readings. Use an analog read to obtain moisture readings from the sensor.
* Record to my lab notebook note the moisture readings for these condions:
* 1) Empty Cup
* 2) Submerged in water to the notch
* 3) Dry Soil
* 4) Soil after plant is watered
* In the prgram, display each moisture reading to the OLED with a Time-stamp:
* Time. Also, integrate a button to manually water the plant.
* 99:99:99
* Moisture
* 99.99
* Author: Ted Fites
* Date: 8/11/20
*/
// HEADER section ********************************************************
// include lines 11-13 & 18 below from Adafruit library header file (lines 19-23). Need to only install
// Adafruit_SSD1306 header file for each program referencing it (it already includes Adafruit_GFX.h).
#include "Adafruit_GFX.h"
#include "Adafruit_SSD1306.h"
#include "Adafruit_BME280.h"
// Use line #26-30 for subsequent programs just as is:
#include <Adafruit_MQTT.h>
#include "Adafruit_MQTT/Adafruit_MQTT.h"
#include "Adafruit_MQTT/Adafruit_MQTT_SPARK.h"
#include "Adafruit_MQTT/Adafruit_MQTT.h"
/************************* Adafruit.io Setup *********************************/
#define AIO_SERVER "io.adafruit.com"
#define AIO_SERVERPORT 1883 // use 8883 for SSL
#define AIO_USERNAME "Ted_Fites"
#define OLED_RESET D4
Adafruit_SSD1306 display(OLED_RESET); // Declare object from lib class supplying OLED 128x64 pixel display access
// Define bme as the I2C interface to the BME280 sensor
Adafruit_BME280 bme; // Declare object for BME sensor
TCPClient TheClient;
// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.
Adafruit_MQTT_SPARK mqtt(&TheClient,AIO_SERVER,AIO_SERVERPORT,AIO_USERNAME,AIO_KEY);
/****************************** Feeds ***************************************/
// Setup Feeds to publish or subscribe
// Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname>
Adafruit_MQTT_Subscribe subMoistureBtn = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/L14_04_PlantBtnFeed");
Adafruit_MQTT_Publish pubMoisture = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/L14_04_Moisture");
Adafruit_MQTT_Publish pubHumidity = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/L14_04_Humidity");
Adafruit_MQTT_Publish pubTemp = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/L14_04_Temperature");
Adafruit_MQTT_Publish pubDust = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/L14_04_Dust");
// Constants & variables
bool status;
char currentDateTime[25];
char currTime[9];
const int pinA5=A5;
const int PIN_VBUS_5V=D7; //Provides 5V power input to relay switch (and pump motor) from Argon pin D7
const int moistureThreshold=1770; // Water pump threshold set by L14_02_SoilMoisture program
float humBME;
float tempBME;
int moistureVal=0;
int btnMoistureVal;
String dateTime;
String timeOnly;
unsigned long int last;
// FM07: Dust sensor variables
const int DUST_SENSOR_INPIN=A3;
unsigned long duration;
unsigned long starttime;
unsigned long sampletime_ms = 2000;
unsigned long lowpulseoccupancy = 0;
float ratio = 0.0;
float concentration = 0.0;
// FM07: END Dust sensor variables
// end HEADER section *******************************************************************************************
//SYSTEM_MODE(SEMI_AUTOMATIC); // Allows project flashing while Argon is disconnected from any wifi internet
void setup()
{
Serial.begin(9600);
// 1) Adafruit OLED: include lines 24-25 below from Adafruit library header file (lines 62-63)
//
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3C (for the 128x64)
// init done
display.display(); // show Adafruit splashscreen to OLED
delay(2000); // maintain display for 2 seconds
/* Connect the Particle Argon to the Particle Cloud to synchronize its
* clock to the current local time.
*/
Serial.printf("Lesson 14_04_PlantWater\n");
Time.zone(-6); // Set Time Zone to MDT (UTC -7)
// unsigned long int cur= millis ();
// Request time synch from Particle Cloud
Particle.syncTime();
waitUntil (Particle.syncTimeDone);
pinMode(pinA5,INPUT); // Declare the Capacitive
/*
* 2) BME 280sensor : Use method .begin for bme object to turn on sensor
*/
while(!Serial); // time to get serial running
delay (1000);
Serial.println(F("BME280 test")); // "F" in println statement: Reference to FLASH memory on SD card)
status = bme.begin(0x76);
if (!status) {
Serial.println("Could not find a valid BME280 sensor, check wiring, address, sensor ID!");
Serial.print("SensorID was: 0x"); Serial.println(bme.sensorID(),HEX);
Serial.println(" ID of 0xFF probably means a bad address, a BMP 180 or BMP 085");
Serial.println(" ID of 0x56-0x58 represents a BMP 280,");
Serial.println(" ID of 0x60 represents a BME 280.");
Serial.println(" ID of 0x61 represents a BME 680.");
while (1);
}
else {
Serial.println("BME280 Up and Running");
}
// Setup MQTT subscription for onoff feed.
//mqtt.subscribe(&TempF);
// READ data from the dashboard "L14_04_PlantWater" button to light up Argon LED D7
mqtt.subscribe(&subMoistureBtn);
pinMode(PIN_VBUS_5V,OUTPUT); // Argon on-board LED to light up when pressing DB button
pinMode(DUST_SENSOR_INPIN,INPUT); // Declare dust sensor pin
starttime = millis(); // Set dust sensor base sample start time
}
void loop()
//************************************************************************************
//************************************* MAIN LOOP **********************************
//************************************************************************************
{
M01_MQTT_connect(); //FM01
// Keep connection alive between publishings
// Stop if already connected.
Serial.print("Connecting to MQTT... ");
if ((millis()-last)>120000) {
Serial.printf("Pinging MQTT \n");
if(! mqtt.ping()) {
Serial.printf("Disconnecting \n");
mqtt.disconnect();
}
last = millis();
}
M02_Subscribe_Moisture_Button(); // FM02-Set up button on L14_04_WaterPlant DB to manually activate pump
M03_get_Curr_Time(); //FM03
M04_get_Moisture_BME_data(); //FM04
M05_display_Screen_Info(); //FM05
M07_Activate_Dust_Sensor(); //FM07
M08_Publish_Values(); //FM08
if (moistureVal>=moistureThreshold)
/*
* AUTOMATIC (PROGRAMMED) CHECK TO ACTIVATE PUMP:If the moisture level (dryness) exceeds
* the threshold level, then activate the pump
* to add water to the pot. Otherwise write LOW to pin D7 to deactivate the pump.
*/
{
Serial.printf("***PUMP ACTIVATED***\n");
digitalWrite(PIN_VBUS_5V,HIGH);
delay(500); // Activate the pump for 1/2 sec (per BR, isolated delay() OK to use)
digitalWrite(PIN_VBUS_5V,LOW);
}
} // end MAIN loop ********************************************************************
/* FM01: MQTT_connect()
*Function to connect and reconnect as necessary to the MQTT server.
*Should be called in the loop function and it will take care if connecting.
*/
void M01_MQTT_connect()
{
int8_t ret;
// Stop if already connected.
if (mqtt.connected())
{
return;
}
Serial.print("Connecting to MQTT... ");
while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
Serial.println(mqtt.connectErrorString(ret));
Serial.println("Retrying MQTT connection in 5 seconds...");
mqtt.disconnect();
delay(5000); // wait 5 seconds
}
Serial.println("MQTT Connected!");
}
void M02_Subscribe_Moisture_Button()
// FM02: Subscribe_Moisture_Button
{
// this is our 'wait for incoming subscription packets' busy subloop
// try to spend your time here
// read (subscribe) the data from the dashboard button every 2 seconds
Adafruit_MQTT_Subscribe *subscription;
while ((subscription = mqtt.readSubscription(2000)))
{
if (subscription == &subMoistureBtn)
{
// moistureVal = atof((char *)subMoistureBtn.lastread);
btnMoistureVal = atoi((char *)subMoistureBtn.lastread);
Serial.printf("BTNmoistureVal > %i\n",btnMoistureVal);
digitalWrite(PIN_VBUS_5V,btnMoistureVal);
}
}
return;
} // end SubscribeMoistureButton
void M03_get_Curr_Time()
// FM03: get_Curr_Time()
{
dateTime=Time.timeStr();
timeOnly=dateTime.substring(11,19);
timeOnly.toCharArray(currTime,9);
return;
} // end get_Curr_Time()
void M04_get_Moisture_BME_data()
// FM04: get_Moisture_BME_data()
{
moistureVal=analogRead(pinA5);
Serial.printf("get_Moisture_BME_data MOISTURE > %i\n",moistureVal);
tempBME = (bme.readTemperature() * 9.0 / 5.0) + 32.0;
humBME = bme.readHumidity();
return;
} // end get_Moisture_BME_data
void M05_display_Screen_Info()
//FM05: display_Screen_Info()
{
Serial.printf("Current time & Moisture> %s %i \n",currTime, moistureVal);
display.clearDisplay(); // clears the screen and buffer
display.setTextSize(1); // Double from 1:1 size for improved readability
display.setCursor(0,0);
display.setTextColor(WHITE); // Draw white text
display.printf("Time: ");
display.printf("%s\n",currTime);
// display.setTextColor(WHITE); // Draw white text
display.printf("Moisture: ");
display.printf("%i\n",moistureVal);
display.printf("Humidity: ");
display.printf("%5.2f\n",humBME);
display.printf("Temperature: ");
display.printf("%5.2f\n",tempBME);
display.display();
delay(5000); // re-display data to the OLED every 5 seconds
return;
} //end display_Screen_Info()
void M07_Activate_Dust_Sensor()
// FM07: Activate_Dust_Sensor()
{
duration = pulseIn(DUST_SENSOR_INPIN, LOW);
lowpulseoccupancy = lowpulseoccupancy+duration;
if ((millis()-starttime) >= sampletime_ms) //if the time exceeds 30ms, take a sample
{
ratio = lowpulseoccupancy/(sampletime_ms*10.0);
concentration = 1.1*pow(ratio,3)-3.8*pow(ratio,2)+520*ratio+0.62;
Serial.printf("DUST SENSOR Concentration > %0.2f pcs/0.01cf \n",concentration);
pubDust.publish(concentration); // publish the DUST
Serial.printf("Publishing Dust (pcs/0.01cf) %0.2f \n",concentration);
lowpulseoccupancy = 0;
starttime = millis();
}
return;
} // END Activate_Dust_Sensor()
void M08_Publish_Values()
//FM08: Publish_Values()
{
if((millis()-last > 10000)) // 10 secs
{
if(mqtt.Update()) {
pubMoisture.publish(moistureVal); // publish the MOISTURE
Serial.printf("Publishing Moisture %i \n",moistureVal);
pubHumidity.publish(humBME); // publish the HUMIDITY
Serial.printf("Publishing Humidity %5.2f \n",humBME);
pubTemp.publish(tempBME); // publish the TEMPERATURE
Serial.printf("Publishing Temp %5.2f \n",tempBME);
}
last = millis();
return;
}
} // end Publish_Values()
Comments