3rd Repo, trying to figure this out.

Dependencies:   LPS25H hts221

Fork of SOFT253_Template_Weather_OS_54 by Stage-1 Students SoCEM

main.cpp

Committer:
niallfrancis
Date:
2017-05-13
Revision:
85:422d0a1b95cf
Parent:
83:0d3572a8a851

File content as of revision 85:422d0a1b95cf:

#include "mbed.h"
#include "rtos.h"
#include "hts221.h"
#include "LPS25H.h"

#include "MessageLogger.h"
#include "CircularArray.h"
#include "FakeSensor.h"

#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <iostream>
#include <sstream>

#define SIGNAL_doMeasure 1
#define SIGNAL_printMessage 2
#define SIGNAL_terminate 3
#define SWITCH1_RELEASE 90
#define BUFFER_SIZE 120
 
/**
    @file :     main.cpp
    @authors :   Radu Marcu, Jacob Williams, Niall Francis, Arron Burch
    
    @section DESCRIPTION
    
    This is the main class where the program runs on startup, this is
    a multi-threaded application that will that make the ST F401 Nucleo Target board
    take periodic measurements of senor data, including temperature,
    pressure and humidity, at a fixed set sample rate. A max of 120 measurements are stored 
    within RAM at any one time, available to be printed to a termial output via a serial port
    connection.
    Once 120 measurements are stored, the oldest measurements are overwritten using a circular
    buffer, so that the most recent 120 measurements can always be stored on the device.
    The application can provide logging data when measurements are taken if logging is turned
    on using the logging command (See readme.md).
    The application has been written to reduce power consumption where possible by utilising
    Thread signals and sleeping when not in use.
    Serial Communications are handled within a sepearate thread to ensure that no action will
    block or interrupt the CPU when measuring samples.
    The serial communications use a priority system, allowing errors to be printed with a higher
    priority than logging messages and the application to be terminated and reset by user input
    if required.
*/

//
//  MBED DECLARATIONS
//
DigitalOut myled(LED1);
DigitalIn  onBoardSwitch(USER_BUTTON);
I2C i2c2(I2C_SDA, I2C_SCL);

//
// SENSOR DECLARATIONS
// MAKE SURE ONE OF THESE IS COMMENTED OUT
// Real sensor
LPS25H barometer(i2c2, LPS25H_V_CHIP_ADDR);
HTS221 measurer(I2C_SDA, I2C_SCL);
// Fake sensor
// FakeBarometer barometer(1029.0, 1031.0);
// FakeMeasurer measurer(20.0, 25.0, 30.0, 50.0);

//
// THREADS DECLARATION
//
osThreadId mainThreadId;
Thread *producerThread;
Thread *measurementThread;
Thread *consumerThread;
Thread *loggingThread;
Ticker timer;
Ticker realTimeDate;
//
//  GLOBAL VARIABLES
// 
Mail<Measure, 16> mail_box;
LocalDate *localDate;
bool logging = false;
bool measuring = true;
double sampleRate = 15;
char temp[256];

// Logging objects
MessageLogger logger;

CircularArray buffer(BUFFER_SIZE, &logger);
// 
//  Called by a ticker
//  Adds 1 second every second to the clock
//
void RealTimeDate()
{
    localDate->TickSecond();
}

//
//  Ticker that signals the measureThread to do a measure
//
 void SendSignalDoMeasure()
{
    measurementThread->signal_set(SIGNAL_doMeasure);    
}

//
//  Called by ticker every T seconds
//  Reads values from sensor board, sends between threads using a mail queue
//
void MeasurementThread() 
{
    float temperature , humidity, pressure;

    while(true)
    {   
        temperature = 0;
        humidity = 0;
        pressure = 0;
        
        Thread::signal_wait(SIGNAL_doMeasure);
        
        Measure *measure = mail_box.alloc();
        if (measure == NULL) 
        {
           logger.SendError("Out of memory\r\n");
           return;   
        }
    
        //Read data from measurer, puts data into a measure object.
        measurer.ReadTempHumi(&temperature,&humidity);
        barometer.get();
        pressure = barometer.pressure();
        
        measure->temperature = temperature;
        measure->humidity = humidity;
        measure->pressure = pressure;
        measure->date.setValues(localDate);

        osStatus stat = mail_box.put(measure);
    
        // Check if mailbox allocation was unsuccessful 
        if (stat == osErrorResource)
        {
            snprintf(temp, 256, "queue->put() Error code: %4Xh, Resource not available\r\n", stat);   
            logger.SendError(temp);
            mail_box.free(measure);
            return;
        }
        
        // Print measurement taken if logging is enabled
        if(logging)
        {
            logger.SendMessage("Measurement Taken:\r\n");
            char *ptr = localDate->ToString();
            snprintf(temp, 256, "    %s T: %f, H: %f, P: %f\n\r",ptr, temperature, humidity, pressure);
            logger.SendMessage(temp);
        }
    }
}
 
