Aeris Workshop program to read Moisture sensor and send the data to AerCloud using MQTT

Dependencies:   MQTT mbed-src

main.cpp

Committer:
agoel
Date:
2015-06-17
Revision:
3:db681a5cf46f
Parent:
2:08cbf9d1754f

File content as of revision 3:db681a5cf46f:

/*
     Dragon Fly Sample Program for MultiTech Connected Cooler Workshop - reading value from Alcohol and Moisture sesnor
*/

// AerCloud sample code for Dragon Fly Sample Program for MultiTech Connected Cooler - created for following hardware:
//   ST Nucleo F411RE   http://developer.mbed.org/platforms/ST-Nucleo-F411RE/
//   ST Sensor Board  http://developer.mbed.org/teams/ST-Americas-mbed-Team/wiki/Getting-Started-with-Nucleo-Sensors
//   MultiTech Socket Modem Shield: http://developer.mbed.org/components/Multi-Tech-SocketModem-Arduino-Shield-MT/
//   MultiTech http://developer.mbed.org/teams/Multi-Hackers/code/MTSAS_Cellular_HTTP_Example/
//  Details on AerCloud: http://www.aeris.com/technology/aercloud/
//  AerCloud Developer Forum: https://developer.aeris.com/
//
// Sample writes moistureValue & alcoholValue to an AerCloud container
//
// Dependencies:
//     mbed HTTP Client Libary
//     mbed MQTT Client Libary
//     DragonFly Support Library
//     mtsas Modem Library
//     mbed Library
//
// To get the sample running, you'll need to fill in the following parameters below
//   Your cellular provider's APN: APN
//   AerCloud API Key: AC_APIKEY
//   AerCloud Account ID: acId
//   AerCloud Container: AC_CONTAINER
//
// and you'll also need to create an AerCloud contianer with the schema
// described below
//

#include "mbed.h"
#include "mtsas.h"
#include "HTTPClient.h"
#include "PubSubClient.h"

//------------------------------------------------------------------------------------
// Connectivity Parameters
//------------------------------------------------------------------------------------

//! Aeris SIM does not use PIN
#define SIMPIN      NULL

//! The APN of your Aeris SIM
//    You can find the APN for your SIM at https://aerport.aeris.com
char _APN[] = "aer.aerisapn.net";

//! User name and password are not required to use Aeris APN
#define USERNAME    NULL

//! User name and password are not required to use Aeris APN
#define PASSWORD    NULL

// ------------------------------------------------------------------------------------
// AerCloud Paramers
// ------------------------------------------------------------------------------------

// AerCloud BASE URL
#define AC_BASE     "http://api.aercloud.aeris.com/v1"

//! AerCloud API Key
//   You can find your api key at https://aerport.aeris.com"
char _AERCLOUD_API_KEY[] = "<Set Your AerCloud Account API KEY>";          // ff767bee-f5bf-403b-badb-fde13bc2b756

//! Aercloud Account Id
//   You can find your account id at https://aerport.aeris.com
char _AERCLOUD_ACCOUNT_ID[] = "<Set Your AerCloud Account Id>";            // 10975

// AerCloud Container
//  The name of the AerCloud Container to write data into
char _AERCLOUD_CONTAINER[] = "AERIS_WORKSHOP_MAIN";
char _AERCLOUD_CONTAINER_ALARM[] = "AERIS_WORKSHOP_ALARM";

// AerCloud Device Id
//  The Device Id that is registered and provisioned at AerCloud 
//  (random value here program automatically detects and provision it)
char _AERCLOUD_DEVICE_ID[] = "*****-nucleo-0001-***********"; 
 
char _host[] = "mqtt.aercloud.aeris.com";
int _port = 1883;

