It is a simple IoT solution for plant life monitoring and maintenance, based on STM32NUCLEO boards and expansion modules.

Dependencies:   BLE_API X_NUCLEO_IDB0XA1 X_NUCLEO_IKS01A1 mbed

GreenYourLife, A Plant Life Monitoring and Maintenance System

Elaborated By Engineers, Designed For The World

Introduction

A healthy ecosystem is one where all the actors in it work in synergy: each of them playing a part that serves the other. Plants play an important part in regulating our quality of life. In the modern world, they would often times be placed in buildings and houses, as decorations or as medicinal and therapeutic purposes. Whatever the reason, GreenYourLife is here to help you manage them.

GreenYourLife is an Internet of Things (IoT) solution that will allow you to perform the following:

  • Follow your plant's well-being on your connected device (smartphone, etc.)
  • Maintain your plant's well-being automatically

We've developed an indoor plant monitoring system with the STM32NUCLEO development board. The system will collect the plant's environmental data (air temperature, air humidity and soil moisture) and transfer them to your connected device. The data collected will then be stored on a cloud server, where you can follow the progress of your plant life.

How it works

Plant maintanence

Our concept of maintanence is the automatic catering of the plant's needs. In this version, we focus on water supply. Future versions may indeed include more than that.

On-board sensors on the STM32NUCLEO will obtain measurements of 3 main parameters on the plant surrounding:

  1. Air temperature
  2. Air humidity
  3. Soil moisture

Based on these values, the software then acts upon the water pump: activating it when in need of water and deactivating it if not.

Plant monitoring

Plant monitoring, for us, is the follow-up of the plant's health by monitoring environment quality and plant nutritions. In this version, we only monitor the three parameters listed above. Future version may include more advanced and smarter monitoring technique.

The STM32NUCLEO device acts as a Bluetooth server and awaits connection from any client (smartphone, etc.). Upon connection, the server updates the plant environment parameters at a fixed interval. The client application will then transfer these received characteristics to the cloud.

Details

Hardware

The target platform is the STM32NUCLEO L476RG development board with two additional modules:

  • the IDB05A1 Bluetooth LE expansion board, which uses a BlueNRG coprocessor
  • and the IKS01A1 MEMS sensor expansion board

There are several additional materials that constitute the system:

  • a Funduino Soil Moisture Sensor, hooked up to 5V at pin PB_1
  • a relay, hooked up to 3.3V at pin PC_8 and controlled by a PWM pulse
  • a DC water pump

Software

We have used the ARM mbed platform for general code bring-up. We've used the following libraries:

  • mbed library by ARM
  • BLE_API library by the Bluetooth Low Energy team
  • X_NUCLEO_IDB0XA1 library by the ST Microelectronics team
  • X_NUCLEO_IKS01A1 library by the ST Microelectronics team

The acquired data on the client device is then sent to a cloud server that we hosted on ThingSpeak, so that people can remotely access the data. It is available at the GreenYourLife channel .

Reference

main.cpp

Committer:
kaiserhaz
Date:
2016-12-05
Revision:
5:b98f4f7cee5a
Parent:
4:e5550110184d

File content as of revision 5:b98f4f7cee5a:

/************************ STM32NUCLEO IOT Contest ******************************
 *
 *                   Green Building IoT Solution for
 *                Plant Life Monitoring And Maintenance
 *
 *                           Authored by
 *                        Dien Hoa Truong
 *                 Muhammad Haziq Bin Kamarul Azman
 *                        
 *                            for the
 *            eSAME 2016 STM32NUCLEO IoT Contest in Sophia-Antipolis
 *
 *  main.cpp | Program main
 *
 *  See LICENCE.txt for information on copyrights
 *
 ******************************************************************************/

/** Includes **/
#include "mbed.h"                                                               // ARM mbed               library
#include "x_nucleo_iks01a1.h"                                                   // STM32NUCLEO board      library
#include "ble/BLE.h"                                                            // Bluetooth LE           library
#include "GreenBuildingService.h"                                               // Green Building service library



/** Defines **/
#define GB_SOIL_MOISTURE_MAX 70                                                 // Soil moisture threshold value



/** Device declarations **/