//
//  Receives data through mail queue, then adds it to the circular array buffer.
//
void ProducerThread() 
{      
    while (true) 
    {
        osEvent evt = mail_box.get();
        
        // Check if mailbox retrieval was successful
        if (evt.status == osEventMail) 
        {
            Measure *measure = (Measure*)evt.value.p;          
            Measure msr(measure->date,measure->temperature, measure->humidity,measure->pressure);
            
            buffer.pushValue(msr);
            mail_box.free(measure);
        } 
        else
        {
            snprintf(temp, 256, "ERROR: %x\r\n", evt.status);   
            logger.SendError(temp);
        }
    }
}

int i;

/**
  Compares two char arrays and returns result

  @param command :        First char Array / pointer
  @param targetCommand :  Second char Array / pointer
  @param size :           Size of the smallest char arrays (between param1 and param2)
  @return :               TRUE if equal, FALSE if not equal
*/
bool CompareCommands(char command[],char targetCommand[], int size)
{
    for(i = 0; i < size; i ++)
    {
        if(command[i] != targetCommand[i])
        {
            return false;
        }
    }
    return true;
} 

/**
  Reads commands from the terminal and 'consumes' the data accordingly.
*/
void ConsumerThread()
{
    char charCmd;
    //Char array that stores the chars entered when user presses enter.
    char command[40];
    //Current Command Size
    int crtSize = 0; 
    logger.SendMessage("\r\nAwaiting Command: \r\n");
    while(1)
    {
        charCmd = NULL;
        charCmd = getchar();
        if(logging)
        {
            logging = false;
            logger.SendMessage("\rKey Pressed. Debug logging disabled.\r\n");
            charCmd = NULL;
        }
        
        if(charCmd != NULL)
        {
            //If BACKSPACE is pressed, Print "DEL" so it deletes last character typed.
            if (charCmd == 127 && crtSize > 0 )
            {
                command[--crtSize] = '\0';
            }
            //If NOT enter AND NOT Backspace is pressed, SAVE the char
            else if(charCmd != 13 && charCmd != 127) 
            {
                command[crtSize++] = charCmd;
            }
            // If enter is pressed, process the command.
            else if(charCmd == 13)
            {   
                logger.SendMessage("\n");
                // Get first word of command.
                char *charPos;
                charPos = strtok(command," -,");
                
                // Check if it's a "read" command
                if(CompareCommands(charPos, "read",4))
                {
                    charPos = strtok(NULL," -,");
                    // Check if it's a "read all" command
                    if(CompareCommands(charPos, "all",3))
                    {                        
                        // Changed to use circular buffer rather than list buffer
                        buffer.readAll();
                        logger.SendMessage("D O N E ! \r\n");
                    }
                    // Check if it's a "read n" command
                    else if(strtol(charPos,NULL,10) != 0)
                    {
                        int num = atoi(charPos);
                        
                        buffer.readN(num);
                        logger.SendMessage("D O N E ! \r\n");
                    }
                    else
                    {
                        logger.SendMessage("Expected parameters: \"all\" | \"n\", where n is a number.\r\n");
                    }
                }
                // Check if it's a "delete" command
                else if (CompareCommands(charPos,"delete",6))
                {
                    charPos = strtok(NULL," -,");
                    // Check if it's a "delete all" command
                    if(CompareCommands(charPos,"all",3))
                    {
                        logger.SendMessage("Deleting all measures performed so far: \r\n");
                        
                        buffer.deleteAll();
                    }
                    // Check if it's a "delete n" command
                    else if (strtol(charPos,NULL,10) != 0)
                    {
                        // Changed to use circular buffer rather than list buffer
                        buffer.deleteN(atoi(charPos));
                        logger.SendMessage("Elements deleted!\r\n");
                    }
                    else
                    {
                        logger.SendMessage("Expected parameters: \"all\" | \"n\", where n is a number.");
                    }                       
                }
                // Check if it's a "status" command
                else if (CompareCommands(charPos,"status",6))
                {
                    char *ptr = localDate->ToString();
                    
                    snprintf(temp, 256, "\nStatus: \r\n");
                    logger.SendMessage(temp);                    
                    snprintf(temp, 256, "    # of measures: %i \r\n", buffer.getSize());
                    logger.SendMessage(temp);
                    snprintf(temp, 256, "    Sampling: %s \r\n", measuring ? "On" : "Off");
                    logger.SendMessage(temp);
                    snprintf(temp, 256, "    Logging: %s \r\n", logging ? "On" : "Off");
                    logger.SendMessage(temp);
                    snprintf(temp, 256, "    Current Date: %s \r\n", ptr);
                    logger.SendMessage(temp);
                    snprintf(temp, 256, "    Sample Rate(s): %2.2f \r\n", sampleRate);
                    logger.SendMessage(temp);
                }
                //Check if it's a "settime" command
                else if (CompareCommands(charPos,"settime",7))
                {
                    int h,m,s;
                    
                    charPos = strtok(NULL," -,");
                    if(strtol(charPos,NULL,10) != 0)
                    {
                        h = atoi(charPos);   
                    }
                    charPos = strtok(NULL," -,");
                    if(strtol(charPos,NULL,10) != 0)
                    {
                        m = atoi(charPos);   
                    }
                    charPos = strtok(NULL," -,");
                    if(strtol(charPos,NULL,10) != 0)
                    {
                        s = atoi(charPos);   
                    }
                    if((h>=0 && h < 24) && (m>=0 && m<60) && (s>=0 && s<60))
                    {
                        localDate->hour = h;
                        localDate->min = m;   
                        localDate->sec = s;
                        char *ptr = localDate->ToString();
                        snprintf(temp, 256, "Updated Date to: %s \r\n", ptr);
                        logger.SendMessage(temp);
                    } 
                    else
                    {
                        logger.SendMessage("\r\nWrong format! please use HH-MM-SS. \r\n");
                    }
                }
                //Check if it's a "setdate" command
                else if (CompareCommands(charPos,"setdate",7))
                {
                    int d,m,y;
                    
                    charPos = strtok(NULL," ,-");
                    if(strtol(charPos,NULL,10) != 0)
                    {
                        d = atoi(charPos);   
                    }
                    
                    charPos = strtok(NULL," ,-");
                    if(strtol(charPos,NULL,10) != 0)
                    {
                        m = atoi(charPos);   
                    }
                    
                    charPos = strtok(NULL," ,-");
                    if(strtol(charPos,NULL,10) != 0)
                    {
                        y = atoi(charPos);   
                    }
                    
                    if((d>=0 && d < 31) && (m>=0 && m<13))
                    {
                        localDate->day = d;
                        localDate->month = m;   
                        localDate->year = y;
                        char *ptr = localDate->ToString();
                        snprintf(temp, 256, "Updated Date to: %s \r\n", ptr);
                        logger.SendMessage(temp);
                    } 
                    else
                    {
                        logger.SendMessage("Wrong format! please use DD-MM-YYYY. \r\n");  
                    }
                }
                // Check if it's a "state" command
                else if(CompareCommands(charPos,"state",5))
                {
                    charPos = strtok(NULL," ,");
                    
                    // Check if it should be turned on / off
                    if(CompareCommands(charPos,"on",2))
                    {
                        logger.SendMessage("Sampling turned on!\r\n");
                        timer.attach(&SendSignalDoMeasure, sampleRate);
                        SendSignalDoMeasure();
                        measuring = true;
                    }
                    else if (CompareCommands(charPos,"off",3))
                    {
                        logger.SendMessage("Sampling turned off!\r\n");
                        timer.detach();
                        measuring = false;
                    }
                    else
                    {
                        logger.SendMessage("Expected parameters: \"on\" | \"off\"\r\n");   
                    }
                }
                // Check if it's a "logging" command
                else if(CompareCommands(charPos,"logging",7))
                {
                    charPos = strtok(NULL," ,");
                    //Check if it should be turned ON / OFF
                    if(CompareCommands(charPos,"on",2))
                    {
                        logging = true;   
                        logger.SendMessage("Debug logging turned on!\r\n");
                        logger.SendMessage("Press any key to deactivate\r\n");
                    }
                    else if (CompareCommands(charPos,"off",3))
                    {
                        logger.SendMessage("Logging is already turned off.\r\n");
                    }
                    else
                    {
                        logger.SendMessage("Expected parameters: \"on\" | \"off\"\r\n");   
                    }
                }
                // Check if it's a "sett" command
                else if(CompareCommands(charPos,"sett",4))
                {
                    charPos = strtok(NULL," ,");
                    double auxRate = atof(charPos);
                    // Validate rate
                    if(auxRate >= 0.1 &&
                        auxRate <= 60)
                    {
                        sampleRate = auxRate;
                        timer.detach();
                        timer.attach(&SendSignalDoMeasure, sampleRate);
                        snprintf(temp, 256, "Successfully updated sample rate to: %2.2f .\r\n",sampleRate);
                        logger.SendMessage(temp);
                    }
                    else
                    {
                        logger.SendMessage("Sample rate must be between 0.1 and 60.\r\n");  
                    }
                }
                // Check if it's a "help" command
                else if (CompareCommands(charPos,"help",4) || CompareCommands(charPos,"?",1))
                {
                    logger.SendMessage("\nAvailable Commands:\r\n");
                    logger.SendMessage("    read <all|n> - Read all or n first measures.\r\n");
                    logger.SendMessage("    delete <all|n> - Delete all or n first measures.\r\n");
                    logger.SendMessage("    setdate <DD> <MM> <YYYY> Set current date.\r\n");
                    logger.SendMessage("    settime <HH> <MM> <SS> Set current time.\r\n");
                    logger.SendMessage("    sett <T> Set sample rate (in seconds).\r\n");
                    logger.SendMessage("    status - Status report of device.\r\n");
                    logger.SendMessage("    state - <on|off> - Turn sampling on or off.\r\n");
                    logger.SendMessage("    logging <on|off> - Turn logging on or off.\r\n");
                    logger.SendMessage("    reset - Resets the current session of the application.\r\n");
                }
                // Check if it's a "reset" command
                else if (CompareCommands(charPos,"reset",5))
                {
                    logger.SendError("Program Terminating...\r\n");
                }
                // If command is not recognized
                else
                {
                    logger.SendMessage("Command not recognized. Type \"help\" for more info.\r\n");
                }
                
                if(!logging) 
                {
                    logger.SendMessage("\r\nAwaiting Command:\r\n");
                }
                
                // Clear currently stored command
                int i = 0;
                for(i =0 ; i < crtSize; i++)
                    command[i] = ' ';
                command[0] = 0;
                crtSize = 0;
            }
        }
    }
}

