Energy Manager App

Dependencies:   C12832_lcd LM75B mbed-rtos mbed

Fork of rtos_mutex by avnish aggarwal

main.cpp

Committer:
jesstvaldez
Date:
2013-09-20
Revision:
6:3b2a6cb895a9
Parent:
1:0f886ffbe0c1

File content as of revision 6:3b2a6cb895a9:

/* ============================================================================
    JESS VALDEZ
    Class 5381
    Homework : Final Project
    Instructor: AVNISH AGGARWAL

    >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
        OFF THE GRID ENERGY MANAGER
    <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

    OTHER Resource:
    p27,p28 i2c pins connected to LM75B thermometer;
    tick interrupt uses TIMER0

    OUTPUT MAPPING
    - led1  - blinks = running
    - led2 - source1 or Solar
    - led3 - source2 or battery
    - led4 - generator
    - p22 general control pin
    - display - LCD (SPI)

    ADC
    p18 - S1 (Solar prescaled input)
    p19 - S2 (raw scale battery)

    JOYSTICK CONTROL
    UP - Start
    RIGHT - Force change source
    DOWN - System down system to OUT - No source selected, used as turn off sequence.

  Added: Power saving mode 9-12-2013

 ==============================================================================*/

#include "mbed.h"
#include "rtos.h"
#include "C12832_lcd.h"
#include "LM75B.h"

Serial pc(USBTX, USBRX);
AnalogIn a_PD[] = {(p16),(p17),(p18),(p19),(p20)};

//p19, p20 are pots used for solar + battery simulation - USE_POT=1
//p17 true solar sense
//p18 true batt sense
#define USE_POT 0

#define OUT    0
#define RUNNING 1
#define FAULT   2

//Defined or available sources of energy
#define SOLAR 0
#define BATTERY 1
#define GENERATOR 2

#define maxSource 3 //adjust to number of sources ablove

float solar_threshold = 0.10; //10 percent
float battery_threshold = 0.20; //20 percent


#include "PowerControl.h"
//#include "EthernetPowerControl.h"
// Need PowerControl *.h files from this URL
// http://mbed.org/users/no2chem/notebook/mbed-power-controlconsumption/

// Function to power down magic USB interface chip with new firmware
#define USR_POWERDOWN    (0x104)
int semihost_powerdown()
{
    uint32_t arg;
    return __semihost(USR_POWERDOWN, &arg);
}

char statStr[3][50] = {"OFF","RUNNING","FAULT"};
char sourceStr[3][50] = {"SOLAR","BATTERY","GENERATOR"};
float sourceLev[20];   /* AD result of measured voltage */
C12832_LCD lcd;
LM75B tmp(p28,p27);
DigitalOut led1(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);
DigitalOut led4(LED4);

DigitalOut generator_control(p22);


void running_thread(void const *);
void get_levels_thread(void const *);
void temperature_thread(void const *);

// GLOBALS
int mainSource;
int prioritySource;
int mainStatus;
short int update;

unsigned int tsec, tsec2, seconds, minutes, hours, days;
//unsigned int setTime, setTime_min; //default cooktime
double controller_temp;
Mutex status_mutex;

// Watchdog code from
// http://mbed.org/forum/mbed/topic/508/
class Watchdog
{
public:
// Load timeout value in watchdog timer and enable
    void kick(float s) {
        LPC_WDT->WDCLKSEL = 0x1;                // Set CLK src to PCLK
        uint32_t clk = SystemCoreClock / 16;    // WD has a fixed /4 prescaler, PCLK default is /4
        LPC_WDT->WDTC = s * (float)clk;
        LPC_WDT->WDMOD = 0x3;                   // Enabled and Reset
        kick();
    }
// "kick" or "feed" the dog - reset the watchdog timer
// by writing this required bit pattern
    void kick() {
        LPC_WDT->WDFEED = 0xAA;
        LPC_WDT->WDFEED = 0x55;
    }
};

// Setup the watchdog timer
Watchdog wdt;