// Board-specific
PwmOut pumpPWM(PC_8);                                                           // PWM motor control out pin
DigitalOut led1(LED1, 1);                                                       // Debug pin instance
AnalogIn moisture_sensor(PB_1);                                                 // Moisture sensor
static X_NUCLEO_IKS01A1 *mems_expansion_board = X_NUCLEO_IKS01A1::Instance(D14, D15); // Expansion board instance
static HumiditySensor   *humidity_sensor = mems_expansion_board->ht_sensor;     // Expansion board humidity sensor instance
static TempSensor       *temp_sensor     = mems_expansion_board->ht_sensor;     // Expansion board temperature sensor instance

// BLE-specific
BLE&                  ble = BLE::Instance(BLE::DEFAULT_INSTANCE);               // BLE device instance
const static char     DEVICE_NAME[] = "GB-Sensor";                              // Device name
static const uint16_t uuid16_list[] = {GreenBuildingService::UUID_GREEN_BUILDING_SERVICE};
GreenBuildingService *gbServicePtr;                                             // Service pointer

// Program-specific
float getMoistureValue();
float getHumidityValue();
float getTemperatureValue();
void errorLoop(void);
void activateFastSensorPoll();
void deactivateFastSensorPoll();
void pumpActivateCallback(void);
void pumpDeactivateCallback(void);

Ticker sanityTicker;
Ticker sensorPollTicker;
Ticker fastSensorPollTicker;
Timeout pumpWaitTimeout;
uint8_t usersConnected;
bool sensorPolling;
bool fastSensorPolling;
bool pumpActivate;
bool waitOnce;
bool bleActive;
bool pumpActive;


/** Callbacks **/

// BLE-specific callback
void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)    // Callback for everytime the connection gets disconnected
{
    ble.gap().startAdvertising();                                               // Restart advertising
    if((!pumpActive)||(!usersConnected))
        deactivateFastSensorPoll();
    bleActive = false;
    --usersConnected;
//    printf("\r\n> BLE  : Disconnected. Advertising restarted.");
}

void connectionCallback(const Gap::ConnectionCallbackParams_t *params)          // Callback for everytime the connection is established
{
    ble.gap().stopAdvertising();                                                // Stop advertising
    activateFastSensorPoll();
    bleActive = true;
    ++usersConnected;
//    printf("\r\n> BLE  : Connected to %x. Accept no subsequent connections.", params->peerAddr);
}

void onBleInitError(BLE &ble, ble_error_t error)
{
//    printf("\r\n> BLE  : Init error encountered. Error returned: %d", error);
    errorLoop();
}

void bleInitComplete(BLE::InitializationCompleteCallbackContext *params)
{
    BLE&        ble   = params->ble;
    ble_error_t error = params->error;

    if (error != BLE_ERROR_NONE) {                                              // Check to see init errors
        onBleInitError(ble, error);
        errorLoop();
    }

    if (ble.getInstanceID() != BLE::DEFAULT_INSTANCE) {                         // If this is not default instance (double instanciation?)
//        printf("\r\n> BLE  : BLE controller instance is invalid.");
        errorLoop();
    }
    
    ble.gap().onDisconnection(disconnectionCallback);                           // Register disconnection callback
    ble.gap().onConnection(connectionCallback);                                 // Register connection callback

    gbServicePtr = new GreenBuildingService(ble);                               // Init service with initial value
    
    /* Setup advertising. */
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS,(uint8_t *)uuid16_list, sizeof(uuid16_list));
    ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
    ble.gap().setAdvertisingInterval(1000); /* 1000ms */
    ble.gap().startAdvertising();
    
//    printf("\r\n> BLE  : BLE Init done.");
}

// Helper functions for retrieving data from sensors
float getMoistureValue()
{
    float moisture = 0;
    for (int i = 1;i<=10;i++) {
        moisture += moisture_sensor.read();                                     // Get ten samples
    }
    moisture = moisture / 10;
    moisture = moisture * 3300;                                                 // Change the value to be in the 0 to 3300 range
    moisture = moisture / 33;                                                   // Convert to percentage
    return moisture;
}

float getHumidityValue()
{
    float humidity = 0;
    humidity_sensor->GetHumidity(&humidity);
    return humidity;
}

float getTemperatureValue()
{
    float temperature = 0;
    temp_sensor->GetTemperature(&temperature);
    return temperature;
}


// Miscellaneous callbacks & functions
void sanityCallback(void)
{
    led1 = !led1;                                                               // Blink LED1 to indicate system sanity
}

