// ====================== // Setup Function // ====================== void do_setup() { /* Clear WDT reset flag. */ MCUSR &= ~(1<<WDRF); DEBUG_BEGIN(9600); DEBUG_PRINTLN("started."); os.begin(); // OpenSprinkler init os.options_setup(); // Setup options pd.init(); // ProgramData init setSyncInterval(RTC_SYNC_INTERVAL); // RTC sync interval // if rtc exists, sets it as time sync source setSyncProvider(RTC.get); os.lcd_print_time(os.now_tz()); // display time to LCD // enable WDT /* In order to change WDE or the prescaler, we need to * set WDCE (This will allow updates for 4 clock cycles). */ WDTCSR |= (1<<WDCE) | (1<<WDE); /* set new watchdog timeout prescaler value */ WDTCSR = 1<<WDP3 | 1<<WDP0; // 8.0 seconds /* Enable the WD interrupt (note no reset). */ WDTCSR |= _BV(WDIE); if (os.start_network()) { // initialize network os.status.network_fails = 0; } else { os.status.network_fails = 1; } os.status.req_network = 0; os.status.req_ntpsync = 1; os.apply_all_station_bits(); // reset station bits os.button_timeout = LCD_BACKLIGHT_TIMEOUT; }
void ui_state_machine() { if (!os.button_timeout) { os.lcd_set_brightness(0); ui_state = UI_STATE_DEFAULT; // also recover to default state } // read button, if something is pressed, wait till release byte button = os.button_read(BUTTON_WAIT_HOLD); if (button & BUTTON_FLAG_DOWN) { // repond only to button down events os.button_timeout = LCD_BACKLIGHT_TIMEOUT; os.lcd_set_brightness(1); } else { return; } switch(ui_state) { case UI_STATE_DEFAULT: switch (button & BUTTON_MASK) { case BUTTON_1: if (button & BUTTON_FLAG_HOLD) { // holding B1: stop all stations if (digitalRead(PIN_BUTTON_3)==0) { // if B3 is pressed while holding B1, run a short test (internal test) manual_start_program(255); } else if (digitalRead(PIN_BUTTON_2)==0) { // if B2 is pressed while holding B1, display gateway IP os.lcd_print_ip(ether.gwip, 0); os.lcd.setCursor(0, 1); os.lcd_print_pgm(PSTR("(gwip)")); ui_state = UI_STATE_DISP_IP; } else { reset_all_stations(); } } else { // clicking B1: display device IP and port os.lcd_print_ip(ether.myip, 0); os.lcd.setCursor(0, 1); os.lcd_print_pgm(PSTR(":")); os.lcd.print(ether.hisport); os.lcd_print_pgm(PSTR(" (osip)")); ui_state = UI_STATE_DISP_IP; } break; case BUTTON_2: if (button & BUTTON_FLAG_HOLD) { // holding B2: reboot if (digitalRead(PIN_BUTTON_1)==0) { // if B1 is pressed while holding B2, display external IP os.lcd_print_ip((byte*)(&os.nvdata.external_ip), 1); os.lcd.setCursor(0, 1); os.lcd_print_pgm(PSTR("(eip)")); ui_state = UI_STATE_DISP_IP; } else if (digitalRead(PIN_BUTTON_3)==0) { // if B3 is pressed while holding B2, display last successful weather call os.lcd.clear(); os.lcd_print_time(os.checkwt_success_lasttime); os.lcd.setCursor(0, 1); os.lcd_print_pgm(PSTR("(lswc)")); ui_state = UI_STATE_DISP_IP; } else { os.reboot_dev(); } } else { // clicking B2: display MAC and gate way IP os.lcd.clear(); os.lcd_print_mac(ether.mymac); ui_state = UI_STATE_DISP_GW; } break; case BUTTON_3: if (button & BUTTON_FLAG_HOLD) { // holding B3: go to main menu os.lcd_print_line_clear_pgm(PSTR("Run a Program:"), 0); os.lcd_print_line_clear_pgm(PSTR("Click B3 to list"), 1); ui_state = UI_STATE_RUNPROG; } else { // clicking B3: switch board display (cycle through master and all extension boards) os.status.display_board = (os.status.display_board + 1) % (os.nboards); } break; } break; case UI_STATE_DISP_IP: case UI_STATE_DISP_GW: ui_state = UI_STATE_DEFAULT; break; case UI_STATE_RUNPROG: if ((button & BUTTON_MASK)==BUTTON_3) { if (button & BUTTON_FLAG_HOLD) { // start manual_start_program(ui_state_runprog); ui_state = UI_STATE_DEFAULT; } else { ui_state_runprog = (ui_state_runprog+1) % (pd.nprograms+1); os.lcd_print_line_clear_pgm(PSTR("Hold B3 to start"), 0); if(ui_state_runprog > 0) { ProgramStruct prog; pd.read(ui_state_runprog-1, &prog); os.lcd_print_line_clear_pgm(PSTR(" "), 1); os.lcd.setCursor(0, 1); os.lcd.print((int)ui_state_runprog); os.lcd_print_pgm(PSTR(". ")); os.lcd.print(prog.name); } else { os.lcd_print_line_clear_pgm(PSTR("0. Test (1 min)"), 1); } } } break; } }
/** Main Loop */ void do_loop() { static ulong last_time = 0; static ulong last_minute = 0; byte bid, sid, s, pid, qid, bitvalue; ProgramStruct prog; os.status.mas = os.options[OPTION_MASTER_STATION]; os.status.mas2= os.options[OPTION_MASTER_STATION_2]; time_t curr_time = os.now_tz(); // ====== Process Ethernet packets ====== #if defined(ARDUINO) // Process Ethernet packets for Arduino uint16_t pos=ether.packetLoop(ether.packetReceive()); if (pos>0) { // packet received handle_web_request((char*)Ethernet::buffer+pos); } wdt_reset(); // reset watchdog timer wdt_timeout = 0; ui_state_machine(); #else // Process Ethernet packets for RPI/BBB EthernetClient client = m_server->available(); if (client) { while(true) { int len = client.read((uint8_t*) ether_buffer, ETHER_BUFFER_SIZE); if (len <=0) { if(!client.connected()) { break; } else { continue; } } else { m_client = &client; ether_buffer[len] = 0; // put a zero at the end of the packet handle_web_request(ether_buffer); m_client = 0; break; } } } #endif // Process Ethernet packets // if 1 second has passed if (last_time != curr_time) { last_time = curr_time; if (os.button_timeout) os.button_timeout--; #if defined(ARDUINO) if (!ui_state) os.lcd_print_time(os.now_tz()); // print time #endif // ====== Check raindelay status ====== if (os.status.rain_delayed) { if (curr_time >= os.nvdata.rd_stop_time) { // rain delay is over os.raindelay_stop(); } } else { if (os.nvdata.rd_stop_time > curr_time) { // rain delay starts now os.raindelay_start(); } } // ====== Check controller status changes and write log ====== if (os.old_status.rain_delayed != os.status.rain_delayed) { if (os.status.rain_delayed) { // rain delay started, record time os.raindelay_start_time = curr_time; } else { // rain delay stopped, write log write_log(LOGDATA_RAINDELAY, curr_time); } os.old_status.rain_delayed = os.status.rain_delayed; } // ====== Check rain sensor status ====== if (os.options[OPTION_SENSOR_TYPE] == SENSOR_TYPE_RAIN) { // if a rain sensor is connected os.rainsensor_status(); if (os.old_status.rain_sensed != os.status.rain_sensed) { if (os.status.rain_sensed) { // rain sensor on, record time os.sensor_lasttime = curr_time; } else { // rain sensor off, write log if (curr_time>os.sensor_lasttime+10) { // add a 10 second threshold // to avoid faulty rain sensors generating // too many log records write_log(LOGDATA_RAINSENSE, curr_time); } } os.old_status.rain_sensed = os.status.rain_sensed; } } // ====== Schedule program data ====== ulong curr_minute = curr_time / 60; boolean match_found = false; RuntimeQueueStruct *q; // since the granularity of start time is minute // we only need to check once every minute if (curr_minute != last_minute) { last_minute = curr_minute; // check through all programs for(pid=0; pid<pd.nprograms; pid++) { pd.read(pid, &prog); if(prog.check_match(curr_time)) { // program match found // process all selected stations for(sid=0;sid<os.nstations;sid++) { bid=sid>>3; s=sid&0x07; // skip if the station is a master station (because master cannot be scheduled independently if ((os.status.mas==sid+1) || (os.status.mas2==sid+1)) continue; // if station has non-zero water time and the station is not disabled if (prog.durations[sid] && !(os.station_attrib_bits_read(ADDR_NVM_STNDISABLE+bid)&(1<<s))) { // water time is scaled by watering percentage ulong water_time = water_time_resolve(water_time_decode(prog.durations[sid])); // if the program is set to use weather scaling if (prog.use_weather) { byte wl = os.options[OPTION_WATER_PERCENTAGE]; water_time = water_time * wl / 100; if (wl < 20 && water_time < 10) // if water_percentage is less than 20% and water_time is less than 10 seconds // do not water water_time = 0; } if (water_time) { // check if water time is still valid // because it may end up being zero after scaling q = pd.enqueue(); if (q) { q->st = 0; q->dur = water_time; q->sid = sid; q->pid = pid+1; match_found = true; } else { // queue is full } }// if water_time }// if prog.durations[sid] }// for sid }// if check_match }// for pid // calculate start and end time if (match_found) { schedule_all_stations(curr_time); // For debugging: print out queued elements DEBUG_PRINT("en:"); for(q=pd.queue;q<pd.queue+pd.nqueue;q++) { DEBUG_PRINT("["); DEBUG_PRINT(q->sid); DEBUG_PRINT(","); DEBUG_PRINT(q->dur); DEBUG_PRINT(","); DEBUG_PRINT(q->st); DEBUG_PRINT("]"); } DEBUG_PRINTLN(""); } }//if_check_current_minute