//------------------------------------------------------------------------------------
// AerCloud SCL (Device) Data Model
// ------------------------------------------------------------------------------------
//
// Code assumes an AerCloud Data Model (AERIS_WORKSHOP_MAIN) with following fields
//
//  moistureSensorValue     FLOAT       Value read frrom moisture sensor
//  moistureProcessedValue  FLOAT       Processed value of moisture sensor (LOW / MEDIUM / HIGH)
//  alcoholSensorValue      FLOAT       Value read frrom alcohol sensor
//  alcoholProcessedValue   FLOAT       Processed value of alcohol sensor (LOW / MEDIUM / HIGH)
//  sclid                   INT         Device-id
//  timeStamp               LONG        timestamp when event occurred
//
// Code assumes an AerCloud Data Model (AERIS_WORKSHOP_ALARM) with following fields
//
//  alarmText           STRING  alarm string
//  timeStamp           LONG    time at which alarn has been generated
//  sclid               INT     Device-id
//
//------------------------------------------------------------------------------------


// Define alcoholSensor and moisture input PIN (Arduino connector namings A0 - PA_0, A1 - PA_1, A2 - PC_4, A3 - PB_0, A4 - PC_1, A5 - PC_0
AnalogIn moistureSensor(PC_2);      // A0 - PA_0
AnalogIn alcoholSensor(PB_0);       // A3

float moistureValue = 0.0f;
float alcoholValue = 0.0f;
float moistureProcessedValue = 0.0f;
float alcoholProcessedValue = 0.0f;

float moistureLastPeakValue = 0.0f;
float alcoholLastPeakValue = 0.0f;

// Enumeration for Range
#define LOW     1
#define MEDIUM  2
#define HIGH    3

char str[512];
char *simImei;

const char* HttpResultToString(HTTPResult r)
{
    switch(r) {
        case HTTP_PROCESSING:
            return "HTTP_PROCESSING";
        case HTTP_PARSE:
            return "HTTP_PARSE";
        case HTTP_DNS:
            return "HTTP_DNS";
        case HTTP_PRTCL:
            return "HTTP_PRTCL";
        case HTTP_NOTFOUND:
            return "HTTP_NOTFOUND - 404";
        case HTTP_REFUSED:
            return "HTTP_REFUSED - 403";
        case HTTP_ERROR:
            return "HTTP_ERROR";
        case HTTP_TIMEOUT:
            return "HTTP_TIMEOUT";
        case HTTP_CONN:
            return "HTTP_CONN";
        case HTTP_CLOSED:
            return "HTTP_CLOSED";
        case HTTP_OK:
            return "HTTP_OK";
    }
    return "UNKNOWN ERROR CODE";
}

void callback(char* topic, char* payload, unsigned int len) {
    logInfo("topic: [%s]\r\npayload: [%s]", topic, payload);
}