void sensorPollCallback(void)
{
    sensorPolling = true;
}

void fastSensorPollCallback(void)
{
    fastSensorPolling = true;
}

void pumpActivateCallback(void)
{
    pumpActivate = true;
}

void pumpDeactivateCallback(void)
{
    pumpActivate = false;
}

void activateFastSensorPoll(void)
{
    fastSensorPolling = true;
    fastSensorPollTicker.attach(&fastSensorPollCallback, 0.9);
}

void deactivateFastSensorPoll(void)
{
    fastSensorPolling = false;
    fastSensorPollTicker.detach();
}


void errorLoop(void)
{
    sanityTicker.detach();
    sensorPollTicker.detach();
    ble.shutdown();
//    printf("\r\n> ERROR : Error encountered. Infinite looping.");
    while(true)
    {
        led1 != led1;        
    }
}



/** Pre-main inits **/



/** Main loop **/
int main(void)
{
    pumpPWM.write(1);
    pumpPWM.period(1.0f);
    
    printf("\r\n/**\r\n * Green Building Sensor Device: Debug Info\r\n */");
    
    sensorPolling = false;
    fastSensorPolling = false;
    pumpActivate = false;
    waitOnce = true;
    bleActive = false;
    pumpActive = false;
    
    sanityTicker.attach(sanityCallback, 1.1);                                   // LED sanity checker
    sensorPollTicker.attach(sensorPollCallback, 4.9);                           // Sensor poll ticker
    
    printf("\r\n> MAIN : Tickers initialized.");
 
    volatile GreenBuildingService::PlantEnvironmentType_t peVal;                // Plant environment var
    uint8_t pumpWaitTime = 3;                                                   // Pump waiting time
 
    ble.init(bleInitComplete);                                                  // Pass BLE init complete function upon init
    
//    while(ble.hasInitialized() == false);
    
    printf("\r\n> MAIN : BLE Init procedure done.");
    
    // Infinite loop
    while (true) {
        
        if(sensorPolling || fastSensorPolling)
        {
            sensorPolling = false;                                              // Deassert polling bit
            fastSensorPolling = false;
            
            peVal.soilMoisture   = (uint8_t) getMoistureValue();                // Update all measurements
            peVal.airHumidity    = (uint8_t) getHumidityValue();
            peVal.airTemperature = (int8_t)  getTemperatureValue();
            
            if(ble.getGapState().connected)                                     // Update characteristic if connected
                gbServicePtr->updatePlantEnvironment(peVal);
            
//            printf("\r\n> MAIN : Current soil moisture    = %d", peVal.soilMoisture);
//            printf("\r\n> MAIN : Current air humidity     = %d", peVal.airHumidity);
//            printf("\r\n> MAIN : Current air temperature  = %d", peVal.airTemperature);
            printf("%d\t%d\t%d\r\n", peVal.airTemperature, peVal.airHumidity, peVal.soilMoisture);
            
            // If moisture is below 50% of max when user is present
            //    or if less than 30% of max
            if( ( ((peVal.soilMoisture < 0.5*GB_SOIL_MOISTURE_MAX) &&  ble.getGapState().connected)   ||
                  ((peVal.soilMoisture < 0.3*GB_SOIL_MOISTURE_MAX) && !ble.getGapState().connected) ) &&
                waitOnce
            )
            {
                pumpWaitTimeout.attach(&pumpActivateCallback, pumpWaitTime);    // Waiting time is hard coded but may be calculated, I think
                activateFastSensorPoll();
                waitOnce = false;
                pumpActive = true;
            }
            else if((peVal.soilMoisture >= 0.6*GB_SOIL_MOISTURE_MAX) && pumpActivate) // Stop condition: when soil moisture is at 60% of max
            {
                pumpPWM.write(1);
                pumpWaitTimeout.detach();
                pumpDeactivateCallback();
                if(!bleActive)
                    deactivateFastSensorPoll();
                waitOnce = true;
                pumpActive = false;
            }
            
            if(pumpActivate)
            {
//                printf("\r\n> MAIN : Activating water pump.");
                pumpPWM.write(0.7);                
                pumpActivate = false;
                pumpWaitTimeout.attach(&pumpActivateCallback, 1);
            }
            
        }
        else
            ble.waitForEvent();                                                 //Low power wait for event   
        
    }
}