/*==============================
    Start INTERRUPT
================================*/
class Control_start
{
public:
    Control_start(PinName pin) : start_interrupt(pin) {
        start_interrupt.rise(this, &Control_start::start_system);
    }
    void start_system() {
        status_mutex.lock();
        mainStatus = RUNNING;
        mainSource = SOLAR;

        lcd.cls();
        seconds=0;
        minutes=0;
        hours=0;
        days=0;
        status_mutex.unlock();
    }
private:
    InterruptIn start_interrupt;
};
/*==============================
    Stop INTERRUPT
================================*/
class Control_stop
{
public:
    Control_stop(PinName pin) : stop_interrupt(pin) {
        stop_interrupt.rise(this, &Control_stop::stop_system);
    }
    void stop_system() {
        status_mutex.lock();
        mainStatus = OUT;

        seconds=0;
        minutes=0;
        hours=0;
        days=0;

        led1 = 0;
        led2 = 0;
        led3 = 0;
        led4 = 0;
        lcd.cls();
        status_mutex.unlock();
    }
private:
    InterruptIn stop_interrupt;
};

/*==============================
    Force next Source INTERRUPT
================================*/
class Control_next
{
public:
    Control_next(PinName pin) : next_interrupt(pin) {
        next_interrupt.rise(this, &Control_next::switch_to_next_source);
    }
    void switch_to_next_source() {
        status_mutex.lock();
        if (mainStatus == RUNNING) {
            mainSource++;
            if (mainSource>(maxSource-1)) mainSource=0;

        }
        lcd.cls();
        seconds=0;
        minutes=0;
        hours=0;
        days=0;
        status_mutex.unlock();
    }
private:
    InterruptIn next_interrupt;
};

Control_next next(p16);
Control_start start(p15);
Control_stop stop(p12);

unsigned short timer_count, clear_count;

//======================================================
//   TIMER0 IRQ Handler
//=======================================================
extern "C" void TIMER0_IRQHandler (void)
{
    if((LPC_TIM0->IR & 0x01) == 0x01) { // if MR0 interrupt, proceed
        LPC_TIM0->IR |= 1 << 0;         // Clear MR0 interrupt flag
        timer_count++;                  //increment timer_count
        clear_count++;
    }
}
//======================================================
//   timer0 initialization
//=======================================================
void timer0_init(void)
{
    LPC_SC->PCONP |=1<1;            //timer0 power on
    LPC_TIM0->MR0 = 2398000;        //100 msec
    LPC_TIM0->MCR = 3;              //interrupt and reset control
    //3 = Interrupt & reset timer0 on match
    //1 = Interrupt only, no reset of timer0
    NVIC_EnableIRQ(TIMER0_IRQn);    //enable timer0 interrupt
    LPC_TIM0->TCR = 1;              //enable Timer0
//   pc.printf("Done timer_init\n\r");
}

/* ==========================================
     main()
     - Entry point.
 ============================================*/
int main()
{
    float s_p[2], volts; //calculated levels in percent
    timer0_init();
    timer_count = 0;
    clear_count = 0;
    generator_control = 0;
    mainStatus = OUT;
    mainSource = SOLAR;
    s_p[0] = 5.0; //give this a value to prevent glitch to generator at startup
    s_p[1] = 5.0;

#ifdef NOETHER
    // Normal mbed power level for this setup is around 690mW
    // assuming 5V used on Vin pin
    // If you don't need networking...
    // Power down Ethernet interface - saves around 175mW
    // Also need to unplug network cable - just a cable sucks power
    PHY_PowerDown();
#endif

    // On reset, indicate a watchdog reset or a pushbutton reset on LED 4 or 3
    if ((LPC_WDT->WDMOD >> 2) & 1)
        led4 = 1;
    else led3 = 1;


    //running thread
    Thread thread1(running_thread, NULL, osPriorityHigh, DEFAULT_STACK_SIZE, NULL);

    //measurements
    Thread thread2(get_levels_thread);

    //fault monitor thread (temperature)
    Thread thread3(temperature_thread, NULL, osPriorityLow, DEFAULT_STACK_SIZE, NULL);

    //main status threads
    while (true) {
        lcd.locate(0,1);
        lcd.printf("%s, %s", statStr[mainStatus], sourceStr[mainSource]);

        volts = (sourceLev[0] / 3.04) * 10.0;
        volts = volts * 4.0; //scale to voltage divider of solar sense
        s_p[0] = (volts / 21.0) * 100.0; //percentage of solar current single panel.

        s_p[1] = (sourceLev[1] / 3.04) * 10.0;   // voltage, sourceLev[1] has no scaling
        s_p[1] = (s_p[1] / 3.3) * 100.0; //percent
        lcd.locate(0,11);
        lcd.printf("S1= %.2f %%, S2= %.2f %%", s_p[0], s_p[1]);

        lcd.locate(0,21);
        lcd.printf("Time= %d days, %.2d:%.2d:%.2d", days, hours, minutes, seconds%60);

        //refresh lcd every 100 seconds
        if (clear_count > 1000) {
            // Clear LCD Screen
            lcd.cls();
            clear_count = 0;
        }
        pc.printf("S1= %.2f %%, S2= %.2f %%, %d, %d\n\r", s_p[0], s_p[1], mainStatus, mainSource);
        Thread::wait(500);
    }
}

