Energy Manager App

Dependencies:   C12832_lcd LM75B mbed-rtos mbed

Fork of rtos_mutex by avnish aggarwal

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);
+    }
+}