Andrea Faustinelli / espresso-for-geeks-master
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 /* Copyright (c) 2017 Philippe Kalaf, MIT License
00002  *
00003  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
00004  * and associated documentation files (the "Software"), to deal in the Software without restriction, 
00005  * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
00006  * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
00007  * furnished to do so, subject to the following conditions:
00008  *
00009  * The above copyright notice and this permission notice shall be included in all copies or 
00010  * substantial portions of the Software.
00011  *
00012  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 
00013  * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
00014  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
00015  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
00016  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00017  */
00018 
00019 // Espresso Machine Mods
00020 // ---------------------
00021 // Hardware
00022 // --------
00023 // Boiler temperature sensor (x2)
00024 // Brewing pressure sensor
00025 // Flowmeter
00026 // Solenoid control
00027 
00028 // Software
00029 // --------
00030 // PID temperature control (back-flush for temperature reduction)
00031 // pressure control (phase control)
00032 // auto-shot clock
00033 // auto-shot volume
00034 // auto-flowrate on fixed shot clock and shot volume
00035 // manual mode
00036 // brew soft-stop
00037 // pre-infusion
00038 
00039 #include "mbed.h"
00040 #include "rtos.h"
00041 #include "Small_6.h"
00042 #include "Small_7.h"
00043 #include "Arial_9.h"
00044 #include "Arial12x12.h"
00045 #include "Arial24x23.h"
00046 #include "stdio.h"
00047 #include "C12832_lcd.h"
00048 #include "string"
00049 #include "brewcontrol.h"
00050 
00051 #if MBED_HEAP_STATS_ENABLED || MBED_STACK_STATS_ENABLED
00052 #include "platform/mbed_stats.h"
00053 #endif
00054 
00055 // This is for debugging and control without a LCD/joystick
00056 Serial pc(USBTX, USBRX); // tx, rx
00057 
00058 // LCD object
00059 C12832_LCD LCD;
00060 
00061 // Menu display thread
00062 Thread menu_thread(osPriorityNormal, 1024);
00063 
00064 // mutex for heap between main and menu_handler threads 
00065 Mutex heap_mutex;
00066 
00067 // Joystick for menu control
00068 InterruptIn Fire(p9);   // JS_PUSH
00069 InterruptIn Up(p30);    // JS_UP
00070 InterruptIn Down(p29);  // JS_DOWN
00071 InterruptIn Left(p28);  // JS_LEFT
00072 InterruptIn Right(p27); // JS_RIGHT
00073 
00074 // This is used to ignore double clicks
00075 Timer js_timer;
00076 
00077 // Timer for power saving mode
00078 Timer power_save_timer;
00079 bool power_save;
00080 #define POWER_SAVE_TIMEOUT_S 600
00081 
00082 // Main Brew Control class holding all brewing features and logic
00083 // parameters are (in order):
00084 // valve/zcd/pump power control (VALVE_CTRL)
00085 // flow sensor input pin (FLOW_IN)
00086 // ZDC detector input (ZCD_IN)
00087 // pump phasecontrol signal (PUMP_CTRL)
00088 // pressure sensor (PRESSURE_IN)
00089 // side temperature sensor (TEMP_IN)
00090 // top temperature sensor (TEMP2_IN)
00091 // boiler control ssr (BOILER_CTRL)
00092 BrewControl brew_control(p10, p15, p12, p13, p20, p25, p14, p26);
00093 
00094 // Menu structures
00095 //
00096 // TODO portafilter size
00097 // TODO powersaving
00098 #define PRE_INFUSE_TIME 0
00099 #define PRESSURE 1
00100 #define YIELD 2
00101 #define SHOT_TIME 3
00102 #define TEMPERATURE 4
00103 #define FLOW_RATE 5
00104 #define EMPTY 6
00105 
00106 #define L_STANDBY 0
00107 #define L_MENU 1
00108 #define L_MENU_SUB 2
00109 #define L_BREW 3
00110 
00111 #define M_BREW_SETTINGS 0
00112 #define M_BREW_TIME MODE_TIME
00113 #define M_BREW_YIELD MODE_YIELD
00114 #define M_BREW_TIME_YIELD MODE_TIME_YIELD
00115 #define M_BREW_MANUAL MODE_MANUAL
00116 #define M_BREW_STEAM MODE_STEAM
00117 
00118 #define NUM_BREW_MENUS 6
00119 
00120 const char* MENU_TITLES[NUM_BREW_MENUS]= {
00121     "Brew Settings",
00122     "Brew Set Time",
00123     "Brew Set Yield",
00124     "Brew Set Time/Yield",
00125     "Manual Brew",
00126     "Steam"
00127 };
00128 
00129 const uint8_t MENU_ITEMS[NUM_BREW_MENUS][5]= {
00130     {1, 3, TEMPERATURE, PRE_INFUSE_TIME, PRESSURE},
00131     {1, 1, SHOT_TIME, EMPTY, EMPTY},
00132     {1, 1, YIELD, EMPTY, EMPTY},
00133     {1, 2, YIELD, SHOT_TIME, EMPTY},
00134     {1, 0, EMPTY, EMPTY, EMPTY}, // stub for manual brew (no sub menu)
00135     {1, 0, EMPTY, EMPTY, EMPTY}  // stub for steam (no sub menu)
00136 };
00137 
00138 const char* ITEM_STRINGS[7]= {
00139     "Pre-Infuse Time (s): ",
00140     "Pressure (bar): ",
00141     "Yield (ml): ",
00142     "Shot Time (s): ",
00143     "Temperature (C): ",
00144     "Flow rate (dl/s): ",
00145     ""
00146 };
00147 
00148 uint8_t brew_params[NUM_BREW_MENUS][3][3]= {
00149     { {92, 1, 9}, {0, 0, 0}, {0, 0, 0} },
00150     { {28, 0, 0}, {0, 0, 0}, {0, 0, 0} },
00151     { {55, 0, 0}, {0, 0, 0}, {0, 0, 0} },
00152     { {55, 28, 0}, {55, 28, 0}, {55, 28, 0} },
00153     { {0, 0, 0}, {0, 0, 0}, {0, 0, 0} }, // stub for manual brew (no sub menu)
00154     { {0, 0, 0}, {0, 0, 0}, {0, 0, 0} }  // stub for steam (no sub menu)
00155 };
00156 
00157 #define LEFT  static_cast<int>(1 << 0)
00158 #define UP    static_cast<int>(1 << 1)
00159 #define RIGHT static_cast<int>(1 << 2)
00160 #define DOWN  static_cast<int>(1 << 3)
00161 #define FIRE  static_cast<int>(1 << 4)
00162 
00163 uint8_t active_menu = 0;
00164 uint8_t menu_level = L_STANDBY;
00165 
00166 // 0 means not in edit mode - 1 to 9 are positions on a 3x3 square
00167 uint8_t edit_mode = 0;
00168 const uint8_t conversion_table[9][2] = {
00169     {0, 0}, {1, 0}, {2, 0},
00170     {0, 1}, {1, 1}, {2, 1},
00171     {0, 2}, {1, 2}, {2, 2}
00172 };
00173 
00174 #define MAIN_LOOP_PERIOD_MS 500
00175 
00176 // takes the params from given menu and give thems to brew control
00177 void set_params(uint8_t menu_id)
00178 {
00179     if (menu_id == M_BREW_MANUAL)
00180     {
00181     brew_control.set_shot_volume(0);
00182     brew_control.set_shot_time(0);
00183     brew_control.set_preinfuse_time(0);
00184     return;
00185     }
00186     uint8_t i;
00187     for(i = 0; i < MENU_ITEMS[menu_id][1]; i++) {
00188     switch (MENU_ITEMS[menu_id][i+2])
00189     {
00190         case PRE_INFUSE_TIME: 
00191                 brew_control.set_preinfuse_time(
00192                     brew_params[menu_id][0][i]); 
00193                 break;
00194         case YIELD:     brew_control.set_shot_volume(
00195                     brew_params[menu_id][0][i]);
00196                 break;
00197         case SHOT_TIME: brew_control.set_shot_time(
00198                     brew_params[menu_id][0][i]);
00199                 break;
00200         case TEMPERATURE:   brew_control.set_shot_temperature(
00201                     brew_params[menu_id][0][i]);
00202                 break;
00203         case FLOW_RATE: brew_control.set_shot_flow_rate(
00204                     float(brew_params[menu_id][0][i]/10));
00205                 break;
00206         case PRESSURE:  brew_control.set_shot_pressure(
00207                     brew_params[menu_id][0][i]);
00208                 break;
00209     }
00210     }
00211 }
00212 
00213 // Draw menu
00214 void draw_menu()
00215 {
00216     uint8_t i, j;
00217     uint8_t state;
00218     string out_s;
00219     // clear screen if required & set
00220     LCD.cls();
00221     LCD.invert(0);
00222 
00223     // Draw standby and brew specific info
00224     if (menu_level == L_STANDBY || menu_level == L_BREW) {
00225     state = brew_control.get_state();
00226 
00227         // Draw brew screen
00228     LCD.set_font((unsigned char*) Small_6);
00229     if (menu_level == L_BREW)
00230     {
00231         // Invert screen while brewing or steaming
00232         if (state != STOPPED)
00233         LCD.invert(1);
00234         if (active_menu == M_BREW_MANUAL)
00235         {
00236         LCD.locate(5,16);
00237         LCD.printf("MANUAL");
00238         LCD.locate(9,23);
00239         LCD.printf("BREW");
00240         }
00241         else if (active_menu == M_BREW_TIME)
00242         {
00243         LCD.locate(5,16);
00244         LCD.printf("TIME");
00245         }
00246         else if (active_menu == M_BREW_YIELD)
00247         {
00248         LCD.locate(4,16);
00249         LCD.printf("YIELD");
00250         }
00251         else if (active_menu == M_BREW_TIME_YIELD)
00252         {
00253         LCD.locate(2,16);
00254         LCD.printf("FLOWRATE");
00255         }
00256         else if (active_menu == M_BREW_STEAM)
00257         {
00258         LCD.locate(4,16);
00259         LCD.printf("STEAM");
00260         }
00261         if (state == BREWING)
00262         {
00263         LCD.locate(5,23);
00264         if (active_menu == M_BREW_STEAM)
00265             LCD.printf("HOT");
00266         else
00267             LCD.printf("BREW");
00268         }
00269         else if (state == PRE_INFUSING)
00270         {
00271         LCD.locate(2,23);
00272         LCD.printf("PRE-INFUSE");
00273         }
00274         else if (state == SOFT_STOPPING)
00275         {
00276         LCD.locate(2,23);
00277         LCD.printf("SOFT-STOP");
00278         }
00279     }
00280         // Draw standby screen
00281         else
00282         {
00283             if (power_save)
00284             {
00285                 LCD.locate(5,17);
00286                 LCD.printf("POWER");
00287                 LCD.locate(5,24);
00288                 LCD.printf("STANDBY");
00289             }
00290             else
00291             {
00292                 LCD.locate(5,19);
00293                 LCD.printf("STANDBY");
00294             }
00295         }
00296 
00297         // Draw common area (metrics)
00298         LCD.locate(4,4);
00299     LCD.set_font((unsigned char*) Arial12x12);
00300     if (state == SOFT_STOPPING)
00301         LCD.printf("Av, pressure: %.1f bar",
00302             brew_control.get_average_pressure());
00303     else
00304         LCD.printf("%.1fC %.1fbar %.1fml/s", 
00305             brew_control.get_current_temperature(),
00306             brew_control.get_current_pressure(),
00307             brew_control.get_current_flow_rate());
00308     LCD.locate(55,17);
00309     LCD.printf("%.0f ml %.0f s",
00310         brew_control.get_current_volume(),
00311         brew_control.get_current_time());
00312     }
00313 
00314     // Draw first level menu (brew modes)
00315     else if (menu_level == L_MENU) {
00316     LCD.locate(0,12);
00317     LCD.set_font((unsigned char*) Arial12x12);
00318     LCD.printf("%s  ", MENU_TITLES[active_menu]);
00319     }
00320 
00321     // Draw second level menu (brew parameters)
00322     else if (menu_level == L_MENU_SUB) {
00323     out_s.clear();
00324     // Display all menu items
00325     for(i = 2; i < sizeof(MENU_ITEMS[active_menu])/sizeof(*MENU_ITEMS[active_menu]); i++) {
00326         // Get the type of current menu item (T, P, Y or ST)
00327         int idx = MENU_ITEMS[active_menu][i];
00328         char default_n[8];
00329         // break out if we have an empty element
00330         if (idx == EMPTY)
00331         break;
00332         out_s += ITEM_STRINGS[idx];
00333         // For each menu item, get all the parameters (either 1 or 3)
00334         for (j = 0; j < MENU_ITEMS[active_menu][0]; j++) {
00335         // Add brackets if edit selection is over current parameter
00336         if (edit_mode == (i-2)*3+(j+1))
00337             sprintf(default_n, "[%d]", brew_params[active_menu][j][i-2]);
00338         else
00339             sprintf(default_n, "%d", brew_params[active_menu][j][i-2]);
00340         out_s += default_n;
00341         out_s += " ";
00342         }
00343         out_s += '\n';
00344     }
00345         // Set position and font size based on number of parameters
00346     if (MENU_ITEMS[active_menu][1] == 1)
00347     {
00348         LCD.set_font((unsigned char*) Arial12x12);
00349         LCD.locate(0,12);
00350     }
00351     else if (MENU_ITEMS[active_menu][1] == 2)
00352     {  
00353         LCD.set_font((unsigned char*) Arial12x12);
00354         LCD.locate(0,6);
00355     }
00356     else
00357     {
00358         LCD.set_font((unsigned char*) Small_7);
00359         LCD.locate(0,2);
00360     }
00361     LCD.printf("%s", out_s.c_str());
00362     }
00363 }
00364 
00365 void enter_power_save()
00366 {
00367     // let's disable PID temperature control
00368     brew_control.disable_boiler();
00369     // turn on LCD power saving
00370     //LCD.power_save(1);
00371 
00372     power_save = 1;
00373     menu_level = L_STANDBY;
00374 }
00375 
00376 void exit_power_save()
00377 {
00378     power_save = 0;
00379     power_save_timer.reset();
00380     // enabled PID temperature control
00381     brew_control.enable_boiler();
00382     // turn off LCD power saving
00383     //LCD.power_save(0);
00384 }
00385 
00386 // should get called everytime a power saving reset event happens 
00387 void check_power_save()
00388 {
00389     if(power_save)
00390         exit_power_save();
00391     else
00392         power_save_timer.reset();
00393 }
00394 
00395 // This thread waits for joystick commands, then sets parameters and then draws the menu
00396 void menu_handler()
00397 {
00398     while(true) {
00399     // wait on joystick action
00400     uint32_t flags = ThisThread::flags_wait_any_for(LEFT | UP | RIGHT | DOWN | FIRE, 500);
00401  
00402         heap_mutex.lock();
00403         // Flags should return osFlagsErrorTimeout but it's returning 0...
00404     if (!flags)
00405     {
00406         draw_menu();
00407             heap_mutex.unlock();
00408         continue;
00409     }
00410         else
00411         {
00412             check_power_save();
00413         }
00414 
00415     // fire
00416     if (flags == FIRE) {
00417         switch (menu_level)
00418         {
00419         case L_STANDBY: break;
00420         case L_MENU:    break;
00421         case L_MENU_SUB:    if(!edit_mode)
00422                     // enter edit mode
00423                     edit_mode = 1;
00424                 else
00425                     // exit edit mode
00426                     edit_mode = 0;
00427                 break;
00428         case L_BREW:    // send params to brew control b4 brewing
00429                 if(brew_control.get_state() == STOPPED)
00430                 {
00431                     // set general settings
00432                     set_params(M_BREW_SETTINGS);
00433                     // set brew mode settings
00434                     set_params(active_menu);
00435                 }
00436                 brew_control.toggle(active_menu);
00437                 break;
00438         }
00439     }
00440     // left
00441     else if (flags == LEFT) {
00442         switch (menu_level)
00443         {
00444         case L_STANDBY: break;  
00445         case L_MENU:    menu_level = L_STANDBY; break;
00446         case L_MENU_SUB:    if(!edit_mode)
00447                 {
00448                     menu_level = L_MENU;
00449                     // if we exiting settings, let's set them
00450                     if(active_menu == M_BREW_SETTINGS)
00451                     set_params(active_menu);
00452                 }
00453                 else
00454                     brew_params[active_menu]
00455                     [conversion_table[edit_mode-1][0]]
00456                     [conversion_table[edit_mode-1][1]]--;
00457                 break;
00458         case L_BREW:        // skip sub-menu for manual or steam mode
00459                 if(active_menu == M_BREW_MANUAL || active_menu == M_BREW_STEAM)
00460                     menu_level = L_MENU;
00461                 // only exit brew menu if not in brewing mode
00462                 else if(brew_control.get_state() == STOPPED)
00463                     menu_level = L_MENU_SUB;
00464                 break;
00465         }
00466     }
00467     // up
00468     else if (flags == UP) {
00469         switch (menu_level)
00470         {
00471         case L_STANDBY: menu_level = L_MENU; break;
00472         case L_MENU:    active_menu = (!active_menu)?active_menu:active_menu-1;
00473                 break;
00474         case L_MENU_SUB:    if(edit_mode > 1)
00475                     // sub 1 or 3 based on number of phases
00476                     edit_mode -= (MENU_ITEMS[active_menu][0] == 1)?3:1;
00477                 break;
00478         case L_BREW:    if(active_menu == M_BREW_MANUAL)
00479                     brew_control.pressure_up(5);
00480                 break;
00481         }
00482     }
00483     // right
00484     else if (flags == RIGHT) {
00485         switch (menu_level)
00486         {
00487         case L_STANDBY: menu_level = L_MENU; break; 
00488         case L_MENU:    if(active_menu == M_BREW_MANUAL 
00489                                         || active_menu == M_BREW_STEAM)
00490                     // skip sub-menu for manual mode
00491                     menu_level = L_BREW;
00492                 else
00493                     menu_level = L_MENU_SUB;
00494                 break;
00495         case L_MENU_SUB:    if(!edit_mode && active_menu != M_BREW_SETTINGS)
00496                 {
00497                     menu_level = L_BREW;
00498                 }
00499                 else
00500                     brew_params[active_menu]
00501                     [conversion_table[edit_mode-1][0]]
00502                     [conversion_table[edit_mode-1][1]]++;
00503                 break;
00504         case L_BREW:    if(brew_control.get_state() == PRE_INFUSING)
00505                     brew_control.stop_preinfuse_now();
00506                 break;
00507         }
00508     }
00509     // down
00510     else if (flags == DOWN) {
00511         switch (menu_level)
00512         {
00513         case L_STANDBY: menu_level = L_MENU; break;
00514         case L_MENU:    active_menu = (active_menu < NUM_BREW_MENUS-1)?active_menu+1:active_menu;
00515                 break;
00516         case L_MENU_SUB:    if(edit_mode < (MENU_ITEMS[active_menu][1]-1)*3
00517                     && edit_mode > 0)
00518                     // move fwd by 1 or 3 based on number of phases
00519                     edit_mode += (MENU_ITEMS[active_menu][0] == 1)?3:1;
00520                 break;
00521         case L_BREW:    if(active_menu == M_BREW_MANUAL)
00522                     brew_control.pressure_down(5);
00523                 break;
00524         } 
00525     }
00526 
00527     // draw next menu
00528     draw_menu();
00529         heap_mutex.unlock();
00530     }
00531 }
00532 
00533 // timer to ignore double clicks
00534 bool check_js_timer()
00535 {
00536     if(js_timer.read_ms() < 250)
00537     {
00538     js_timer.reset();
00539     return 0;
00540     }
00541     else
00542     {
00543     js_timer.reset();
00544     return 1;
00545     }
00546 }
00547 
00548 // Joystick ISRs just send signals to unblock menu thread 
00549 void cycle_up()
00550 {
00551     if(check_js_timer())
00552         menu_thread.flags_set(UP);
00553 }
00554 void cycle_down()
00555 {
00556     if(check_js_timer())
00557         menu_thread.flags_set(DOWN);
00558 }
00559 void cycle_left()
00560 {
00561     if(check_js_timer())
00562         menu_thread.flags_set(LEFT);
00563 }
00564 void cycle_right()
00565 {
00566     if(check_js_timer())
00567         menu_thread.flags_set(RIGHT);
00568 }
00569 void fire_away()
00570 {
00571     if(check_js_timer())
00572         menu_thread.flags_set(FIRE);
00573 }
00574 
00575 
00576 #if MBED_HEAP_STATS_ENABLED
00577 void print_heap_stats()
00578 {
00579     mbed_stats_heap_t heap_info;
00580     pc.printf("\nMemoryStats:");
00581     mbed_stats_heap_get( &heap_info );
00582     pc.printf("\n\tBytes allocated currently: %d", heap_info.current_size);
00583     pc.printf("\n\tMax bytes allocated at a given time: %d", heap_info.max_size);
00584     pc.printf("\n\tCumulative sum of bytes ever allocated: %d", heap_info.total_size);
00585     pc.printf("\n\tCurrent number of bytes allocated for the heap: %d", heap_info.reserved_size);
00586     pc.printf("\n\tCurrent number of allocations: %d", heap_info.alloc_cnt);
00587     pc.printf("\n\tNumber of failed allocations: %d", heap_info.alloc_fail_cnt);
00588 }
00589 #endif
00590 
00591 #if MBED_STACK_STATS_ENABLED
00592 void print_stack_stats()
00593 {
00594     mbed_stats_stack_t stack_info[ 10 ];
00595     mbed_stats_stack_get( &stack_info[0] );
00596     pc.printf("\nCumulative Stack Info:");
00597     pc.printf("\n\tMaximum number of bytes used on the stack: %d", stack_info[0].max_size);
00598     pc.printf("\n\tCurrent number of bytes allocated for the stack: %d", stack_info[0].reserved_size);
00599     pc.printf("\n\tNumber of stacks stats accumulated in the structure: %d", stack_info[0].stack_cnt);
00600     
00601     mbed_stats_stack_get_each( stack_info, 10 );
00602     pc.printf("\nThread Stack Info:");
00603     for(int i=0;i < 10; i++) {
00604         if(stack_info[i].thread_id != 0) {
00605             pc.printf("\n\tThread: %d", i);
00606             pc.printf("\n\t\tThread Id: 0x%08X", stack_info[i].thread_id);
00607             pc.printf("\n\t\tMaximum number of bytes used on the stack: %d", stack_info[i].max_size);
00608             pc.printf("\n\t\tCurrent number of bytes allocated for the stack: %d", stack_info[i].reserved_size);
00609             pc.printf("\n\t\tNumber of stacks stats accumulated in the structure: %d", stack_info[i].stack_cnt); 
00610         }        
00611     }
00612 }
00613 #endif
00614 
00615 // print the actual contrast
00616 int main()
00617 {
00618     pc.baud(115200);
00619 
00620     // Initialize default mode - 60ml shots
00621     int target_volume = 60;
00622     brew_control.set_shot_volume(target_volume);
00623     js_timer.start();
00624 
00625     power_save_timer.start();
00626     power_save = 0;
00627 
00628     // We want to call the menu_handler in another thread
00629     menu_thread.start(callback(menu_handler));
00630 
00631     // Attach joystick ISR callbacks
00632     Up.rise(&cycle_up);
00633     Down.rise(&cycle_down);
00634     Left.rise(&cycle_left);
00635     Right.rise(&cycle_right);
00636     Fire.rise(&fire_away);
00637 
00638     // This loop will use the main thread to get commands from ttyUSB
00639     // it will also display debug/status info on ttyUSB
00640     while(true) { 
00641 #if 1
00642     if (pc.readable()) {
00643         char c = pc.getc();
00644             heap_mutex.lock();
00645 
00646         // Menu navigation from ttyUSB
00647         if(c == 'i')
00648         cycle_up();
00649         else if(c == 'j')
00650         cycle_left();
00651         else if(c == 'k')
00652         cycle_down();
00653         else if(c == 'l')
00654         cycle_right();
00655         else if(c == 'm')
00656         fire_away();
00657 
00658         // b for Manual Brew Toggle
00659         else if(c == 'b')
00660         {
00661         brew_control.set_shot_volume(0);
00662         brew_control.set_shot_time(0);
00663         brew_control.toggle(MODE_MANUAL);
00664         pc.printf("brew to %d\n", brew_control.get_state());
00665         }
00666 
00667         // q and a to control target pressure
00668         else if(c == 'q')
00669         {
00670         brew_control.pressure_up();
00671         }
00672         else if(c == 'a')
00673         {
00674         brew_control.pressure_down();
00675         }
00676 
00677         // z for Automatic Volume Brew
00678         else if(c == 'z')
00679         {
00680         brew_control.set_shot_volume(target_volume);
00681         brew_control.start(MODE_YIELD);
00682         }
00683 
00684         // w and s to set target temperature
00685         else if (c == 'w')
00686         {
00687         brew_control.set_shot_temperature(brew_control.get_shot_temperature() + 0.5);
00688         pc.printf("Temp set to %.1f\n", brew_control.get_shot_temperature());
00689         }
00690         else if (c == 's')
00691         {
00692         brew_control.set_shot_temperature(brew_control.get_shot_temperature() - 0.5);
00693         pc.printf("Temp set to %.1f\n", brew_control.get_shot_temperature());
00694         }
00695 
00696         // e and d to set pre-infuse time
00697         else if (c == 'e')
00698         {
00699         brew_control.set_preinfuse_time(brew_control.get_preinfuse_time() + 1);
00700         pc.printf("Pre-Infuse set to %d seconds\n", brew_control.get_preinfuse_time());
00701         }
00702         else if (c == 'd')
00703         {
00704         brew_control.set_preinfuse_time(brew_control.get_preinfuse_time() - 1);
00705         pc.printf("Pre-Infuse set to %d seconds\n", brew_control.get_preinfuse_time());
00706         }
00707 
00708         // r and f to set target shot volume
00709         else if (c == 'r')
00710         {
00711         target_volume = brew_control.get_shot_volume() + 5;
00712         brew_control.set_shot_volume(target_volume);
00713         pc.printf("Target Volume set to %d\n", target_volume);
00714         }
00715         else if (c == 'f')
00716         {
00717         target_volume = brew_control.get_shot_volume() - 5;
00718         brew_control.set_shot_volume(target_volume);
00719         pc.printf("Target Volume set to %d\n", target_volume);
00720         }
00721 
00722         // p to toggle PID control (if off there will be no heating at all)
00723         else if (c == 'p')
00724         {
00725         if (brew_control.toggle_boiler())
00726             pc.printf("Enabling PID\n");
00727         else
00728             pc.printf("Disabling PID\n");
00729         }
00730         else if (c == '=')
00731         brew_control.toggle_solenoid();
00732 
00733             heap_mutex.unlock();
00734     }
00735 /*
00736     pc.printf("%d %.3f ml %.2f bar %.1f C %.1f C %.1f s %.2f ml/s\n", 
00737         brew_control.get_pump_level(),
00738         brew_control.get_current_volume(),
00739         brew_control.get_current_pressure(),
00740         brew_control.get_current_temperature_side(),
00741         brew_control.get_current_temperature_top(),
00742         brew_control.get_current_time(),
00743         brew_control.get_current_flow_rate());
00744                 */
00745 
00746         pc.printf("%.1f, %.1f, %.1f, %d, %.2f, %.2f, %.2f, %d\n",
00747         brew_control.get_current_time(),
00748         brew_control.get_current_temperature_side(),
00749         brew_control.get_current_temperature_top(),
00750         brew_control.get_pump_level(),
00751         brew_control.get_current_pressure(),
00752         brew_control.get_current_volume(),
00753         brew_control.get_current_flow_rate(),
00754                 brew_control.get_state());
00755 
00756 #if MBED_HEAP_STATS_ENABLED
00757         print_heap_stats();
00758 #endif
00759 
00760 #if MBED_STACK_STATS_ENABLED
00761         print_stack_stats();
00762 #endif
00763 
00764 #endif
00765 
00766         heap_mutex.lock();
00767         // If there has been no keypress for POWER_SAVE_TIMEOUT_S, go to powersaving mode
00768         if (power_save_timer.read() > POWER_SAVE_TIMEOUT_S)
00769             enter_power_save();
00770         heap_mutex.unlock();
00771 
00772         ThisThread::sleep_for(MAIN_LOOP_PERIOD_MS);   // wait 0.1s
00773     }
00774 
00775 }