Flotsam / Full-Project

Dependencies:   GPSLibrary GSM mbed-modifed Storage_Library Temp_Library Wakeup pH_Sensor

main.cpp

Committer:
ptcrews
Date:
2015-11-30
Revision:
12:973566676eba
Parent:
11:cc22917d6634
Child:
13:52fba498c00d

File content as of revision 12:973566676eba:

#include "Adafruit_FONA.h"
#include "WakeUp.h"
#include "mbed.h"
#include "MBed_Adafruit_GPS.h"
#include <string>

// Cellular communication constants
#define FONA_TX D8
#define FONA_RX D2
#define FONA_RST D3
#define FONA_RI D4
#define FONA_KEY D5

// Global Positioning System constants
#define GPS_TX D6
#define GPS_RX PB_11
#define GPS_EN D7

// pH sensor constants
#define PH_TX PC_10
#define PH_RX PC_11

#define TMP_ANALOG A0
#define ADC_CONVERSION 3.3/5.0
AnalogIn temperature(TMP_ANALOG);

#define READINGSIZE sizeof(struct reading)
#define URL "http://requestb.in/sd2o2gsd"

// Cellular communication global variables
Adafruit_FONA fona(FONA_TX, FONA_RX, FONA_RST, FONA_RI);
Serial pcSerial(USBTX, USBRX);
DigitalOut key(FONA_KEY);

struct reading {
    float temperature;
    float pH;
    float latitude;     //Signed positive if N, negative if S
    float longitude;    //Signed positive if E, negative if W
    uint8_t day;
    uint8_t month;
    uint8_t year;
    uint8_t hour;
    uint8_t minutes;
};

// GPS global variables
char c; //when read via Adafruit_GPS::read(), the class returns single character stored here
Timer refresh_Timer; //sets up a timer for use in loop; how often do we print GPS info?
const int refresh_Time = 2000; //refresh time in ms
Serial gps_Serial(GPS_TX, GPS_RX); // Serial object for GPS
Adafruit_GPS myGPS(&gps_Serial);
DigitalOut gpsEN(GPS_EN);

// pH sensor global variables
Serial ph_Serial (PH_TX, PH_RX);
string sensorstring = "";
bool input_stringcomplete = false;
bool sensor_stringcomplete = false;
float pH;

struct reading lastReadingBuffer;

void setupPCSerial() {
    pcSerial.baud(115200);
    pcSerial.printf("\n\n PC Serial connection established at 115200 baud.\n");
}

void disableContinuousMode() {
    printf("pH sensor will NOT run in continous mode.\n");
    if(ph_Serial.writeable() <= 0) printf("Not writable\n");
    // disable continuous mode
    ph_Serial.printf("C,0");
    ph_Serial.printf("%c", '\r');
    printf("Waiting five seconds... ");
    wait(5);
}

void setupPH() {
    ph_Serial.baud(9600);
    disableContinuousMode();
}

void setupGPS() {
    gpsEN.write(1);
    myGPS.begin(9600);
    myGPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA); //these commands are defined in MBed_Adafruit_GPS.h; a link is provided there for command creation
    myGPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
    myGPS.sendCommand(PGCMD_ANTENNA);
    wait(1);
    refresh_Timer.start();  //starts the clock on the timer
    printf("setupGPS seems to be fine\n");
}

void setup() {
    printf("\n=====\nSetup in Full Project\n");
    setupPCSerial();
    setupGPS();
    setupPH();
} 

// Send default message to pH sensor, asking for data.
void pHRequest() {
    printf("Sending pH request...\n");
    if(ph_Serial.writeable() <= 0) printf("Not writable\n");
    // request one reading
    ph_Serial.printf("R");
    ph_Serial.printf("%c", '\r');
}

