static void getweather_callback(byte status, uint16_t off, uint16_t len) { #if defined(ARDUINO) char *p = (char*)Ethernet::buffer + off; #else char *p = ether_buffer; #endif /* scan the buffer until the first & symbol */ while(*p && *p!='&') { p++; } if (*p != '&') return; int v; if (findKeyVal(p, tmp_buffer, TMP_BUFFER_SIZE, PSTR("sunrise"), true)) { v = atoi(tmp_buffer); if (v>=0 && v<=1440) { os.nvdata.sunrise_time = v; } } if (findKeyVal(p, tmp_buffer, TMP_BUFFER_SIZE, PSTR("sunset"), true)) { v = atoi(tmp_buffer); if (v>=0 && v<=1440) { os.nvdata.sunset_time = v; } } os.nvdata_save(); // save non-volatile memory if (findKeyVal(p, tmp_buffer, TMP_BUFFER_SIZE, PSTR("scale"), true)) { v = atoi(tmp_buffer); if (v>=0 && v<=250 && v != os.options[OPTION_WATER_PERCENTAGE].value) { // only save if the value has changed os.options[OPTION_WATER_PERCENTAGE].value = v; os.options_save(); } } if (findKeyVal(p, tmp_buffer, TMP_BUFFER_SIZE, PSTR("tz"), true)) { v = atoi(tmp_buffer); if (v>=0 && v<= 96) { if (v != os.options[OPTION_TIMEZONE].value) { // if timezone changed, save change and force ntp sync os.options[OPTION_TIMEZONE].value = v; os.options_save(); } } } if (findKeyVal(p, tmp_buffer, TMP_BUFFER_SIZE, PSTR("eip"), true)) { os.external_ip = atol(tmp_buffer); } os.checkwt_success_lasttime = os.now_tz(); }
void do_setup() { initialiseEpoch(); // initialize time reference for millis() and micros() os.begin(); // OpenSprinkler init os.options_setup(); // Setup options pd.init(); // ProgramData init if (os.start_network()) { // initialize network DEBUG_PRINTLN("network established."); os.status.network_fails = 0; } else { DEBUG_PRINTLN("network failed."); os.status.network_fails = 1; } os.status.req_network = 0; }
static void getweather_callback(byte status, uint16_t off, uint16_t len) { #if defined(ARDUINO) char *p = (char*)Ethernet::buffer + off; #else char *p = ether_buffer; #endif /* scan the buffer until the first & symbol */ while(*p && *p!='&') { p++; } if (*p != '&') return; int v; if (findKeyVal(p, tmp_buffer, TMP_BUFFER_SIZE, PSTR("sunrise"), true)) { v = atoi(tmp_buffer); if (v>=0 && v<=1440 && v != os.nvdata.sunrise_time) { os.nvdata.sunrise_time = v; os.nvdata_save(); os.weather_update_flag |= WEATHER_UPDATE_SUNRISE; } } if (findKeyVal(p, tmp_buffer, TMP_BUFFER_SIZE, PSTR("sunset"), true)) { v = atoi(tmp_buffer); if (v>=0 && v<=1440 && v != os.nvdata.sunset_time) { os.nvdata.sunset_time = v; os.nvdata_save(); os.weather_update_flag |= WEATHER_UPDATE_SUNSET; } } if (findKeyVal(p, tmp_buffer, TMP_BUFFER_SIZE, PSTR("et0"), true)) { v = atoi(tmp_buffer); v += os.nvdata.ethist[0]; if (v != os.nvdata.water_balance[0]) { os.nvdata.water_balance[0] = v; os.nvdata_save(); os.weather_update_flag |= WEATHER_UPDATE_ET0 }
// ====================== // 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 GetWeather() { EthernetClient client; static struct hostent *server = NULL; if (!server) { strcpy(tmp_buffer, WEATHER_SCRIPT_HOST); server = gethostbyname(tmp_buffer); if (!server) { DEBUG_PRINTLN("can't resolve weather server"); return; } DEBUG_PRINT("weather server ip:"); DEBUG_PRINT(((uint8_t*)server->h_addr)[0]); DEBUG_PRINT(":"); DEBUG_PRINT(((uint8_t*)server->h_addr)[1]); DEBUG_PRINT(":"); DEBUG_PRINT(((uint8_t*)server->h_addr)[2]); DEBUG_PRINT(":"); DEBUG_PRINTLN(((uint8_t*)server->h_addr)[3]); } if (!client.connect((uint8_t*)server->h_addr, 80)) { DEBUG_PRINTLN("failed to connect to weather server"); client.stop(); return; } BufferFiller bf = tmp_buffer; char tmp[100]; read_from_file(wtopts_name, tmp, 100); bf.emit_p(PSTR("$D.py?loc=$E&key=$E&fwv=$D&wto=$S"), (int) os.options[OPTION_USE_WEATHER].value, ADDR_NVM_LOCATION, ADDR_NVM_WEATHER_KEY, (int)os.options[OPTION_FW_VERSION].value, tmp); char *src=tmp_buffer+strlen(tmp_buffer); char *dst=tmp_buffer+TMP_BUFFER_SIZE-1; char c; // url encode. convert SPACE to %20 // copy reversely from the end because we are potentially expanding // the string size while(src!=tmp_buffer) { c = *src--; if(c==' ') { *dst-- = '0'; *dst-- = '2'; *dst-- = '%'; } else { *dst-- = c; } }; *dst = *src; char urlBuffer[255]; strcpy(urlBuffer, "GET /weather"); strcat(urlBuffer, dst); strcat(urlBuffer, " HTTP/1.0\r\nHOST: weather.opensprinkler.com\r\n\r\n"); client.write((uint8_t *)urlBuffer, strlen(urlBuffer)); bzero(ether_buffer, ETHER_BUFFER_SIZE); time_t timeout = os.now_tz() + 5; // 5 seconds timeout while(os.now_tz() < timeout) { int len=client.read((uint8_t *)ether_buffer, ETHER_BUFFER_SIZE); if (len<=0) { if(!client.connected()) break; else continue; } peel_http_header(); getweather_callback(0, 0, ETHER_BUFFER_SIZE); } client.stop(); }
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