Energy Manager App
Dependencies: C12832_lcd LM75B mbed-rtos mbed
Fork of rtos_mutex by
Diff: main.cpp
- Revision:
- 6:3b2a6cb895a9
- Parent:
- 1:0f886ffbe0c1
--- a/main.cpp Thu Aug 15 06:50:18 2013 +0000 +++ b/main.cpp Fri Sep 20 04:12:04 2013 +0000 @@ -1,24 +1,459 @@ +/* ============================================================================ + 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" -Mutex stdio_mutex; +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 -void notify(const char* name, int state) { - stdio_mutex.lock(); - printf("%s: %d\n\r", name, state); - stdio_mutex.unlock(); +//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); } -void test_thread(void const *args) { +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) { - notify((const char*)args, 0); Thread::wait(1000); - notify((const char*)args, 1); Thread::wait(1000); + 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); } } -int main() { - Thread t2(test_thread, (void *)"Th 2"); - Thread t3(test_thread, (void *)"Th 3"); - - test_thread((void *)"Th 1"); +//====================================================== +// 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); + } +}