// Function to set RTC clock using the time received from Radio 
// This is required as the board does not have a clock so every time board gets reset the RTC clock gets reset too.
void set_current_time (Cellular* radio)
{
    string response = radio->sendCommand("AT+CCLK?", 1000);
    if (response.find("+CCLK: ") == string::npos) {
        printf("Error : Could not retrieve time \n\n\n");
    }
//    printf("Output from AT Command\n%s", response.c_str());

    char dateString [256];
    char timeString [256];
    int start = response.find(':');
    int stop = response.find(',', start);
    string signal = response.substr(start + 2, stop - start - 2);
    strcpy (dateString, signal.c_str());
    string signal2 = response.substr(stop+1);
    strcpy (timeString, signal2.c_str());
//    printf("***** Date String  ****** %s \n\n", dateString);
//    printf("***** Time String  ****** %s \n\n", timeString);

    char dayStr[2+1];
    char monthStr[2+1];
    char yearStr[2+1];
 
    uint32_t dayInt;
    uint32_t monthInt;
    uint32_t yearInt;
 
    char hourStr[2+1];
    char minuteStr[2+1];
    char secStr[2+1];
    char timeZoneStr[3+1];
 
    uint32_t hourInt;
    uint32_t minuteInt;
    uint32_t secInt;
    int32_t tzInt;

    //Step 1 - Seperate the day, month and year into their own NULL terminated string buffers...
    yearStr[0] = dateString[1];
    yearStr[1] = dateString[2];
    yearStr[2] = '\0';

    monthStr[0] = dateString[4];
    monthStr[1] = dateString[5];
    monthStr[2] = '\0';

    dayStr[0] = dateString[7];
    dayStr[1] = dateString[8];
    dayStr[2] = '\0';
 
    //Step 2 - Run atoi() to parse the strings into integer values...
    dayInt = atoi(dayStr);
    monthInt = atoi(monthStr);
    yearInt = atoi(yearStr) + 2000;
 
    //Step 3 - Seperate the hour, minute and sec into their own NULL terminated string buffers...
    hourStr[0] = timeString[0];
    hourStr[1] = timeString[1];
    hourStr[2] = '\0';
 
    minuteStr[0] = timeString[3];
    minuteStr[1] = timeString[4];
    minuteStr[2] = '\0';

    secStr[0] = timeString[6];
    secStr[1] = timeString[7];
    secStr[2] = '\0';

    timeZoneStr[0] = timeString[8];
    timeZoneStr[1] = timeString[9];
    if (timeString[10] != '\"') {
        timeZoneStr[2] = timeString[10];
    } else {
        timeZoneStr[2] = '\0';
    }
    timeZoneStr[3] = '\0';

    //Step 4 - Run atoi() to parse the strings into integer values...
    hourInt = atoi(hourStr);
    minuteInt = atoi(minuteStr);
    secInt = atoi(secStr);
    tzInt = atoi(timeZoneStr);

    time_t rawtime;
    struct tm * timeinfo;

    // get current timeinfo and modify it to the user's choice
    time_t ctTime;
    ctTime = time ( &rawtime );
    timeinfo = localtime (&rawtime);

//    printf("Time before setting is (UTC): %s\r\n", ctime(&ctTime));

    //Step 5 - Build up our date data structure...this will be used by the next step...
    timeinfo->tm_mday = dayInt;
    timeinfo->tm_mon = monthInt - 1; //num months since January - so Jan=0, Feb=1, Mar=2, etc
    timeinfo->tm_year = yearInt - 1900; //num years since 1900
    timeinfo->tm_hour = hourInt;
    timeinfo->tm_min = minuteInt;
    timeinfo->tm_sec = secInt;
     
//    printf("***** Year: %s, Month: %s, Day: %s ****** \n\n\n", yearStr, monthStr, dayStr);
//    printf("***** Year: %d, Month: %d, Day: %d ****** \n\n\n", yearInt, monthInt, dayInt);
//    printf("***** hour: %s, minute: %s, sec: %s, TZ: %s ****** \n\n\n", hourStr, minuteStr, secStr, timeZoneStr);
//    printf("***** hour: %d, minute: %d, sec: %d, TZ: %d ****** \n\n\n", hourInt, minuteInt, secInt, tzInt);

    time_t t1=0;
    t1 = mktime(timeinfo);
    set_time (t1 - tzInt*15*60); // offseting the TimeZone Index (received value from string is in quarter of the hour)
    ctTime = time (NULL);
    printf("Time set to (UTC): %s\r\n", ctime(&ctTime));
}