//======================================================
//   thread that displays the time elapsed while running on a source
//   also links led1
//=======================================================
void running_thread(void const *args)
{

    while (true) {
        status_mutex.lock();
        update=0;
        if (mainStatus == RUNNING) {
            led1 = !led1;

            if (timer_count > 9) {
                /* timer_count is 10 per second (so 0-9)*/
                seconds++;
                minutes = seconds/60.0;
                hours = minutes/60.0;
                days = hours/24.0;
                timer_count=0;
            }
            //led update OK, but not a reliable way to set a power switch
            switch(mainSource) {
                case 0:
                    led2=1;
                    led3=0;
                    led4=0;
                    break;
                case 1:
                    led2=0;
                    led3=1;
                    led4=0;
                    break;
                case 2:
                    led2=0;
                    led3=0;
                    led4=1;
                    break;
            }
        }

        if ((mainStatus == OUT)|| (mainStatus == FAULT)) {
            led1 = 0;
            led2 = 0;
            led3 = 0;
            led4 = 0;
            Sleep(); //Save Power
        }


        //OK decide which source is available as mainSource
        switch(mainSource) {
            case SOLAR:
                if ((sourceLev[0] < solar_threshold) && (sourceLev[1] > battery_threshold)) {
                    mainSource++;
                    update=1;
                }
                if ((sourceLev[0] < solar_threshold) && (sourceLev[1] < battery_threshold)) {
                    mainSource=GENERATOR;    //if both solar and battery are down
                    generator_control=1;
                    update=1;
                }
                break;

            case BATTERY:
                if (sourceLev[0] > solar_threshold) {
                    mainSource=SOLAR;    //if solar comes to life
                    update=1;
                }
                if ((sourceLev[0] < solar_threshold) && (sourceLev[1] < battery_threshold)) {
                    mainSource=GENERATOR;    //solar or batt are low
                    generator_control=1;
                    update=1;
                }
                break;

            case GENERATOR:
                if (sourceLev[1] > battery_threshold) {
                    mainSource=BATTERY;    //check if we can switch to battery
                    generator_control=0;
                    update=1;
                }
                if (sourceLev[0] > solar_threshold) {
                    mainSource=SOLAR;    //switch to solar if ever possible
                    generator_control=0;
                    update=1;
                }
                break;
            default:
                mainStatus = OUT;
                update=1;
                break;
        }

        if (update==1) {
            //if update occurred
            lcd.cls();
            seconds=0;
            minutes=0;
            hours=0;
            days=0;
            update=0;
        }
        status_mutex.unlock();
        Thread::wait(500);
    }
}
//======================================================
//get levels thread
//======================================================
void get_levels_thread(void const *args)
{
    while (true) {
        status_mutex.lock();
        if (mainStatus == RUNNING) {
#if USE_POT
            //using pots during development
            sourceLev[0]=a_PD[3]; //pot1
            sourceLev[1]=a_PD[4]; //pot2
#else
            //using actual solar + battery
            sourceLev[0]=a_PD[2]; //p18 of mbed
            sourceLev[1]=a_PD[1]; //p17 of mbed

#endif
        }

        status_mutex.unlock();
        Thread::wait(500);
    }
}

//======================================================
//   thread that monitors temperature of the controller PCB
//   generates fault or error when temp reaches threshold 100C
//=======================================================
void temperature_thread(void const *args)
{
    while (true) {
        status_mutex.lock();

        controller_temp = tmp.read();
        if (controller_temp > 100) {
            mainStatus = FAULT;
            generator_control = 0;
            wdt.kick();
        }
        status_mutex.unlock();
        Thread::wait(500);
    }
}