/**
  Waits for a signal from the MessageLogger before confirming a message is to be printed,
  ensuring messages are displayed in the correct order.
  
  If an error is recieved, error is prioritised and signal is sent to the main thread for the
  program to be terminated, waiting for user input to reset.
*/
void LoggingThread()
{    
    while(true)
    {
        Thread::signal_wait(SIGNAL_printMessage);
        
        while(true)
        {
            if(logger.GetError())
            {
                osSignalSet(mainThreadId, SIGNAL_terminate);
            }
            else if(!logger.GetMessage())
            {
                break;
            }      
        } 
    }
}
 
/**
  Main Thread: Entry point to program.
*/
int main() {
    
    mainThreadId = osThreadGetId();
    
    measurer.init();
    measurer.calib();
    
    localDate = new LocalDate();      
   
    // Timer initialised for measurements to be collected every sampleRate seconds.
    timer.attach(&SendSignalDoMeasure, sampleRate);
    realTimeDate.attach(&RealTimeDate,1.0);
               
    // Start Threads
    
    loggingThread = new Thread();
    loggingThread->start(LoggingThread);
    
    logger.SetThread(loggingThread);
    
    producerThread = new Thread();
    producerThread->start(ProducerThread); 
    
    measurementThread = new Thread();
    measurementThread->start(MeasurementThread);
    
    consumerThread = new Thread();
    consumerThread->start(ConsumerThread);
    
    logger.SendMessage("\r\n--- W E L C O M E ---\r\n");
    
    SendSignalDoMeasure();
    
    while(true) 
    {
        // Waits for temination signal from logging thread when an error is received.
        osSignalWait(SIGNAL_terminate, osWaitForever);
        
        producerThread->terminate();
        measurementThread->terminate();
        consumerThread->terminate();
        loggingThread->terminate();
        
        // Waits for user input before resetting the system.
        printf("Press any key to restart...");
        char c = getchar();    
        printf("===============================\n\n\n");
        NVIC_SystemReset();        
    }
}