int main()
{
    bool simProvisioned = false;
    int ret;

    printf("Dragon Fly Sample Program for MultiTech Board - Sample Workshop Program ...\n\r");

    //Sets the log level to INFO, higher log levels produce more log output.
    //Possible levels: NONE, FATAL, ERROR, WARNING, INFO, DEBUG, TRACE
    MTSLog::setLogLevel(MTSLog::TRACE_LEVEL);

    /** STMicro Nucelo F411RE
    * The supported jumper configurations of the MTSAS do not line up with
    * the pin mapping of the Nucleo F411RE. Therefore, the MTSAS serial TX
    * pin (JP8 Pin 2) must be manually jumped to Serial1 RX (Shield pin D2)
    * and the MTSAS serial RX pin (JP9 Pin 2) pin must be manually jumped to
    * Serial1 TX (Shield pin D8).
    */
    MTSSerialFlowControl* io = new MTSSerialFlowControl(RADIO_TX, RADIO_RX, RADIO_RTS, RADIO_CTS);

    //Sets the baud rate for communicating with the radio
    io->baud(115200);

    //Creates a radio object
    Cellular* radio = CellularFactory::create(io);

    if (! radio) {
        logFatal("failed to create Cellular object - exiting");
        printf("failed to create Cellular object - exiting.\n\r");
        return 1;
    }

//    radio->configureSignals(D11,D7,RESET);          // Modified Flow Control for ST Sensor Board  D11
    Transport::setTransport(radio);                 // Required to control Cell Radio vs WiFi solution

    while (radio->setApn(_APN) != MTS_SUCCESS) {
        logError("failed to set APN [%s]", _APN);
        printf("failed to set APN.\n\r");
        wait(2);
    }

    while (! radio->connect()) {
        logError("failed to bring up PPP link");
        printf("failed to bring up PPP link.\n\r");
        wait(2);
    }

    printf("Signal Strength: %d\n\r", radio->getSignalStrength()); //Check the signal strength should be above 8

    // If you suspect a connectivity issue, uncomment the code below and if ping works.  If you are not getting a
    //  valid ping, there's a connectivity problem.  First step is to verify you've got the right APN set
    //
    // Try pinging default server "8.8.8.8" (Google's DNS)
    int ping_valid = 0;
    while (ping_valid == 0) {
        ping_valid = radio->ping();
        // or can use ping_valid = radio->ping("www.google.com");
        printf("Ping Valid: %s\n\r", ping_valid ? "true" : "false");
        if (ping_valid == 0) {
            wait(3);
            printf("wait 3");
        }
    }

    // Get the IMEI of the SIM, this will act as SCL-id on AerCloud
    string sImei;
    sImei = radio->getEquipmentIdentifier();
    simImei = (char*) sImei.c_str();
    strcpy (_AERCLOUD_DEVICE_ID, simImei);

    // set RTC clock to current time using radio time
    set_current_time (radio);

    // Create http client
    HTTPClient http;

    // Check if SIM is provisioned in AerCloud
    printf("\r\nIs the SIM provisioned?  ");
    char url[512];
    snprintf(url, sizeof(url), "%s/%s/scls/%s?apiKey=%s", AC_BASE, _AERCLOUD_ACCOUNT_ID, simImei, _AERCLOUD_API_KEY);
    ret = http.get(url, str, 128);
//    printf("URL : %s", url);
//    printf("STR : %s", str);

    if (ret == HTTP_OK) {
        // We're already provisioned
        printf("Yes\r\n");
//        printf("Page fetched successfully - read %d characters\r\n", strlen(str));
//        printf("Result: %s\r\n", str);
        simProvisioned = true;
    } else {
        printf("No\r\n");
//        printf("Error - ret = %d - HTTP return code = %d\r\n", ret, http.getHTTPResponseCode());

        //SIM is not provisioned. trying to provision it...
        char url[512];
        snprintf(url, sizeof(url), "%s/%s/scls?apiKey=%s", AC_BASE, _AERCLOUD_ACCOUNT_ID, _AERCLOUD_API_KEY);
        snprintf(str, sizeof(str), "{\"sclId\":\"%s\"}\0",simImei);
        HTTPJson outText(str);
        HTTPJson inText(str, 512);
        ret = http.post(url, outText, &inText);

        if (!ret) {
            printf("Executed POST successfully - read %d characters\r\n", strlen(str));
//            printf("Result: %s\r\n", str);
            simProvisioned = true;
        } else {
            if(http.getHTTPResponseCode() == 200)
                simProvisioned = true;
            printf("Error - ret = %d - HTTP return code = %d\r\n", ret, http.getHTTPResponseCode());
        }
    }

    PubSubClient mqtt(_host, _port, callback);
    unsigned int connectRetry = 0;
    while(! mqtt.connect(_AERCLOUD_DEVICE_ID, _AERCLOUD_ACCOUNT_ID, _AERCLOUD_API_KEY)) {
        logError("failed to connect to AerCloud Server");
        wait(5);
        connectRetry++;
        if (connectRetry > 5) break;
    }

    //POST data to containers if SIM has been successfully provisioned
    if (simProvisioned) {
        while(1) {

            // Read data from sensor
            char alarmStringM[512] = "";
            bool generateAlertM = false;

            moistureValue = moistureSensor;
            printf("Moisture value read : %f \n\n",moistureValue);
            if (moistureValue > .5f) {
                moistureProcessedValue = HIGH;
            } else if (moistureValue > .25f) {
                moistureProcessedValue = MEDIUM;
            } else {
                moistureProcessedValue = LOW;
            }

            if (moistureLastPeakValue != moistureProcessedValue) {  //condition to trigger alarm
                if (moistureLastPeakValue == 0) {                   // first time
                    moistureLastPeakValue = moistureProcessedValue;
                    generateAlertM = false;
                } else {
                    if (moistureLastPeakValue == LOW) {
                        if (moistureProcessedValue == MEDIUM) {
                            strcpy (alarmStringM, "Moisture level going up from LOW to MEDIUM");
                        } else if (moistureProcessedValue == HIGH) {
                            strcpy (alarmStringM, "Moisture level going up from LOW to HIGH");
                        }
                    } else if (moistureLastPeakValue == MEDIUM) {
                        if (moistureProcessedValue == LOW) {
                            strcpy (alarmStringM, "Moisture level going down from MEDIUM to LOW");
                        } else if (moistureProcessedValue == HIGH) {
                            strcpy (alarmStringM, "Moisture level going up from MEDIUM to HIGH");
                        }
                    } else if (moistureLastPeakValue == HIGH) {
                        if (moistureProcessedValue == LOW) {
                            strcpy (alarmStringM, "Moisture level going down from HIGH to LOW");
                        } else if (moistureProcessedValue == MEDIUM) {
                            strcpy (alarmStringM, "Moisture level going down from HIGH to MEDIUM");
                        }
                    }
                    moistureLastPeakValue = moistureProcessedValue;
                    generateAlertM = true;
                }
            }
            
            char alarmStringA[512] = "";
            bool generateAlertA = false;

            alcoholValue = 1 - alcoholSensor; // as per example logic value needs to be subtracted from 1 (inverse value)
            printf("Alcohol value read : %f \n\n",alcoholValue);
            if (alcoholValue > .5f) {
                alcoholProcessedValue = HIGH;
            } else if (alcoholValue > .25f) {
                alcoholProcessedValue = MEDIUM;
            } else {
                alcoholProcessedValue = LOW;
            }

            if (alcoholLastPeakValue != alcoholProcessedValue) {  //condition to trigger alarm
                if (alcoholLastPeakValue == 0) {                   // first time
                    alcoholLastPeakValue = alcoholProcessedValue;
                    generateAlertA = false;
                } else {
                    if (alcoholLastPeakValue == LOW) {
                        if (alcoholProcessedValue == MEDIUM) {
                            strcpy (alarmStringA, "Alcohol level going up from LOW to MEDIUM");
                        } else if (alcoholProcessedValue == HIGH) {
                            strcpy (alarmStringA, "Alcohol level going up from LOW to HIGH");
                        }
                    } else if (alcoholLastPeakValue == MEDIUM) {
                        if (alcoholProcessedValue == LOW) {
                            strcpy (alarmStringA, "Alcohol level going down from MEDIUM to LOW");
                        } else if (alcoholProcessedValue == HIGH) {
                            strcpy (alarmStringA, "Alcohol level going up from MEDIUM to HIGH");
                        }
                    } else if (alcoholLastPeakValue == HIGH) {
                        if (alcoholProcessedValue == LOW) {
                            strcpy (alarmStringA, "Alcohol level going down from HIGH to LOW");
                        } else if (alcoholProcessedValue == MEDIUM) {
                            strcpy (alarmStringA, "Alcohol level going down from HIGH to MEDIUM");
                        }
                    }
                    alcoholLastPeakValue = alcoholProcessedValue;
                    generateAlertA = true;
                }
            }
 
            // POST data to AerCloud using MQTT 
            time_t seconds = time(NULL);
            sprintf(str,"{\"moistureSensorValue\": %.5f, \"moistureProcessedValue\": %.2f, \"alcoholSensorValue\": %.5f, \"alcoholProcessedValue\": %.2f, \"sclId\":\"%s\", \"timeStamp\": %u}", moistureValue, moistureProcessedValue, alcoholValue, alcoholProcessedValue, simImei, seconds);

            printf("str = %s \r\n",str);
            time_t beforeTime = time(NULL);
            printf("\r\nAcquired data going to post now...  %u \r\n", beforeTime);
            if (! mqtt.publish(_AERCLOUD_CONTAINER, str)) {
                printf("failed to publish: [%s]", str);
            }
            wait_ms (100); // wait for 100 ms to get the data published
            printf("\r\nPosted data took ...  %u seconds \r\n", time(NULL)-beforeTime );

            // post alert for Moisture
            if (generateAlertM) {
                sprintf(str,"{\"alarmText\": \"%s\", \"sclId\":\"%s\", \"timeStamp\": %u}", alarmStringM, simImei, seconds);
                printf("str = %s \r\n",str);
                time_t beforeTime = time(NULL);
                printf("\r\nAcquired data going to post now...  %u \r\n", beforeTime);
                if (! mqtt.publish(_AERCLOUD_CONTAINER_ALARM, str)) {
                    printf("failed to publish: [%s]", str);
                }
                wait_ms (100); // wait for 100 ms to get the data published
                printf("\r\nPosted data took ...  %u seconds \r\n", time(NULL)-beforeTime );
            }
            
            // post alert for Alcohol
            if (generateAlertA) {
                sprintf(str,"{\"alarmText\": \"%s\", \"sclId\":\"%s\", \"timeStamp\": %u}", alarmStringA, simImei, seconds);
                printf("str = %s \r\n",str);
                time_t beforeTime = time(NULL);
                printf("\r\nAcquired data going to post now...  %u \r\n", beforeTime);
                if (! mqtt.publish(_AERCLOUD_CONTAINER_ALARM, str)) {
                    printf("failed to publish: [%s]", str);
                }
                wait_ms (100); // wait for 100 ms to get the data published
                printf("\r\nPosted data took ...  %u seconds \r\n", time(NULL)-beforeTime );
            }

            // URL to print
            printf("URL for Main Container: %s/%s/scls/%s/containers/%s/contentInstances?apiKey=%s&dataFormat=jsonRaw\n\n", AC_BASE, _AERCLOUD_ACCOUNT_ID, simImei, _AERCLOUD_CONTAINER, _AERCLOUD_API_KEY);
            printf("URL for Alam Container: %s/%s/scls/%s/containers/%s/contentInstances?apiKey=%s&dataFormat=jsonRaw\n\n", AC_BASE, _AERCLOUD_ACCOUNT_ID, simImei, _AERCLOUD_CONTAINER_ALARM, _AERCLOUD_API_KEY);

            printf("Will be measuring again after 1 seconds !!!! \r\n\n");
            wait (1);
        }
    }

    //Disconnect ppp link of radio
    mqtt.disconnect();
    //Disconnect ppp link of radio
    radio->disconnect();
}