void pHRead() {
    pHRequest();
    printf("Reading pH information.\n");
    if (ph_Serial.readable() > 0) {                     //if we see that the Atlas Scientific product has sent a character.
        printf("Receiving sensor string... [");
        char inchar;
        while((inchar = (char)ph_Serial.getc()) != '\r') {
            sensorstring += inchar;
            printf("%c", inchar);
        }
        printf("] ...sensor string received!\n");
        sensor_stringcomplete = true;
        printf(sensorstring.c_str());
        // +1 is to get rid of control character
        pH = atof(sensorstring.c_str()+1);                      //convert the string to a floating point number so it can be evaluated by the Arduino

        if (pH >= 7.0) {                                  //if the pH is greater than or equal to 7.0
            printf("high\n");                         //print "high" this is demonstrating that the Arduino is evaluating the pH as a number and not as a string
        }

        if (pH <= 6.999) {                                //if the pH is less than or equal to 6.999
            printf("low\n");                          //print "low" this is demonstrating that the Arduino is evaluating the pH as a number and not as a string
        }

        //ph_Serial.printf("SLEEP");
        //ph_Serial.printf("%c", '\r');

        sensorstring = "";                                //clear the string:
        sensor_stringcomplete = false;                    //reset the flag used to tell if we have received a completed string from the Atlas Scientific product
    } else {
        printf("pH sensor is not readable\n");
    }
}

// n_queries is the number of times we query the GPS. We need something like 23000 characters.
void GPSRead(int n_queries) {
    for(int i = 0; i < 20; i++){
        wait(1);
        if(myGPS.fix) break;
    }
    pcSerial.printf("\n");
    for (int i = 0; i < n_queries; i++) {
        c = myGPS.read();   //queries the GPS
        
        if (c) { pcSerial.printf("%c", c); } //this line will echo the GPS data if not paused
        
        //check if we recieved a new message from GPS, if so, attempt to parse it,
        if ( myGPS.newNMEAreceived() ) {
            if ( !myGPS.parse(myGPS.lastNMEA()) ) {
                continue;   
            }    
        }
        
        //check if enough time has passed to warrant printing GPS info to screen
        //note if refresh_Time is too low or pcSerial.baud is too low, GPS data may be lost during printing
        if (refresh_Timer.read_ms() >= refresh_Time) {
            refresh_Timer.reset();
            pcSerial.printf("Time: %d:%d:%d.%u\n", myGPS.hour, myGPS.minute, myGPS.seconds, myGPS.milliseconds);   
            pcSerial.printf("Date: %d/%d/20%d\n", myGPS.day, myGPS.month, myGPS.year);
            pcSerial.printf("Fix: %d\n", (int) myGPS.fix);
            pcSerial.printf("Quality: %d\n", (int) myGPS.fixquality);
            
            lastReadingBuffer.hour = myGPS.hour;
            lastReadingBuffer.minutes = myGPS.minute;
            lastReadingBuffer.day = myGPS.day;
            lastReadingBuffer.month = myGPS.month;
            lastReadingBuffer.year = myGPS.year;
            lastReadingBuffer.latitude = 0.0;
            lastReadingBuffer.longitude = 0.0;
            if (myGPS.fix) {
                float mylatitude = myGPS.latitude;
                if(myGPS.lat == 'S')
                    mylatitude *= -1;
                float mylongitude = myGPS.longitude;
                if(myGPS.lon == 'W')
                    mylongitude *= -1;
                lastReadingBuffer.latitude = mylatitude;
                lastReadingBuffer.longitude = mylongitude;
                pcSerial.printf("Location: %5.2f%c, %5.2f%c\n", myGPS.latitude, myGPS.lat, myGPS.longitude, myGPS.lon);
                pcSerial.printf("Speed: %5.2f knots\n", myGPS.speed);
                pcSerial.printf("Angle: %5.2f\n", myGPS.angle);
                pcSerial.printf("Altitude: %5.2f\n", myGPS.altitude);
                pcSerial.printf("Satellites: %d\n", myGPS.satellites);
            }
        }
    }
    pcSerial.printf("\n");
}

void setupGSM()
{
    printf("Starting FONA\n");
    while(!fona.begin(9600)) {
        printf("Cannot find FONA\n");
        wait(1);
    }
    fona.begin(9600);
    printf("After begin\n");
    fona.setGPRSNetworkSettings("pwg", "", "");
    printf("After set setting\n");
    bool enable = false;
    while(enable != true) {
        fona.enableGPRS(true);
        fona.enableGPRS(false);
        enable = fona.enableGPRS(true);
    }
    printf("After enable\n");
    key.write(1);
}

void changeGSMPowerState()
{
    key.write(0);
    wait(2);
    key.write(1);
}

//Found this online and it claims that it resets ADC to work after deepsleep \_O_/
void resetADC()
{
    // Enable the HSI (to clock the ADC)
    RCC_OscInitTypeDef RCC_OscInitStruct;
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
    RCC_OscInitStruct.HSIState       = RCC_HSI_ON;
    RCC_OscInitStruct.PLL.PLLState   = RCC_PLL_NONE;
    HAL_RCC_OscConfig(&RCC_OscInitStruct);  
}

void enterSleep(int msec)
{
    gpsEN.write(0);
    if(msec > 0) WakeUp::set_ms(msec);
    deepsleep();
    resetADC();
    gpsEN.write(1);
}

bool sendDataOverHTTP(char* url, uint8_t* data, int dlength)
{
    uint16_t statuscode;
    int16_t length;
    if (!fona.HTTP_POST_start(url, "text/plain", data, dlength, &statuscode, (uint16_t *)&length)) {
        pcSerial.printf("Failed!\r\n");
        return false;
    }
    while (length > 0) {
        while (fona.readable()) {
            char c = fona.getc();
            pcSerial.putc(c);
            length--;
            if (! length) break;
        }
    }
    pcSerial.printf("\r\n****\r\n");
    fona.HTTP_POST_end();
    return true;
}

//define where the EEPROM begins
#define stadr 0x08080000

//Our current offset in the EEPROM
int offset = 0;
int roffset = 0;

//This function writes a byte of data to the EEPROM at the appropriate location.
//This is called by my writeEEPROMbytes.
//Use this if you want to write a single byte.
HAL_StatusTypeDef writeEEPROMByte(uint8_t data)
 {
    HAL_StatusTypeDef  status;
    int address = offset + stadr;
    offset++;
    HAL_FLASHEx_DATAEEPROM_Unlock();  //Unprotect the EEPROM to allow writing
    status = HAL_FLASHEx_DATAEEPROM_Program(TYPEPROGRAMDATA_BYTE, address, data);
    HAL_FLASHEx_DATAEEPROM_Lock();  // Reprotect the EEPROM
    return status;
}

//This function takes an array of bytes and its size and writes them to the EEPROM
//Use this if you want to write a lot of bytes.
void writeEEPROMbytes(uint8_t* data, uint8_t size)
{
    for(int i = 0; i < size; i++)
    {
        writeEEPROMByte(data[i]);
    }
}

//This function reads a byte of data from the EEPROM at the offset passed in.
//This is called by my getEEPROM function
uint8_t readEEPROMByte(uint32_t off) {
    uint8_t tmp = 0;
    off = off + stadr;
    tmp = *(__IO uint32_t*)off;
    
    return tmp;
}

//Call this function when you have sent all the data in the EEPROM through GSM
//It just resets the offset to 0 so we start writing from the start.
void resetEEPROM()
{
    offset = 0;
}

//This takes an array of bytes an fills it with our entire EEPROM
//Call this function when you want to send the data over GSM
void getEEPROM(uint8_t *ptr)
{
    int nbytes = offset;
    printf("The number of bytes in the EEPROM is %d\n", nbytes);
    for(int i = 0; i < nbytes; i++)
    {
        ptr[i] = readEEPROMByte(i);
    }
    //printf("WARNING DEBUG CODE BREAKS THINGS");
    //roffset += nbytes;
    return;
}

//function to get both the 
float AD22100K_AI_value_to_Celsius() {                                               // Convert Analog-input value to temperature
  //1023 is to scale it up to the arduino read values.
  float voltage = (int)((temperature.read() * ADC_CONVERSION) * 1023);
   
  float temperatureValue = (voltage * 0.217226044) - 61.1111111; // conversion factor simplified.
  pcSerial.printf("AI_Val: %f\n", temperatureValue);
  lastReadingBuffer.temperature = temperatureValue;
  return temperatureValue;       // 22.5 mV / °C; Ratiometric measurement, conversion valid for 5 V!
}

bool sendDataToURL(uint8_t* data, int size)
{
    changeGSMPowerState();
    setupGSM();
    printf("Setup GSM\n");
    bool res = sendDataOverHTTP(URL, data, size);
    printf("After sent data\n");
    changeGSMPowerState();    
    return res;
}

// In the final version of this code:
// We wake up ten times an hour to record information from our sensors.
// but we wait until we have accumulated 240 measurements before we 
// start attempting to send away all our data. Once we successfully send 
// our information, we clear our memory and begin from the start.

// TODO: Sleep mode. How does the Nucleo enter / exit sleep mode? 
// How do we power on / power off the other devices:
//  - GPS?
//    use the EN pin on the GPS
//  - pH sensor?
//    There's a sleep command
//  - Temperature sensor?
//    Should automatically switch on / off when the nucleo enters deep sleep mode.
//  - GSM?
//    The enable pin is modified by changeGSMPowerStat();
// TODO: Web communication
//  - write server

int main()
{
    setup();
    int nreadings = 0;
    bool toSend = false;
    while (true) {
        printf("~~~~~[pH]~~~~~\n");
        pHRead();
        lastReadingBuffer.pH = pH;
        wait(1);
        printf("~~~~~[GPS]~~~~~\n");
        GPSRead(300000);
        wait(1);
        printf("~~~~~[Temperature]~~~~~\n");
        AD22100K_AI_value_to_Celsius();
        wait(1);
        writeEEPROMbytes((uint8_t *) &lastReadingBuffer, READINGSIZE);
        nreadings++;
        if(nreadings == 10)
            toSend = true;
        if(toSend) {
            struct reading* data = new struct reading[nreadings];
            getEEPROM((uint8_t*) data);
            char buffer[((8+8+10+10+5*sizeof(int)+8)*nreadings) + 1];
            for(int i = 0; i < nreadings; i++)
            {
                int n = sprintf(buffer, "%8f %8f %10f %10f %d %d %d %d %d", data[i].temperature, data[i].pH, data[i].latitude, data[i].longitude, data[i].day, 
                                              data[i].month, data[i].year, data[i].hour, data[i].minutes);
                printf("%s\n", buffer);
                printf("%d\n", n);
                printf("%d\n", (sizeof(buffer)));
                printf("%d\n", data[i].minutes);
            }
            printf("HERE\n");
            bool res = sendDataToURL((uint8_t*) buffer, sizeof(buffer));   
            if(res) {
                toSend = false;
                nreadings = 0;
                resetEEPROM();
            }
            delete[] data;
        }
        //else changeGSMPowerState();
        /*
        printf("Reading Size is %d\n", READINGSIZE);
        printf("ROM Latitude: %f\n", verifier.latitude);
        printf("ROM Longitude: %f\n", verifier.longitude);
        printf("ROM pH: %f\n", verifier.pH);
        printf("ROM Temperature: %f\n", verifier.temperature);
        printf("ROM Day: %d\n", verifier.day);
        printf("ROM Month: %d\n", verifier.month);
        printf("ROM Year: %d\n", verifier.year);
        printf("ROM Hour: %d\n", verifier.hour);
        printf("ROM Minutes: %d\n", verifier.minutes);
        */
        wait(1);
        key.write(1);
        enterSleep(360000);
    }

    return 0;
}